@uselingu/react-native-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +279 -0
  2. package/lib/commonjs/components/LinguChat.js +283 -0
  3. package/lib/commonjs/components/LinguChat.js.map +1 -0
  4. package/lib/commonjs/components/LinguWebView.js +203 -0
  5. package/lib/commonjs/components/LinguWebView.js.map +1 -0
  6. package/lib/commonjs/components/MessageBubble.js +91 -0
  7. package/lib/commonjs/components/MessageBubble.js.map +1 -0
  8. package/lib/commonjs/components/NativeChatUI.js +192 -0
  9. package/lib/commonjs/components/NativeChatUI.js.map +1 -0
  10. package/lib/commonjs/components/TypingIndicator.js +92 -0
  11. package/lib/commonjs/components/TypingIndicator.js.map +1 -0
  12. package/lib/commonjs/context/LinguProvider.js +47 -0
  13. package/lib/commonjs/context/LinguProvider.js.map +1 -0
  14. package/lib/commonjs/context/index.js +17 -0
  15. package/lib/commonjs/context/index.js.map +1 -0
  16. package/lib/commonjs/hooks/useLinguChat.js +295 -0
  17. package/lib/commonjs/hooks/useLinguChat.js.map +1 -0
  18. package/lib/commonjs/index.js +129 -0
  19. package/lib/commonjs/index.js.map +1 -0
  20. package/lib/commonjs/services/LinguAPI.js +196 -0
  21. package/lib/commonjs/services/LinguAPI.js.map +1 -0
  22. package/lib/commonjs/services/SessionManager.js +131 -0
  23. package/lib/commonjs/services/SessionManager.js.map +1 -0
  24. package/lib/commonjs/services/StorageManager.js +116 -0
  25. package/lib/commonjs/services/StorageManager.js.map +1 -0
  26. package/lib/commonjs/theme/ThemeProvider.js +49 -0
  27. package/lib/commonjs/theme/ThemeProvider.js.map +1 -0
  28. package/lib/commonjs/theme/index.js +28 -0
  29. package/lib/commonjs/theme/index.js.map +1 -0
  30. package/lib/commonjs/theme/theme.js +150 -0
  31. package/lib/commonjs/theme/theme.js.map +1 -0
  32. package/lib/commonjs/types/index.js +2 -0
  33. package/lib/commonjs/types/index.js.map +1 -0
  34. package/lib/commonjs/utils/animations.js +118 -0
  35. package/lib/commonjs/utils/animations.js.map +1 -0
  36. package/lib/commonjs/utils/formatters.js +89 -0
  37. package/lib/commonjs/utils/formatters.js.map +1 -0
  38. package/lib/commonjs/utils/index.js +28 -0
  39. package/lib/commonjs/utils/index.js.map +1 -0
  40. package/lib/module/components/LinguChat.js +277 -0
  41. package/lib/module/components/LinguChat.js.map +1 -0
  42. package/lib/module/components/LinguWebView.js +195 -0
  43. package/lib/module/components/LinguWebView.js.map +1 -0
  44. package/lib/module/components/MessageBubble.js +83 -0
  45. package/lib/module/components/MessageBubble.js.map +1 -0
  46. package/lib/module/components/NativeChatUI.js +184 -0
  47. package/lib/module/components/NativeChatUI.js.map +1 -0
  48. package/lib/module/components/TypingIndicator.js +84 -0
  49. package/lib/module/components/TypingIndicator.js.map +1 -0
  50. package/lib/module/context/LinguProvider.js +38 -0
  51. package/lib/module/context/LinguProvider.js.map +1 -0
  52. package/lib/module/context/index.js +6 -0
  53. package/lib/module/context/index.js.map +1 -0
  54. package/lib/module/hooks/useLinguChat.js +288 -0
  55. package/lib/module/hooks/useLinguChat.js.map +1 -0
  56. package/lib/module/index.js +30 -0
  57. package/lib/module/index.js.map +1 -0
  58. package/lib/module/services/LinguAPI.js +190 -0
  59. package/lib/module/services/LinguAPI.js.map +1 -0
  60. package/lib/module/services/SessionManager.js +124 -0
  61. package/lib/module/services/SessionManager.js.map +1 -0
  62. package/lib/module/services/StorageManager.js +108 -0
  63. package/lib/module/services/StorageManager.js.map +1 -0
  64. package/lib/module/theme/ThemeProvider.js +40 -0
  65. package/lib/module/theme/ThemeProvider.js.map +1 -0
  66. package/lib/module/theme/index.js +7 -0
  67. package/lib/module/theme/index.js.map +1 -0
  68. package/lib/module/theme/theme.js +142 -0
  69. package/lib/module/theme/theme.js.map +1 -0
  70. package/lib/module/types/index.js +2 -0
  71. package/lib/module/types/index.js.map +1 -0
  72. package/lib/module/utils/animations.js +106 -0
  73. package/lib/module/utils/animations.js.map +1 -0
  74. package/lib/module/utils/formatters.js +78 -0
  75. package/lib/module/utils/formatters.js.map +1 -0
  76. package/lib/module/utils/index.js +7 -0
  77. package/lib/module/utils/index.js.map +1 -0
  78. package/lib/typescript/components/LinguChat.d.ts +18 -0
  79. package/lib/typescript/components/LinguChat.d.ts.map +1 -0
  80. package/lib/typescript/components/LinguWebView.d.ts +16 -0
  81. package/lib/typescript/components/LinguWebView.d.ts.map +1 -0
  82. package/lib/typescript/components/MessageBubble.d.ts +13 -0
  83. package/lib/typescript/components/MessageBubble.d.ts.map +1 -0
  84. package/lib/typescript/components/NativeChatUI.d.ts +16 -0
  85. package/lib/typescript/components/NativeChatUI.d.ts.map +1 -0
  86. package/lib/typescript/components/TypingIndicator.d.ts +7 -0
  87. package/lib/typescript/components/TypingIndicator.d.ts.map +1 -0
  88. package/lib/typescript/context/LinguProvider.d.ts +32 -0
  89. package/lib/typescript/context/LinguProvider.d.ts.map +1 -0
  90. package/lib/typescript/context/index.d.ts +5 -0
  91. package/lib/typescript/context/index.d.ts.map +1 -0
  92. package/lib/typescript/hooks/useLinguChat.d.ts +18 -0
  93. package/lib/typescript/hooks/useLinguChat.d.ts.map +1 -0
  94. package/lib/typescript/index.d.ts +19 -0
  95. package/lib/typescript/index.d.ts.map +1 -0
  96. package/lib/typescript/services/LinguAPI.d.ts +39 -0
  97. package/lib/typescript/services/LinguAPI.d.ts.map +1 -0
  98. package/lib/typescript/services/SessionManager.d.ts +46 -0
  99. package/lib/typescript/services/SessionManager.d.ts.map +1 -0
  100. package/lib/typescript/services/StorageManager.d.ts +36 -0
  101. package/lib/typescript/services/StorageManager.d.ts.map +1 -0
  102. package/lib/typescript/theme/ThemeProvider.d.ts +23 -0
  103. package/lib/typescript/theme/ThemeProvider.d.ts.map +1 -0
  104. package/lib/typescript/theme/index.d.ts +6 -0
  105. package/lib/typescript/theme/index.d.ts.map +1 -0
  106. package/lib/typescript/theme/theme.d.ts +130 -0
  107. package/lib/typescript/theme/theme.d.ts.map +1 -0
  108. package/lib/typescript/types/index.d.ts +50 -0
  109. package/lib/typescript/types/index.d.ts.map +1 -0
  110. package/lib/typescript/utils/animations.d.ts +34 -0
  111. package/lib/typescript/utils/animations.d.ts.map +1 -0
  112. package/lib/typescript/utils/formatters.d.ts +25 -0
  113. package/lib/typescript/utils/formatters.d.ts.map +1 -0
  114. package/lib/typescript/utils/index.d.ts +6 -0
  115. package/lib/typescript/utils/index.d.ts.map +1 -0
  116. package/package.json +98 -0
  117. package/src/components/LinguChat.tsx +309 -0
  118. package/src/components/LinguWebView.tsx +216 -0
  119. package/src/components/MessageBubble.tsx +120 -0
  120. package/src/components/NativeChatUI.tsx +247 -0
  121. package/src/components/TypingIndicator.tsx +84 -0
  122. package/src/context/LinguProvider.tsx +53 -0
  123. package/src/context/index.ts +5 -0
  124. package/src/hooks/useLinguChat.ts +314 -0
  125. package/src/index.ts +43 -0
  126. package/src/services/LinguAPI.ts +214 -0
  127. package/src/services/SessionManager.ts +131 -0
  128. package/src/services/StorageManager.ts +111 -0
  129. package/src/theme/ThemeProvider.tsx +58 -0
  130. package/src/theme/index.ts +6 -0
  131. package/src/theme/theme.ts +254 -0
  132. package/src/types/index.ts +59 -0
  133. package/src/utils/animations.ts +141 -0
  134. package/src/utils/formatters.ts +80 -0
  135. package/src/utils/index.ts +6 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Theme Configuration
3
+ * Centralized design tokens for the SDK
4
+ * Colors are sourced from backend widget config
5
+ */
6
+
7
+ export interface ThemeColors {
8
+ primary: string;
9
+ background: string;
10
+ text: string;
11
+ // Semantic colors
12
+ userBubble: string;
13
+ botBubble: string;
14
+ userText: string;
15
+ botText: string;
16
+ inputBackground: string;
17
+ inputBorder: string;
18
+ inputText: string;
19
+ headerBackground: string;
20
+ headerText: string;
21
+ errorBackground: string;
22
+ errorText: string;
23
+ badge: string;
24
+ loadingIndicator: string;
25
+ // Neutral colors
26
+ gray50: string;
27
+ gray100: string;
28
+ gray200: string;
29
+ gray300: string;
30
+ gray400: string;
31
+ gray500: string;
32
+ gray600: string;
33
+ gray700: string;
34
+ gray800: string;
35
+ gray900: string;
36
+ }
37
+
38
+ export interface Typography {
39
+ fontSizes: {
40
+ xs: number;
41
+ sm: number;
42
+ base: number;
43
+ lg: number;
44
+ xl: number;
45
+ '2xl': number;
46
+ '3xl': number;
47
+ };
48
+ fontWeights: {
49
+ normal: '400';
50
+ medium: '500';
51
+ semibold: '600';
52
+ bold: '700';
53
+ };
54
+ lineHeights: {
55
+ tight: number;
56
+ normal: number;
57
+ relaxed: number;
58
+ };
59
+ }
60
+
61
+ export interface Spacing {
62
+ xs: number;
63
+ sm: number;
64
+ md: number;
65
+ lg: number;
66
+ xl: number;
67
+ '2xl': number;
68
+ '3xl': number;
69
+ }
70
+
71
+ export interface BorderRadius {
72
+ sm: number;
73
+ md: number;
74
+ lg: number;
75
+ xl: number;
76
+ full: number;
77
+ }
78
+
79
+ export interface Shadows {
80
+ sm: {
81
+ shadowColor: string;
82
+ shadowOffset: { width: number; height: number };
83
+ shadowOpacity: number;
84
+ shadowRadius: number;
85
+ elevation: number;
86
+ };
87
+ md: {
88
+ shadowColor: string;
89
+ shadowOffset: { width: number; height: number };
90
+ shadowOpacity: number;
91
+ shadowRadius: number;
92
+ elevation: number;
93
+ };
94
+ lg: {
95
+ shadowColor: string;
96
+ shadowOffset: { width: number; height: number };
97
+ shadowOpacity: number;
98
+ shadowRadius: number;
99
+ elevation: number;
100
+ };
101
+ }
102
+
103
+ export interface AnimationDurations {
104
+ fast: number;
105
+ normal: number;
106
+ slow: number;
107
+ }
108
+
109
+ export interface Theme {
110
+ colors: ThemeColors;
111
+ typography: Typography;
112
+ spacing: Spacing;
113
+ borderRadius: BorderRadius;
114
+ shadows: Shadows;
115
+ animations: AnimationDurations;
116
+ }
117
+
118
+ // Default typography (constant across themes)
119
+ export const defaultTypography: Typography = {
120
+ fontSizes: {
121
+ xs: 12,
122
+ sm: 14,
123
+ base: 15,
124
+ lg: 16,
125
+ xl: 18,
126
+ '2xl': 20,
127
+ '3xl': 24,
128
+ },
129
+ fontWeights: {
130
+ normal: '400',
131
+ medium: '500',
132
+ semibold: '600',
133
+ bold: '700',
134
+ },
135
+ lineHeights: {
136
+ tight: 1.2,
137
+ normal: 1.5,
138
+ relaxed: 1.75,
139
+ },
140
+ };
141
+
142
+ // Default spacing (constant across themes)
143
+ export const defaultSpacing: Spacing = {
144
+ xs: 4,
145
+ sm: 8,
146
+ md: 12,
147
+ lg: 16,
148
+ xl: 20,
149
+ '2xl': 24,
150
+ '3xl': 32,
151
+ };
152
+
153
+ // Default border radius (constant across themes)
154
+ export const defaultBorderRadius: BorderRadius = {
155
+ sm: 4,
156
+ md: 8,
157
+ lg: 12,
158
+ xl: 16,
159
+ full: 9999,
160
+ };
161
+
162
+ // Default shadows (constant across themes)
163
+ export const defaultShadows: Shadows = {
164
+ sm: {
165
+ shadowColor: '#000',
166
+ shadowOffset: { width: 0, height: 1 },
167
+ shadowOpacity: 0.1,
168
+ shadowRadius: 2,
169
+ elevation: 2,
170
+ },
171
+ md: {
172
+ shadowColor: '#000',
173
+ shadowOffset: { width: 0, height: 2 },
174
+ shadowOpacity: 0.15,
175
+ shadowRadius: 4,
176
+ elevation: 4,
177
+ },
178
+ lg: {
179
+ shadowColor: '#000',
180
+ shadowOffset: { width: 0, height: 4 },
181
+ shadowOpacity: 0.2,
182
+ shadowRadius: 8,
183
+ elevation: 8,
184
+ },
185
+ };
186
+
187
+ // Default animation durations (constant across themes)
188
+ export const defaultAnimations: AnimationDurations = {
189
+ fast: 150,
190
+ normal: 300,
191
+ slow: 500,
192
+ };
193
+
194
+ /**
195
+ * Create theme colors from backend widget config
196
+ */
197
+ export const createThemeColors = (
198
+ primaryColor: string,
199
+ backgroundColor: string,
200
+ textColor: string,
201
+ theme: 'light' | 'dark' = 'light'
202
+ ): ThemeColors => {
203
+ const isLight = theme === 'light';
204
+
205
+ return {
206
+ primary: primaryColor,
207
+ background: backgroundColor,
208
+ text: textColor,
209
+ // Semantic colors
210
+ userBubble: primaryColor,
211
+ botBubble: isLight ? '#ffffff' : '#2d3748',
212
+ userText: '#ffffff',
213
+ botText: isLight ? '#1f2937' : '#f9fafb',
214
+ inputBackground: isLight ? '#f9fafb' : '#2d3748',
215
+ inputBorder: isLight ? '#e5e7eb' : '#4a5568',
216
+ inputText: isLight ? '#1f2937' : '#f9fafb',
217
+ headerBackground: primaryColor,
218
+ headerText: '#ffffff',
219
+ errorBackground: '#fef2f2',
220
+ errorText: '#991b1b',
221
+ badge: '#ef4444',
222
+ loadingIndicator: primaryColor,
223
+ // Neutral colors
224
+ gray50: '#f9fafb',
225
+ gray100: '#f3f4f6',
226
+ gray200: '#e5e7eb',
227
+ gray300: '#d1d5db',
228
+ gray400: '#9ca3af',
229
+ gray500: '#6b7280',
230
+ gray600: '#4b5563',
231
+ gray700: '#374151',
232
+ gray800: '#1f2937',
233
+ gray900: '#111827',
234
+ };
235
+ };
236
+
237
+ /**
238
+ * Create complete theme from backend widget config
239
+ */
240
+ export const createTheme = (
241
+ primaryColor: string = '#4ade80',
242
+ backgroundColor: string = '#ffffff',
243
+ textColor: string = '#1f2937',
244
+ themeMode: 'light' | 'dark' = 'light'
245
+ ): Theme => {
246
+ return {
247
+ colors: createThemeColors(primaryColor, backgroundColor, textColor, themeMode),
248
+ typography: defaultTypography,
249
+ spacing: defaultSpacing,
250
+ borderRadius: defaultBorderRadius,
251
+ shadows: defaultShadows,
252
+ animations: defaultAnimations,
253
+ };
254
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Core types for Lingu React Native SDK
3
+ */
4
+
5
+ export interface LinguConfig {
6
+ apiKey: string;
7
+ baseURL?: string;
8
+ autoOpen?: boolean;
9
+ onSessionStart?: (sessionId: string) => void;
10
+ onSessionEnd?: (sessionId: string) => void;
11
+ onMessageSent?: (message: string) => void;
12
+ onMessageReceived?: (message: string) => void;
13
+ }
14
+
15
+ export type ChatPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
16
+
17
+ export type ThemeMode = 'light' | 'dark' | 'system';
18
+
19
+ export type MessageSender = 'user' | 'bot';
20
+
21
+ export interface Message {
22
+ id: string;
23
+ text: string;
24
+ sender: MessageSender;
25
+ timestamp: Date;
26
+ }
27
+
28
+ export interface WidgetConfig {
29
+ widgetPosition: ChatPosition;
30
+ widgetSize: 'small' | 'medium' | 'large';
31
+ theme: ThemeMode;
32
+ primaryColor: string;
33
+ backgroundColor: string;
34
+ textColor: string;
35
+ brandName?: string;
36
+ brandLogo?: string;
37
+ greetingMessage: string;
38
+ }
39
+
40
+ export interface SessionData {
41
+ sessionId: string;
42
+ startedAt: Date;
43
+ lastActivity: Date;
44
+ }
45
+
46
+ export interface ApiResponse<T> {
47
+ success: boolean;
48
+ data: T;
49
+ error?: string;
50
+ }
51
+
52
+ export interface ChatResponse {
53
+ message: string;
54
+ context?: any[];
55
+ }
56
+
57
+ export interface SessionResponse {
58
+ sessionId: string;
59
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Animation Utilities
3
+ * Reusable animation functions for SDK components
4
+ */
5
+
6
+ import { Animated, Easing } from 'react-native';
7
+
8
+ /**
9
+ * Fade in animation
10
+ */
11
+ export const fadeIn = (
12
+ animatedValue: Animated.Value,
13
+ duration: number = 300,
14
+ toValue: number = 1
15
+ ): Animated.CompositeAnimation => {
16
+ return Animated.timing(animatedValue, {
17
+ toValue,
18
+ duration,
19
+ easing: Easing.out(Easing.ease),
20
+ useNativeDriver: true,
21
+ });
22
+ };
23
+
24
+ /**
25
+ * Fade out animation
26
+ */
27
+ export const fadeOut = (
28
+ animatedValue: Animated.Value,
29
+ duration: number = 300,
30
+ toValue: number = 0
31
+ ): Animated.CompositeAnimation => {
32
+ return Animated.timing(animatedValue, {
33
+ toValue,
34
+ duration,
35
+ easing: Easing.in(Easing.ease),
36
+ useNativeDriver: true,
37
+ });
38
+ };
39
+
40
+ /**
41
+ * Slide up animation (for messages)
42
+ */
43
+ export const slideUp = (
44
+ animatedValue: Animated.Value,
45
+ duration: number = 300
46
+ ): Animated.CompositeAnimation => {
47
+ return Animated.timing(animatedValue, {
48
+ toValue: 0,
49
+ duration,
50
+ easing: Easing.out(Easing.cubic),
51
+ useNativeDriver: true,
52
+ });
53
+ };
54
+
55
+ /**
56
+ * Scale animation (for FAB entrance)
57
+ */
58
+ export const scaleIn = (
59
+ animatedValue: Animated.Value
60
+ ): Animated.CompositeAnimation => {
61
+ return Animated.spring(animatedValue, {
62
+ toValue: 1,
63
+ friction: 8,
64
+ tension: 40,
65
+ useNativeDriver: true,
66
+ });
67
+ };
68
+
69
+ /**
70
+ * Pulse animation (for unread badge on FAB)
71
+ */
72
+ export const pulse = (
73
+ animatedValue: Animated.Value
74
+ ): Animated.CompositeAnimation => {
75
+ return Animated.loop(
76
+ Animated.sequence([
77
+ Animated.timing(animatedValue, {
78
+ toValue: 1.2,
79
+ duration: 500,
80
+ easing: Easing.inOut(Easing.ease),
81
+ useNativeDriver: true,
82
+ }),
83
+ Animated.timing(animatedValue, {
84
+ toValue: 1,
85
+ duration: 500,
86
+ easing: Easing.inOut(Easing.ease),
87
+ useNativeDriver: true,
88
+ }),
89
+ ])
90
+ );
91
+ };
92
+
93
+ /**
94
+ * Typing indicator animation (for the dots)
95
+ */
96
+ export const typingDotAnimation = (
97
+ animatedValue: Animated.Value,
98
+ delay: number = 0
99
+ ): Animated.CompositeAnimation => {
100
+ return Animated.loop(
101
+ Animated.sequence([
102
+ Animated.delay(delay),
103
+ Animated.timing(animatedValue, {
104
+ toValue: -8,
105
+ duration: 400,
106
+ easing: Easing.inOut(Easing.ease),
107
+ useNativeDriver: true,
108
+ }),
109
+ Animated.timing(animatedValue, {
110
+ toValue: 0,
111
+ duration: 400,
112
+ easing: Easing.inOut(Easing.ease),
113
+ useNativeDriver: true,
114
+ }),
115
+ ])
116
+ );
117
+ };
118
+
119
+ /**
120
+ * Message entrance animation (slide up + fade in)
121
+ */
122
+ export const messageEntrance = (
123
+ translateY: Animated.Value,
124
+ opacity: Animated.Value,
125
+ duration: number = 300
126
+ ): Animated.CompositeAnimation => {
127
+ return Animated.parallel([
128
+ Animated.timing(translateY, {
129
+ toValue: 0,
130
+ duration,
131
+ easing: Easing.out(Easing.cubic),
132
+ useNativeDriver: true,
133
+ }),
134
+ Animated.timing(opacity, {
135
+ toValue: 1,
136
+ duration,
137
+ easing: Easing.out(Easing.ease),
138
+ useNativeDriver: true,
139
+ }),
140
+ ]);
141
+ };
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Formatting Utilities
3
+ * Helper functions for formatting data in the SDK
4
+ */
5
+
6
+ /**
7
+ * Format a date to relative time (e.g., "just now", "2m ago", "1h ago")
8
+ */
9
+ export const formatRelativeTime = (date: Date): string => {
10
+ const now = new Date();
11
+ const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
12
+
13
+ if (diffInSeconds < 10) {
14
+ return 'just now';
15
+ }
16
+
17
+ if (diffInSeconds < 60) {
18
+ return `${diffInSeconds}s ago`;
19
+ }
20
+
21
+ const diffInMinutes = Math.floor(diffInSeconds / 60);
22
+ if (diffInMinutes < 60) {
23
+ return `${diffInMinutes}m ago`;
24
+ }
25
+
26
+ const diffInHours = Math.floor(diffInMinutes / 60);
27
+ if (diffInHours < 24) {
28
+ return `${diffInHours}h ago`;
29
+ }
30
+
31
+ const diffInDays = Math.floor(diffInHours / 24);
32
+ if (diffInDays < 7) {
33
+ return `${diffInDays}d ago`;
34
+ }
35
+
36
+ // For dates older than a week, show the actual date
37
+ return date.toLocaleDateString();
38
+ };
39
+
40
+ /**
41
+ * Format time for display (e.g., "2:30 PM")
42
+ */
43
+ export const formatTime = (date: Date): string => {
44
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
45
+ };
46
+
47
+ /**
48
+ * Truncate text with ellipsis
49
+ */
50
+ export const truncate = (text: string, maxLength: number): string => {
51
+ if (text.length <= maxLength) {
52
+ return text;
53
+ }
54
+ return text.substring(0, maxLength - 3) + '...';
55
+ };
56
+
57
+ /**
58
+ * Check if a color is light or dark (for text contrast)
59
+ */
60
+ export const isLightColor = (color: string): boolean => {
61
+ // Remove # if present
62
+ const hex = color.replace('#', '');
63
+
64
+ // Convert to RGB
65
+ const r = parseInt(hex.substring(0, 2), 16);
66
+ const g = parseInt(hex.substring(2, 4), 16);
67
+ const b = parseInt(hex.substring(4, 6), 16);
68
+
69
+ // Calculate luminance
70
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
71
+
72
+ return luminance > 0.5;
73
+ };
74
+
75
+ /**
76
+ * Get contrasting text color for a background
77
+ */
78
+ export const getContrastingColor = (backgroundColor: string): string => {
79
+ return isLightColor(backgroundColor) ? '#000000' : '#ffffff';
80
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utilities exports
3
+ */
4
+
5
+ export * from './animations';
6
+ export * from './formatters';