xertica-ui 1.0.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 (141) hide show
  1. package/App.tsx +182 -0
  2. package/README.md +330 -0
  3. package/assets/xertica-logo.svg +38 -0
  4. package/assets/xertica-x-logo.svg +21 -0
  5. package/bin/cli.ts +193 -0
  6. package/components/AssistenteXertica.tsx +2003 -0
  7. package/components/AudioPlayer.tsx +203 -0
  8. package/components/CodeBlock.tsx +242 -0
  9. package/components/DocumentEditor.tsx +504 -0
  10. package/components/ForgotPasswordPage.tsx +170 -0
  11. package/components/FormattedDocument.tsx +87 -0
  12. package/components/HomeContent.tsx +123 -0
  13. package/components/HomePage.tsx +70 -0
  14. package/components/LanguageSelector.tsx +54 -0
  15. package/components/LoginPage.tsx +199 -0
  16. package/components/MarkdownMessage.tsx +62 -0
  17. package/components/ModernChatInput.tsx +502 -0
  18. package/components/PodcastPlayer.tsx +409 -0
  19. package/components/ResetPasswordPage.tsx +234 -0
  20. package/components/Sidebar.tsx +489 -0
  21. package/components/TemplateContent.tsx +629 -0
  22. package/components/TemplatePage.tsx +70 -0
  23. package/components/ThemeToggle.tsx +65 -0
  24. package/components/VerifyEmailPage.tsx +187 -0
  25. package/components/XerticaLogo.tsx +69 -0
  26. package/components/XerticaOrbe.tsx +1339 -0
  27. package/components/XerticaXLogo.tsx +53 -0
  28. package/components/examples/DrawingMapExample.tsx +530 -0
  29. package/components/examples/FilterableMapExample.tsx +380 -0
  30. package/components/examples/LocationPickerExample.tsx +330 -0
  31. package/components/examples/MapExamples.tsx +280 -0
  32. package/components/examples/MapShowcase.tsx +446 -0
  33. package/components/examples/RouteMapExamples.tsx +329 -0
  34. package/components/examples/SimpleFilterableMap.tsx +192 -0
  35. package/components/examples/index.ts +52 -0
  36. package/components/figma/ImageWithFallback.tsx +27 -0
  37. package/components/index.ts +44 -0
  38. package/components/media/AudioPlayer.tsx +278 -0
  39. package/components/media/FloatingMediaWrapper.tsx +166 -0
  40. package/components/media/VideoPlayer.tsx +285 -0
  41. package/components/ui/accordion.tsx +66 -0
  42. package/components/ui/alert-dialog.tsx +159 -0
  43. package/components/ui/alert.tsx +91 -0
  44. package/components/ui/aspect-ratio.tsx +11 -0
  45. package/components/ui/avatar.tsx +65 -0
  46. package/components/ui/badge.tsx +55 -0
  47. package/components/ui/breadcrumb.tsx +109 -0
  48. package/components/ui/button.tsx +78 -0
  49. package/components/ui/calendar.tsx +235 -0
  50. package/components/ui/card.tsx +92 -0
  51. package/components/ui/carousel.tsx +241 -0
  52. package/components/ui/chart.tsx +353 -0
  53. package/components/ui/checkbox.tsx +32 -0
  54. package/components/ui/collapsible.tsx +33 -0
  55. package/components/ui/command.tsx +177 -0
  56. package/components/ui/context-menu.tsx +252 -0
  57. package/components/ui/dialog.tsx +138 -0
  58. package/components/ui/drawer.tsx +134 -0
  59. package/components/ui/dropdown-menu.tsx +257 -0
  60. package/components/ui/empty.tsx +90 -0
  61. package/components/ui/file-upload.tsx +152 -0
  62. package/components/ui/form.tsx +195 -0
  63. package/components/ui/google-maps-loader.tsx +379 -0
  64. package/components/ui/hover-card.tsx +44 -0
  65. package/components/ui/index.ts +242 -0
  66. package/components/ui/input-otp.tsx +77 -0
  67. package/components/ui/input.tsx +38 -0
  68. package/components/ui/label.tsx +24 -0
  69. package/components/ui/map-config.ts +12 -0
  70. package/components/ui/map-layers.tsx +129 -0
  71. package/components/ui/map.exports.ts +31 -0
  72. package/components/ui/map.tsx +412 -0
  73. package/components/ui/menubar.tsx +276 -0
  74. package/components/ui/navigation-menu.tsx +162 -0
  75. package/components/ui/notification-badge.tsx +61 -0
  76. package/components/ui/page-header.tsx +229 -0
  77. package/components/ui/pagination.tsx +127 -0
  78. package/components/ui/popover.tsx +48 -0
  79. package/components/ui/progress.tsx +31 -0
  80. package/components/ui/radio-group.tsx +56 -0
  81. package/components/ui/rating.tsx +102 -0
  82. package/components/ui/resizable.tsx +405 -0
  83. package/components/ui/route-map.tsx +246 -0
  84. package/components/ui/scroll-area.tsx +58 -0
  85. package/components/ui/search.tsx +70 -0
  86. package/components/ui/select.tsx +176 -0
  87. package/components/ui/separator.tsx +28 -0
  88. package/components/ui/sheet.tsx +138 -0
  89. package/components/ui/sidebar.tsx +726 -0
  90. package/components/ui/simple-map.tsx +92 -0
  91. package/components/ui/skeleton.tsx +13 -0
  92. package/components/ui/slider.tsx +58 -0
  93. package/components/ui/sonner.tsx +77 -0
  94. package/components/ui/stats-card.tsx +84 -0
  95. package/components/ui/stepper.tsx +126 -0
  96. package/components/ui/switch.tsx +34 -0
  97. package/components/ui/table.tsx +116 -0
  98. package/components/ui/tabs.tsx +66 -0
  99. package/components/ui/textarea.tsx +26 -0
  100. package/components/ui/timeline.tsx +140 -0
  101. package/components/ui/toggle-group.tsx +71 -0
  102. package/components/ui/toggle.tsx +46 -0
  103. package/components/ui/tooltip.tsx +61 -0
  104. package/components/ui/tree-view.tsx +123 -0
  105. package/components/ui/use-mobile.ts +24 -0
  106. package/components/ui/utils.ts +6 -0
  107. package/components/ui/xertica-assistant.tsx +1420 -0
  108. package/contexts/ApiKeyContext.tsx +123 -0
  109. package/contexts/AssistenteContext.tsx +118 -0
  110. package/contexts/BrandColorsContext.tsx +551 -0
  111. package/contexts/LanguageContext.tsx +36 -0
  112. package/contexts/ThemeContext.tsx +85 -0
  113. package/dist/cli.js +20922 -0
  114. package/eslint.config.js +41 -0
  115. package/guidelines/Guidelines.md +61 -0
  116. package/hooks/useTheme.ts +4 -0
  117. package/imports/Podcast.tsx +389 -0
  118. package/imports/XerticaAi.tsx +46 -0
  119. package/imports/XerticaX.tsx +20 -0
  120. package/imports/svg-aueiaqngck.ts +11 -0
  121. package/imports/svg-v9krss1ozd.ts +16 -0
  122. package/imports/svg-vhrdofe3qe.ts +5 -0
  123. package/index.css +4448 -0
  124. package/index.html +14 -0
  125. package/main.tsx +10 -0
  126. package/package.json +119 -0
  127. package/postcss.config.js +6 -0
  128. package/routes.tsx +33 -0
  129. package/styles/globals.css +15 -0
  130. package/styles/xertica/app-overrides/chat.css +61 -0
  131. package/styles/xertica/app-overrides/scrollbar.css +33 -0
  132. package/styles/xertica/base.css +70 -0
  133. package/styles/xertica/integrations/google-maps.css +76 -0
  134. package/styles/xertica/integrations/sonner.css +73 -0
  135. package/styles/xertica/theme-map.css +88 -0
  136. package/styles/xertica/tokens.css +190 -0
  137. package/tsconfig.json +31 -0
  138. package/tsconfig.node.json +10 -0
  139. package/utils/gemini.ts +140 -0
  140. package/vite-env.d.ts +12 -0
  141. package/vite.config.ts +36 -0
@@ -0,0 +1,551 @@
1
+ import React, { createContext, useContext, useState, useEffect } from 'react';
2
+
3
+ interface BrandColors {
4
+ // Main Colors
5
+ primary: string;
6
+ primaryForeground: string;
7
+ primaryLight: string; // Used for backgrounds
8
+
9
+ // Dark Mode Overrides
10
+ primaryDarkMode: string;
11
+ primaryForegroundDark: string;
12
+
13
+ // Sidebar Colors
14
+ sidebarLight: string;
15
+ sidebarDark: string;
16
+
17
+ // Gradients
18
+ gradientStart: string;
19
+ gradientEnd: string;
20
+ gradientStartDark: string;
21
+ gradientEndDark: string;
22
+
23
+ // Chart Colors (Light Mode) - Dark mode will be calculated or fixed
24
+ chart1: string;
25
+ chart2: string;
26
+ chart3: string;
27
+ chart4: string;
28
+ chart5: string;
29
+
30
+ // Optional: Radius override per theme
31
+ radius?: string;
32
+ }
33
+
34
+ export interface ColorTheme {
35
+ id: string;
36
+ name: string;
37
+ description: string;
38
+ colors: BrandColors;
39
+ preview: {
40
+ primary: string;
41
+ secondary: string;
42
+ accent: string;
43
+ };
44
+ }
45
+
46
+ interface BrandColorsContextType {
47
+ colors: BrandColors;
48
+ currentTheme: string;
49
+ themes: ColorTheme[];
50
+ setTheme: (themeId: string) => void;
51
+ radius: number;
52
+ setRadius: (radius: number) => void;
53
+ }
54
+
55
+ // Shadcn Standard Palette Values (approximate hex for compatibility)
56
+ const PALETTE = {
57
+ zinc: { 900: '#18181b', 50: '#fafafa' },
58
+ slate: { 900: '#0f172a', 50: '#f8fafc' },
59
+ stone: { 900: '#1c1917', 50: '#fafaf9' },
60
+ gray: { 900: '#111827', 50: '#f9fafb' },
61
+ neutral: { 900: '#171717', 50: '#fafafa' },
62
+ red: { 600: '#dc2626', 500: '#ef4444' },
63
+ rose: { 600: '#e11d48', 500: '#f43f5e' },
64
+ orange: { 600: '#ea580c', 500: '#f97316' },
65
+ green: { 600: '#16a34a', 500: '#22c55e' },
66
+ blue: { 600: '#2563eb', 500: '#3b82f6' },
67
+ yellow: { 600: '#ca8a04', 500: '#eab308' },
68
+ violet: { 600: '#7c3aed', 500: '#8b5cf6' },
69
+ };
70
+
71
+ export const colorThemes: ColorTheme[] = [
72
+ {
73
+ id: 'xertica-original',
74
+ name: 'Xertica Original',
75
+ description: 'A identidade visual clássica da Xertica com o gradiente original',
76
+ colors: {
77
+ primary: '#2C275B',
78
+ primaryForeground: '#FFFFFF',
79
+ primaryLight: '#5B568F',
80
+ primaryDarkMode: '#72CDFD',
81
+ primaryForegroundDark: '#09090B',
82
+
83
+ sidebarLight: '#2C275B', // Primary Color
84
+ sidebarDark: '#1a1928', // Original Dark BG
85
+
86
+ gradientStart: '#FDB0F2',
87
+ gradientEnd: '#72CDFD',
88
+ gradientStartDark: '#7B4A7A',
89
+ gradientEndDark: '#3A5C7D',
90
+
91
+ chart1: '#2C275B',
92
+ chart2: '#FDB0F2',
93
+ chart3: '#72CDFD',
94
+ chart4: '#5B568F',
95
+ chart5: '#4F46E5',
96
+ },
97
+ preview: {
98
+ primary: '#2C275B',
99
+ secondary: '#FDB0F2',
100
+ accent: '#72CDFD',
101
+ },
102
+ },
103
+ {
104
+ id: 'zinc',
105
+ name: 'Zinc',
106
+ description: 'Minimalista, sério e elegante (Escala de Cinza)',
107
+ colors: {
108
+ primary: '#18181B',
109
+ primaryForeground: '#FAFAFA',
110
+ primaryLight: '#E4E4E7',
111
+ primaryDarkMode: '#FAFAFA',
112
+ primaryForegroundDark: '#18181B',
113
+
114
+ sidebarLight: '#18181B', // Primary Color
115
+ sidebarDark: '#09090B',
116
+
117
+ gradientStart: '#52525B',
118
+ gradientEnd: '#D4D4D8',
119
+ gradientStartDark: '#27272A',
120
+ gradientEndDark: '#52525B',
121
+
122
+ chart1: '#18181B',
123
+ chart2: '#52525B',
124
+ chart3: '#A1A1AA',
125
+ chart4: '#D4D4D8',
126
+ chart5: '#E4E4E7',
127
+ },
128
+ preview: {
129
+ primary: '#18181B',
130
+ secondary: '#52525B',
131
+ accent: '#E4E4E7',
132
+ },
133
+ },
134
+ {
135
+ id: 'slate',
136
+ name: 'Slate',
137
+ description: 'Profissional com tons de azul acinzentado',
138
+ colors: {
139
+ primary: '#0F172A',
140
+ primaryForeground: '#F8FAFC',
141
+ primaryLight: '#CBD5E1',
142
+ primaryDarkMode: '#F8FAFC',
143
+ primaryForegroundDark: '#0F172A',
144
+
145
+ sidebarLight: '#0F172A', // Primary Color
146
+ sidebarDark: '#020617',
147
+
148
+ gradientStart: '#475569',
149
+ gradientEnd: '#94A3B8',
150
+ gradientStartDark: '#1E293B',
151
+ gradientEndDark: '#475569',
152
+
153
+ chart1: '#0F172A',
154
+ chart2: '#475569',
155
+ chart3: '#94A3B8',
156
+ chart4: '#CBD5E1',
157
+ chart5: '#E2E8F0',
158
+ },
159
+ preview: {
160
+ primary: '#0F172A',
161
+ secondary: '#475569',
162
+ accent: '#94A3B8',
163
+ },
164
+ },
165
+ {
166
+ id: 'blue',
167
+ name: 'Blue',
168
+ description: 'Confiável e seguro, padrão corporativo',
169
+ colors: {
170
+ primary: '#2563EB',
171
+ primaryForeground: '#FFFFFF',
172
+ primaryLight: '#DBEAFE',
173
+ primaryDarkMode: '#3B82F6',
174
+ primaryForegroundDark: '#FFFFFF',
175
+
176
+ sidebarLight: '#1E3A8A', // Blue 900 (High Contrast Sidebar)
177
+ sidebarDark: '#172554',
178
+
179
+ gradientStart: '#2563EB',
180
+ gradientEnd: '#60A5FA',
181
+ gradientStartDark: '#1D4ED8',
182
+ gradientEndDark: '#2563EB',
183
+
184
+ chart1: '#2563EB',
185
+ chart2: '#3B82F6',
186
+ chart3: '#60A5FA',
187
+ chart4: '#93C5FD',
188
+ chart5: '#BFDBFE',
189
+ },
190
+ preview: {
191
+ primary: '#2563EB',
192
+ secondary: '#60A5FA',
193
+ accent: '#DBEAFE',
194
+ },
195
+ },
196
+ {
197
+ id: 'violet',
198
+ name: 'Violet',
199
+ description: 'Criativo e vibrante',
200
+ colors: {
201
+ primary: '#7C3AED',
202
+ primaryForeground: '#FFFFFF',
203
+ primaryLight: '#EDE9FE',
204
+ primaryDarkMode: '#8B5CF6',
205
+ primaryForegroundDark: '#FFFFFF',
206
+
207
+ sidebarLight: '#4C1D95', // Violet 900 (High Contrast Sidebar)
208
+ sidebarDark: '#2E1065',
209
+
210
+ gradientStart: '#7C3AED',
211
+ gradientEnd: '#A78BFA',
212
+ gradientStartDark: '#5B21B6',
213
+ gradientEndDark: '#7C3AED',
214
+
215
+ chart1: '#7C3AED',
216
+ chart2: '#8B5CF6',
217
+ chart3: '#A78BFA',
218
+ chart4: '#C4B5FD',
219
+ chart5: '#DDD6FE',
220
+ },
221
+ preview: {
222
+ primary: '#7C3AED',
223
+ secondary: '#A78BFA',
224
+ accent: '#EDE9FE',
225
+ },
226
+ },
227
+ {
228
+ id: 'rose',
229
+ name: 'Rose',
230
+ description: 'Elegante e suave',
231
+ colors: {
232
+ primary: '#BE123C', // Rose 700 (Better text contrast for Primary)
233
+ primaryForeground: '#FFFFFF',
234
+ primaryLight: '#FFE4E6',
235
+ primaryDarkMode: '#F43F5E',
236
+ primaryForegroundDark: '#FFFFFF',
237
+
238
+ sidebarLight: '#881337', // Rose 900 (High Contrast Sidebar)
239
+ sidebarDark: '#881337',
240
+
241
+ gradientStart: '#E11D48',
242
+ gradientEnd: '#FB7185',
243
+ gradientStartDark: '#9F1239',
244
+ gradientEndDark: '#E11D48',
245
+
246
+ chart1: '#E11D48',
247
+ chart2: '#F43F5E',
248
+ chart3: '#FB7185',
249
+ chart4: '#FDA4AF',
250
+ chart5: '#FECDD3',
251
+ },
252
+ preview: {
253
+ primary: '#BE123C',
254
+ secondary: '#FB7185',
255
+ accent: '#FFE4E6',
256
+ },
257
+ },
258
+ {
259
+ id: 'emerald',
260
+ name: 'Emerald',
261
+ description: 'Natural e equilibrado',
262
+ colors: {
263
+ primary: '#047857', // Emerald 700 (Better text contrast for Primary)
264
+ primaryForeground: '#FFFFFF',
265
+ primaryLight: '#D1FAE5',
266
+ primaryDarkMode: '#10B981',
267
+ primaryForegroundDark: '#FFFFFF',
268
+
269
+ sidebarLight: '#064E3B', // Emerald 900 (High Contrast Sidebar)
270
+ sidebarDark: '#064E3B',
271
+
272
+ gradientStart: '#059669',
273
+ gradientEnd: '#34D399',
274
+ gradientStartDark: '#064E3B',
275
+ gradientEndDark: '#059669',
276
+
277
+ chart1: '#059669',
278
+ chart2: '#10B981',
279
+ chart3: '#34D399',
280
+ chart4: '#6EE7B7',
281
+ chart5: '#A7F3D0',
282
+ },
283
+ preview: {
284
+ primary: '#047857',
285
+ secondary: '#34D399',
286
+ accent: '#D1FAE5',
287
+ },
288
+ },
289
+ {
290
+ id: 'amber',
291
+ name: 'Amber',
292
+ description: 'Quente e energético',
293
+ colors: {
294
+ primary: '#B45309', // Amber 700 (Better text contrast for Primary)
295
+ primaryForeground: '#FFFFFF',
296
+ primaryLight: '#FEF3C7',
297
+ primaryDarkMode: '#F59E0B',
298
+ primaryForegroundDark: '#FFFFFF',
299
+
300
+ sidebarLight: '#78350F', // Amber 900 (High Contrast Sidebar)
301
+ sidebarDark: '#78350F',
302
+
303
+ gradientStart: '#D97706',
304
+ gradientEnd: '#FBBF24',
305
+ gradientStartDark: '#92400E',
306
+ gradientEndDark: '#D97706',
307
+
308
+ chart1: '#D97706',
309
+ chart2: '#F59E0B',
310
+ chart3: '#FBBF24',
311
+ chart4: '#FDE68A',
312
+ chart5: '#FEF3C7',
313
+ },
314
+ preview: {
315
+ primary: '#B45309',
316
+ secondary: '#FBBF24',
317
+ accent: '#FEF3C7',
318
+ },
319
+ },
320
+ {
321
+ id: 'orange',
322
+ name: 'Orange',
323
+ description: 'Vibrante e amigável',
324
+ colors: {
325
+ primary: '#C2410C', // Orange 700 (Better text contrast for Primary)
326
+ primaryForeground: '#FFFFFF',
327
+ primaryLight: '#FFEDD5',
328
+ primaryDarkMode: '#F97316',
329
+ primaryForegroundDark: '#FFFFFF',
330
+
331
+ sidebarLight: '#7C2D12', // Orange 900 (High Contrast Sidebar)
332
+ sidebarDark: '#7C2D12',
333
+
334
+ gradientStart: '#EA580C',
335
+ gradientEnd: '#FB923C',
336
+ gradientStartDark: '#9A3412',
337
+ gradientEndDark: '#EA580C',
338
+
339
+ chart1: '#EA580C',
340
+ chart2: '#F97316',
341
+ chart3: '#FB923C',
342
+ chart4: '#FDBA74',
343
+ chart5: '#FED7AA',
344
+ },
345
+ preview: {
346
+ primary: '#C2410C',
347
+ secondary: '#FB923C',
348
+ accent: '#FFEDD5',
349
+ },
350
+ },
351
+ ];
352
+
353
+ const BrandColorsContext = createContext<BrandColorsContextType | undefined>(undefined);
354
+
355
+ export const BrandColorsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
356
+ const [currentTheme, setCurrentTheme] = useState<string>(() => {
357
+ const saved = localStorage.getItem('color-theme');
358
+ return saved || 'xertica-original';
359
+ });
360
+
361
+ const [radius, setRadius] = useState<number>(0.5);
362
+
363
+ const [colors, setColors] = useState<BrandColors>(() => {
364
+ const theme = colorThemes.find(t => t.id === currentTheme);
365
+ return theme?.colors || colorThemes[0].colors;
366
+ });
367
+
368
+ useEffect(() => {
369
+ const theme = colorThemes.find(t => t.id === currentTheme);
370
+ if (theme) {
371
+ setColors(theme.colors);
372
+ }
373
+ }, [currentTheme]);
374
+
375
+ // Função auxiliar para converter hex para rgb
376
+ const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
377
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
378
+ return result
379
+ ? {
380
+ r: parseInt(result[1], 16),
381
+ g: parseInt(result[2], 16),
382
+ b: parseInt(result[3], 16),
383
+ }
384
+ : null;
385
+ };
386
+
387
+ // Função para aplicar as cores baseadas no modo atual e tema
388
+ const applyColors = React.useCallback(() => {
389
+ const root = document.documentElement;
390
+ const isDark = root.classList.contains('dark');
391
+
392
+ // --- Determine Colors based on Mode ---
393
+ const primary = isDark ? colors.primaryDarkMode : colors.primary;
394
+ const primaryForeground = isDark ? colors.primaryForegroundDark : colors.primaryForeground;
395
+
396
+ // Gradient Logic
397
+ const gradientStart = isDark ? colors.gradientStartDark : colors.gradientStart;
398
+ const gradientEnd = isDark ? colors.gradientEndDark : colors.gradientEnd;
399
+
400
+ // --- Semantic Colors (Fixed Standard) ---
401
+ // Success (Green), Warning (Amber/Yellow), Info (Blue), Destructive (Red)
402
+ const semantic = {
403
+ success: isDark ? '#4ade80' : '#22c55e', // Green 400 / 500
404
+ warning: isDark ? '#fbbf24' : '#f59e0b', // Amber 400 / 500
405
+ info: isDark ? '#60a5fa' : '#3b82f6', // Blue 400 / 500
406
+ destructive: isDark ? '#f87171' : '#ef4444', // Red 400 / 500
407
+ };
408
+
409
+ // --- Apply CSS Variables ---
410
+
411
+ // 1. Core Primary Colors
412
+ root.style.setProperty('--primary', primary);
413
+ root.style.setProperty('--primary-foreground', primaryForeground);
414
+
415
+ // 2. Semantic Colors
416
+ root.style.setProperty('--success', semantic.success);
417
+ root.style.setProperty('--success-foreground', '#ffffff');
418
+ root.style.setProperty('--warning', semantic.warning);
419
+ root.style.setProperty('--warning-foreground', '#ffffff');
420
+ root.style.setProperty('--info', semantic.info);
421
+ root.style.setProperty('--info-foreground', '#ffffff');
422
+ root.style.setProperty('--destructive', semantic.destructive);
423
+ root.style.setProperty('--destructive-foreground', '#ffffff');
424
+
425
+ // 3. Secondary & Accents (Derived or Specific)
426
+
427
+ // 2. Secondary & Accents (Derived or Specific)
428
+ // We use primaryLight for subtle backgrounds
429
+ // In dark mode, we might want to adjust opacity
430
+ const primaryRGB = hexToRgb(primary);
431
+ if (primaryRGB) {
432
+ // Create a subtle tint for secondary/accent/muted
433
+ const alpha = isDark ? 0.2 : 0.1;
434
+ const subtle = `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, ${alpha})`;
435
+
436
+ // Note: Shadcn usually has specific greys for secondary, but for "Brand" themes
437
+ // we can tint them slightly or stick to standard zinc.
438
+ // For this implementation, we will keep existing "neutral" variables from CSS
439
+ // but override "ring" and "sidebar-ring" to match brand.
440
+
441
+ root.style.setProperty('--ring', isDark ? colors.primaryDarkMode : `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.5)`);
442
+
443
+ // Also update the Xertica specific variables
444
+ root.style.setProperty('--xertica-primary', primary);
445
+ root.style.setProperty('--primary-light', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.15)`);
446
+ root.style.setProperty('--primary-light-foreground', primary);
447
+ }
448
+
449
+ // 3. Radius
450
+ root.style.setProperty('--radius', `${radius}rem`);
451
+
452
+ // 4. Charts
453
+ root.style.setProperty('--chart-1', colors.chart1);
454
+ root.style.setProperty('--chart-2', colors.chart2);
455
+ root.style.setProperty('--chart-3', colors.chart3);
456
+ root.style.setProperty('--chart-4', colors.chart4);
457
+ root.style.setProperty('--chart-5', colors.chart5);
458
+
459
+ // 5. Sidebar (Override)
460
+ // Dynamic sidebar colors based on theme and mode
461
+ const sidebarBg = isDark ? colors.sidebarDark : colors.sidebarLight;
462
+
463
+ // Determine if sidebar background is dark or light to set text color
464
+ // We assume if the sidebar background is the Primary Color (which are generally dark/saturated), text should be white.
465
+ // Xertica Original (Purple), Blue, Violet, etc. are all dark.
466
+ // If sidebarLight is White (#FFFFFF) or very light, text should be dark.
467
+
468
+ // Simple check: is sidebarBg roughly White/Light?
469
+ const isSidebarLight = sidebarBg.toLowerCase() === '#ffffff' || sidebarBg.toLowerCase() === '#f3f4f6' || sidebarBg.toLowerCase() === '#fafafa';
470
+
471
+ const sidebarFg = isSidebarLight ? '#0F172A' : '#FFFFFF'; // Dark text for light BG, White text for dark BG
472
+
473
+ root.style.setProperty('--sidebar', sidebarBg);
474
+ root.style.setProperty('--sidebar-foreground', sidebarFg);
475
+
476
+ // Sidebar Accent
477
+ if (primaryRGB) {
478
+ if (isSidebarLight) {
479
+ // Standard behavior for light sidebar
480
+ root.style.setProperty('--sidebar-accent', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.1)`);
481
+ root.style.setProperty('--sidebar-accent-foreground', primary);
482
+ root.style.setProperty('--sidebar-border', 'rgba(0,0,0,0.1)');
483
+ } else {
484
+ // Behavior for colored/dark sidebar
485
+ // Accent should be a slightly lighter/darker shade of the sidebar bg
486
+ // Or white with low opacity
487
+ root.style.setProperty('--sidebar-accent', 'rgba(255, 255, 255, 0.1)');
488
+ root.style.setProperty('--sidebar-accent-foreground', '#FFFFFF');
489
+ root.style.setProperty('--sidebar-border', 'rgba(255,255,255,0.1)');
490
+ }
491
+ root.style.setProperty('--sidebar-ring', `rgba(${primaryRGB.r}, ${primaryRGB.g}, ${primaryRGB.b}, 0.5)`);
492
+ }
493
+
494
+ // Sidebar Primary (Active Item)
495
+ // If sidebar is colored (dark), the primary active item usually looks good as white or a very bright accent.
496
+ // But standard "Primary" might clash if it's the same color as the background.
497
+ if (!isSidebarLight) {
498
+ root.style.setProperty('--sidebar-primary', '#FFFFFF');
499
+ root.style.setProperty('--sidebar-primary-foreground', primary); // Text becomes primary color
500
+ } else {
501
+ root.style.setProperty('--sidebar-primary', primary);
502
+ root.style.setProperty('--sidebar-primary-foreground', primaryForeground);
503
+ }
504
+
505
+ // 6. Gradients
506
+ root.style.setProperty(
507
+ '--gradient-diagonal',
508
+ `linear-gradient(135deg, ${gradientStart} 0%, ${gradientEnd} 100%)`
509
+ );
510
+
511
+ // Save preferences
512
+ localStorage.setItem('color-theme', currentTheme);
513
+ }, [colors, currentTheme, radius]);
514
+
515
+ // Aplicar cores quando elas mudarem
516
+ useEffect(() => {
517
+ applyColors();
518
+ }, [applyColors]);
519
+
520
+ // Observar mudanças na classe 'dark' do documento
521
+ useEffect(() => {
522
+ const root = document.documentElement;
523
+ const observer = new MutationObserver((mutations) => {
524
+ mutations.forEach((mutation) => {
525
+ if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
526
+ applyColors();
527
+ }
528
+ });
529
+ });
530
+ observer.observe(root, { attributes: true, attributeFilter: ['class'] });
531
+ return () => observer.disconnect();
532
+ }, [applyColors]);
533
+
534
+ const setTheme = (themeId: string) => {
535
+ setCurrentTheme(themeId);
536
+ };
537
+
538
+ return (
539
+ <BrandColorsContext.Provider value={{ colors, currentTheme, themes: colorThemes, setTheme, radius, setRadius }}>
540
+ {children}
541
+ </BrandColorsContext.Provider>
542
+ );
543
+ };
544
+
545
+ export const useBrandColors = () => {
546
+ const context = useContext(BrandColorsContext);
547
+ if (context === undefined) {
548
+ throw new Error('useBrandColors must be used within a BrandColorsProvider');
549
+ }
550
+ return context;
551
+ };
@@ -0,0 +1,36 @@
1
+ import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
2
+
3
+ export type Language = 'pt' | 'en' | 'es';
4
+
5
+ interface LanguageContextType {
6
+ language: Language;
7
+ setLanguage: (language: Language) => void;
8
+ }
9
+
10
+ const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
11
+
12
+ // Provider simplificado - mantém seletor visual mas funciona apenas em português
13
+ export function LanguageProvider({ children }: { children: ReactNode }) {
14
+ const [language, setLanguage] = useState<Language>(() => {
15
+ const saved = localStorage.getItem('xertica_language');
16
+ return (saved as Language) || 'pt';
17
+ });
18
+
19
+ useEffect(() => {
20
+ localStorage.setItem('xertica_language', language);
21
+ }, [language]);
22
+
23
+ return (
24
+ <LanguageContext.Provider value={{ language, setLanguage }}>
25
+ {children}
26
+ </LanguageContext.Provider>
27
+ );
28
+ }
29
+
30
+ export function useLanguage() {
31
+ const context = useContext(LanguageContext);
32
+ if (context === undefined) {
33
+ throw new Error('useLanguage must be used within a LanguageProvider');
34
+ }
35
+ return context;
36
+ }
@@ -0,0 +1,85 @@
1
+ import React, { createContext, useContext, useEffect, useState } from 'react';
2
+
3
+ export type Theme = 'light' | 'dark';
4
+
5
+ interface ThemeContextType {
6
+ theme: Theme;
7
+ toggleTheme: () => void;
8
+ setThemeMode: (theme: Theme) => void;
9
+ isDark: boolean;
10
+ }
11
+
12
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
13
+
14
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
15
+ // Inicialização mais direta - verificar localStorage imediatamente
16
+ const getInitialTheme = (): Theme => {
17
+ // Primeiro, garantir que não há classe dark aplicada por padrão
18
+ if (typeof window !== 'undefined') {
19
+ document.documentElement.classList.remove('dark');
20
+
21
+ const savedTheme = localStorage.getItem('xertica-theme') as Theme;
22
+ if (savedTheme && (savedTheme === 'light' || savedTheme === 'dark')) {
23
+ return savedTheme;
24
+ }
25
+ }
26
+ // Sempre retornar 'light' como padrão
27
+ return 'light';
28
+ };
29
+
30
+ const [theme, setTheme] = useState<Theme>(getInitialTheme);
31
+
32
+ // Aplicar tema ao documento sempre que o tema mudar
33
+ useEffect(() => {
34
+ const root = document.documentElement;
35
+
36
+ if (theme === 'dark') {
37
+ root.classList.add('dark');
38
+ } else {
39
+ root.classList.remove('dark');
40
+ }
41
+
42
+ // Salvar no localStorage
43
+ localStorage.setItem('xertica-theme', theme);
44
+ }, [theme]);
45
+
46
+ // Garantir que o tema seja aplicado na inicialização
47
+ useEffect(() => {
48
+ const root = document.documentElement;
49
+ if (theme === 'light') {
50
+ root.classList.remove('dark');
51
+ }
52
+ localStorage.setItem('xertica-theme', theme);
53
+ }, []);
54
+
55
+ // Alternar tema
56
+ const toggleTheme = () => {
57
+ setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
58
+ };
59
+
60
+ // Definir tema específico
61
+ const setThemeMode = (newTheme: Theme) => {
62
+ setTheme(newTheme);
63
+ };
64
+
65
+ const value: ThemeContextType = {
66
+ theme,
67
+ toggleTheme,
68
+ setThemeMode,
69
+ isDark: theme === 'dark'
70
+ };
71
+
72
+ return (
73
+ <ThemeContext.Provider value={value}>
74
+ {children}
75
+ </ThemeContext.Provider>
76
+ );
77
+ }
78
+
79
+ export function useTheme() {
80
+ const context = useContext(ThemeContext);
81
+ if (context === undefined) {
82
+ throw new Error('useTheme must be used within a ThemeProvider');
83
+ }
84
+ return context;
85
+ }