A React Native app for the ultimate thinking partner.
1// Letta Brand Animation System
2
3// Duration constants (in milliseconds)
4export const duration = {
5 instant: 0,
6 fast: 200,
7 normal: 300,
8 slow: 400,
9 slower: 600,
10} as const;
11
12// Easing functions
13export const easing = {
14 // Standard easing
15 linear: 'linear',
16 easeIn: 'ease-in',
17 easeOut: 'ease-out',
18 easeInOut: 'ease-in-out',
19
20 // Custom cubic-bezier curves
21 bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
22 smooth: 'cubic-bezier(0.4, 0, 0.2, 1)',
23 sharp: 'cubic-bezier(0.4, 0, 0.6, 1)',
24} as const;
25
26// Pre-defined animation configurations
27export const animations = {
28 // Micro-interactions
29 buttonHover: {
30 duration: duration.fast,
31 easing: easing.smooth,
32 transform: 'scale(1.02)',
33 },
34 buttonPress: {
35 duration: duration.fast,
36 easing: easing.sharp,
37 transform: 'scale(0.98)',
38 },
39
40 // Message animations
41 messageAppear: {
42 duration: duration.normal,
43 easing: easing.smooth,
44 from: { opacity: 0, transform: 'translateY(10px)' },
45 to: { opacity: 1, transform: 'translateY(0px)' },
46 },
47 reasoningReveal: {
48 duration: duration.slow,
49 easing: easing.smooth,
50 from: { opacity: 0, maxHeight: 0 },
51 to: { opacity: 1, maxHeight: '200px' },
52 },
53
54 // Loading states
55 pulse: {
56 duration: 1500,
57 easing: easing.easeInOut,
58 iterationCount: 'infinite',
59 direction: 'alternate',
60 from: { opacity: 0.4 },
61 to: { opacity: 1 },
62 },
63 spin: {
64 duration: 1000,
65 easing: easing.linear,
66 iterationCount: 'infinite',
67 from: { transform: 'rotate(0deg)' },
68 to: { transform: 'rotate(360deg)' },
69 },
70
71 // Layout transitions
72 slideIn: {
73 duration: duration.normal,
74 easing: easing.smooth,
75 from: { transform: 'translateX(-100%)' },
76 to: { transform: 'translateX(0%)' },
77 },
78 slideOut: {
79 duration: duration.normal,
80 easing: easing.smooth,
81 from: { transform: 'translateX(0%)' },
82 to: { transform: 'translateX(-100%)' },
83 },
84 fadeIn: {
85 duration: duration.normal,
86 easing: easing.smooth,
87 from: { opacity: 0 },
88 to: { opacity: 1 },
89 },
90 fadeOut: {
91 duration: duration.fast,
92 easing: easing.smooth,
93 from: { opacity: 1 },
94 to: { opacity: 0 },
95 },
96
97 // Focus states
98 focusGlow: {
99 duration: duration.fast,
100 easing: easing.smooth,
101 boxShadow: '0 0 20px rgba(0, 102, 255, 0.4)',
102 },
103
104 // Geometric loading (Letta-specific)
105 geometricAssembly: {
106 duration: duration.slower,
107 easing: easing.bounce,
108 keyframes: [
109 { transform: 'scale(0) rotate(0deg)', opacity: 0 },
110 { transform: 'scale(0.5) rotate(180deg)', opacity: 0.5 },
111 { transform: 'scale(1) rotate(360deg)', opacity: 1 },
112 ],
113 },
114} as const;
115
116// Animation utility functions
117export const createKeyframes = (keyframes: Record<string, any>) => {
118 return Object.entries(keyframes)
119 .map(([key, value]) => `${key} { ${Object.entries(value).map(([prop, val]) => `${prop}: ${val};`).join(' ')} }`)
120 .join(' ');
121};
122
123export const createTransition = (
124 property: string | string[],
125 duration: number = 300,
126 easing: string = 'ease-in-out'
127) => {
128 const properties = Array.isArray(property) ? property : [property];
129 return properties.map(prop => `${prop} ${duration}ms ${easing}`).join(', ');
130};
131
132export type Duration = typeof duration;
133export type Easing = typeof easing;
134export type Animations = typeof animations;