xertica-ui 2.1.2 → 2.1.4

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 (181) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +1 -1
  3. package/bin/cli.ts +1 -1
  4. package/bin/generate-tokens.ts +13 -7
  5. package/components/assistant/xertica-assistant/index.ts +2 -0
  6. package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +97 -0
  7. package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -0
  8. package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -0
  9. package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +86 -0
  10. package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +77 -0
  11. package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +573 -0
  12. package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +65 -0
  13. package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -0
  14. package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +98 -0
  15. package/components/assistant/xertica-assistant/parts/index.ts +16 -0
  16. package/components/assistant/xertica-assistant/types.ts +139 -0
  17. package/components/assistant/xertica-assistant/use-assistant.ts +559 -0
  18. package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
  19. package/components/assistant/xertica-assistant/xertica-assistant.tsx +198 -1460
  20. package/components/brand/theme-toggle/ThemeToggle.tsx +8 -27
  21. package/components/hooks/index.ts +3 -0
  22. package/components/hooks/use-layout-shortcuts.ts +46 -0
  23. package/components/layout/sidebar/index.ts +2 -0
  24. package/components/layout/sidebar/sidebar.stories.tsx +160 -8
  25. package/components/layout/sidebar/sidebar.tsx +606 -497
  26. package/components/layout/sidebar/use-sidebar.ts +104 -0
  27. package/components/media/audio-player/AudioPlayer.tsx +131 -206
  28. package/components/media/audio-player/use-audio-player.ts +298 -0
  29. package/components/pages/home-page/HomePage.tsx +1 -1
  30. package/components/pages/template-content/TemplateContent.tsx +5 -5
  31. package/components/pages/template-page/TemplatePage.tsx +5 -5
  32. package/components/shared/CustomTooltipContent.tsx +52 -0
  33. package/components/shared/layout-constants.ts +1 -1
  34. package/components/ui/chart/chart.stories.tsx +966 -7
  35. package/components/ui/chart/chart.tsx +918 -45
  36. package/components/ui/file-upload/file-upload.stories.tsx +100 -0
  37. package/components/ui/file-upload/file-upload.tsx +14 -74
  38. package/components/ui/file-upload/index.ts +1 -0
  39. package/components/ui/file-upload/use-file-upload.ts +181 -0
  40. package/components/ui/pagination/index.ts +2 -0
  41. package/components/ui/pagination/pagination.stories.tsx +94 -0
  42. package/components/ui/pagination/use-pagination.ts +194 -0
  43. package/components/ui/rich-text-editor/index.ts +2 -0
  44. package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
  45. package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
  46. package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
  47. package/components/ui/stepper/index.ts +3 -1
  48. package/components/ui/stepper/stepper.stories.tsx +116 -0
  49. package/components/ui/stepper/stepper.tsx +4 -4
  50. package/components/ui/stepper/use-stepper.ts +137 -0
  51. package/components/ui/tree-view/index.ts +4 -1
  52. package/components/ui/tree-view/tree-view.stories.tsx +110 -4
  53. package/components/ui/tree-view/tree-view.tsx +17 -125
  54. package/components/ui/tree-view/use-tree-view.ts +229 -0
  55. package/contexts/AssistenteContext.tsx +17 -54
  56. package/contexts/BrandColorsContext.tsx +6 -17
  57. package/contexts/LayoutContext.tsx +5 -31
  58. package/dist/AssistantChart-BAudAfne.cjs +3591 -0
  59. package/dist/AssistantChart-BP8upjMk.js +3565 -0
  60. package/dist/AudioPlayer-1ypwE2Wh.cjs +936 -0
  61. package/dist/AudioPlayer-DuKXrCfy.js +937 -0
  62. package/dist/CustomTooltipContent-DHjkY0ww.js +40 -0
  63. package/dist/CustomTooltipContent-c_K-DWRr.cjs +56 -0
  64. package/dist/LanguageContext-BwhwC3G2.js +657 -0
  65. package/dist/LanguageContext-DvUt5jBg.cjs +656 -0
  66. package/dist/LayoutContext-BDmcZfMH.cjs +84 -0
  67. package/dist/LayoutContext-dbQvdC4O.js +85 -0
  68. package/dist/ThemeContext-RTy1m2Uq.js +82 -0
  69. package/dist/ThemeContext-bSzuOit2.cjs +81 -0
  70. package/dist/VerifyEmailPage-C_ihbcth.js +2828 -0
  71. package/dist/VerifyEmailPage-Dt7zgA4w.cjs +2827 -0
  72. package/dist/XerticaProvider-CW9hpCdF.cjs +39 -0
  73. package/dist/XerticaProvider-siSt9uG2.js +40 -0
  74. package/dist/XerticaXLogo-D8jf0SNv.cjs +214 -0
  75. package/dist/XerticaXLogo-fAJMy3H4.js +215 -0
  76. package/dist/assistant.cjs.js +2 -1
  77. package/dist/assistant.es.js +3 -2
  78. package/dist/brand.cjs.js +2 -2
  79. package/dist/brand.es.js +2 -2
  80. package/dist/cli.js +14 -8
  81. package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
  82. package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +13 -0
  83. package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +16 -0
  84. package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +17 -0
  85. package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +19 -0
  86. package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +11 -0
  87. package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +29 -0
  88. package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +13 -0
  89. package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +4 -0
  90. package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +17 -0
  91. package/dist/components/assistant/xertica-assistant/parts/index.d.ts +16 -0
  92. package/dist/components/assistant/xertica-assistant/types.d.ts +106 -0
  93. package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +125 -0
  94. package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +8 -97
  95. package/dist/components/hooks/index.d.ts +3 -0
  96. package/dist/components/hooks/use-layout-shortcuts.d.ts +22 -0
  97. package/dist/components/layout/sidebar/index.d.ts +2 -0
  98. package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
  99. package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
  100. package/dist/components/media/audio-player/AudioPlayer.d.ts +4 -1
  101. package/dist/components/media/audio-player/use-audio-player.d.ts +72 -0
  102. package/dist/components/shared/CustomTooltipContent.d.ts +20 -0
  103. package/dist/components/shared/layout-constants.d.ts +1 -1
  104. package/dist/components/ui/alert/alert.d.ts +1 -1
  105. package/dist/components/ui/badge/badge.d.ts +1 -1
  106. package/dist/components/ui/button/button.d.ts +2 -2
  107. package/dist/components/ui/chart/chart.d.ts +162 -5
  108. package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
  109. package/dist/components/ui/file-upload/index.d.ts +1 -0
  110. package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
  111. package/dist/components/ui/pagination/index.d.ts +2 -0
  112. package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
  113. package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
  114. package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
  115. package/dist/components/ui/stepper/index.d.ts +3 -1
  116. package/dist/components/ui/stepper/stepper.d.ts +2 -2
  117. package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
  118. package/dist/components/ui/tree-view/index.d.ts +4 -1
  119. package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
  120. package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
  121. package/dist/contexts/AssistenteContext.d.ts +10 -49
  122. package/dist/hooks.cjs.js +30 -10
  123. package/dist/hooks.es.js +25 -4
  124. package/dist/index.cjs.js +20 -9
  125. package/dist/index.es.js +38 -27
  126. package/dist/layout.cjs.js +82 -1
  127. package/dist/layout.es.js +83 -2
  128. package/dist/media.cjs.js +1 -1
  129. package/dist/media.es.js +1 -1
  130. package/dist/pages.cjs.js +1 -1
  131. package/dist/pages.es.js +1 -1
  132. package/dist/rich-text-editor-BmsjY03B.js +2949 -0
  133. package/dist/rich-text-editor-GS2kpTAK.cjs +2966 -0
  134. package/dist/sidebar-CVUGHOS_.cjs +756 -0
  135. package/dist/sidebar-CmvwjnVb.js +757 -0
  136. package/dist/ui.cjs.js +12 -2
  137. package/dist/ui.es.js +24 -14
  138. package/dist/use-audio-player-Bkh23vQ3.js +177 -0
  139. package/dist/use-audio-player-Dn1NR9xN.cjs +176 -0
  140. package/dist/utils/color-utils.d.ts +51 -0
  141. package/dist/xertica-assistant-BMqdyRVi.js +2082 -0
  142. package/dist/xertica-assistant-Bj3vBCq_.cjs +2081 -0
  143. package/dist/xertica-ui.css +1 -1
  144. package/docs/ai-usage.md +28 -10
  145. package/docs/architecture-improvements.md +463 -0
  146. package/docs/architecture.md +77 -1
  147. package/docs/components/assistant-chart.md +1 -1
  148. package/docs/components/assistant.md +159 -0
  149. package/docs/components/audio-player.md +46 -0
  150. package/docs/components/branding.md +251 -0
  151. package/docs/components/chart.md +354 -39
  152. package/docs/components/code-block.md +108 -0
  153. package/docs/components/file-upload.md +119 -2
  154. package/docs/components/formatted-document.md +113 -0
  155. package/docs/components/hooks.md +430 -0
  156. package/docs/components/image-with-fallback.md +106 -0
  157. package/docs/components/map-layers.md +140 -0
  158. package/docs/components/modern-chat-input.md +163 -0
  159. package/docs/components/pages.md +351 -0
  160. package/docs/components/pagination.md +187 -0
  161. package/docs/components/rich-text-editor.md +164 -0
  162. package/docs/components/sidebar.md +153 -4
  163. package/docs/components/stepper.md +157 -12
  164. package/docs/components/tree-view.md +164 -6
  165. package/docs/doc-audit.md +223 -0
  166. package/docs/getting-started.md +155 -1
  167. package/docs/guidelines.md +14 -8
  168. package/docs/layout.md +2 -2
  169. package/docs/llms.md +29 -9
  170. package/docs/patterns/detail-page.md +276 -0
  171. package/docs/patterns/settings.md +346 -0
  172. package/docs/patterns/wizard.md +217 -0
  173. package/guidelines/Guidelines.md +5 -3
  174. package/llms.txt +1 -1
  175. package/package.json +10 -10
  176. package/styles/xertica/tokens.css +41 -12
  177. package/templates/CLAUDE.md +16 -6
  178. package/templates/guidelines/Guidelines.md +16 -4
  179. package/templates/package.json +3 -3
  180. package/templates/src/styles/xertica/tokens.css +39 -10
  181. package/utils/color-utils.ts +72 -0
@@ -0,0 +1,229 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+
3
+ // ─────────────────────────────────────────────────────────────────────────────
4
+ // Types
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+
7
+ export interface TreeNode {
8
+ id: string;
9
+ label: string;
10
+ icon?: React.ReactNode;
11
+ children?: TreeNode[];
12
+ }
13
+
14
+ export interface UseTreeViewProps {
15
+ /** The tree data to manage. */
16
+ data: TreeNode[];
17
+ /** Node IDs that are expanded by default (uncontrolled). */
18
+ defaultExpanded?: string[];
19
+ /**
20
+ * Controlled selected node ID.
21
+ * When provided, selection state is managed externally.
22
+ */
23
+ selectedNodeId?: string;
24
+ /** Called when a node is clicked or activated via keyboard. */
25
+ onNodeClick?: (node: TreeNode) => void;
26
+ /** Called when a node becomes the selected item. */
27
+ onNodeSelect?: (node: TreeNode) => void;
28
+ }
29
+
30
+ export interface UseTreeViewReturn {
31
+ // ── State ─────────────────────────────────────────────────────────────────
32
+ /** Set of currently expanded node IDs. */
33
+ expanded: Set<string>;
34
+ /** The currently selected node ID (controlled or internal). */
35
+ effectiveSelectedId: string | undefined;
36
+
37
+ // ── Refs ──────────────────────────────────────────────────────────────────
38
+ /**
39
+ * Map of node ID → button element.
40
+ * Attach via the `setRef` helper returned by `getNodeRef`.
41
+ */
42
+ nodeRefs: React.MutableRefObject<Map<string, HTMLButtonElement>>;
43
+
44
+ // ── Helpers ───────────────────────────────────────────────────────────────
45
+ /** Returns a ref-setter callback for a given node ID. */
46
+ getNodeRef: (nodeId: string) => (el: HTMLButtonElement | null) => void;
47
+
48
+ // ── Handlers ──────────────────────────────────────────────────────────────
49
+ /** Toggle the expanded state of a node. */
50
+ toggleExpand: (nodeId: string) => void;
51
+ /** Select a node (updates internal state if uncontrolled, fires callbacks). */
52
+ handleSelect: (node: TreeNode) => void;
53
+ /**
54
+ * Full WAI-ARIA keyboard handler.
55
+ * Attach to `onKeyDown` of each tree-item button.
56
+ */
57
+ handleKeyDown: (e: React.KeyboardEvent, node: TreeNode) => void;
58
+
59
+ // ── Computed ──────────────────────────────────────────────────────────────
60
+ /** Returns the ordered list of all currently visible nodes (respects expand state). */
61
+ getVisibleNodes: () => TreeNode[];
62
+ }
63
+
64
+ // ─────────────────────────────────────────────────────────────────────────────
65
+ // Hook
66
+ // ─────────────────────────────────────────────────────────────────────────────
67
+
68
+ /**
69
+ * Headless hook for hierarchical tree-view logic.
70
+ *
71
+ * @description
72
+ * Manages expand/collapse state, selection (controlled or uncontrolled),
73
+ * WAI-ARIA keyboard navigation (Arrow keys, Home, End, Enter/Space),
74
+ * and focus management via node refs. Pair with any custom tree UI.
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * const { expanded, effectiveSelectedId, toggleExpand, handleSelect, handleKeyDown, getNodeRef } =
79
+ * useTreeView({ data, onNodeSelect });
80
+ * ```
81
+ */
82
+ export function useTreeView({
83
+ data,
84
+ defaultExpanded = [],
85
+ selectedNodeId,
86
+ onNodeClick,
87
+ onNodeSelect,
88
+ }: UseTreeViewProps): UseTreeViewReturn {
89
+ const [expanded, setExpanded] = useState<Set<string>>(new Set(defaultExpanded));
90
+ const [internalSelectedId, setInternalSelectedId] = useState<string | undefined>(undefined);
91
+ const nodeRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
92
+
93
+ const isControlled = selectedNodeId !== undefined;
94
+ const effectiveSelectedId = isControlled ? selectedNodeId : internalSelectedId;
95
+
96
+ // ── Helpers ───────────────────────────────────────────────────────────────
97
+
98
+ const getNodeRef = useCallback(
99
+ (nodeId: string) => (el: HTMLButtonElement | null) => {
100
+ if (el) nodeRefs.current.set(nodeId, el);
101
+ else nodeRefs.current.delete(nodeId);
102
+ },
103
+ []
104
+ );
105
+
106
+ const getVisibleNodes = useCallback((): TreeNode[] => {
107
+ const result: TreeNode[] = [];
108
+ const traverse = (nodes: TreeNode[]) => {
109
+ for (const node of nodes) {
110
+ result.push(node);
111
+ if (node.children?.length && expanded.has(node.id)) traverse(node.children);
112
+ }
113
+ };
114
+ traverse(data);
115
+ return result;
116
+ }, [data, expanded]);
117
+
118
+ const findParent = useCallback(
119
+ (nodes: TreeNode[], targetId: string): TreeNode | null => {
120
+ for (const n of nodes) {
121
+ if (n.children?.some(c => c.id === targetId)) return n;
122
+ if (n.children) {
123
+ const found = findParent(n.children, targetId);
124
+ if (found) return found;
125
+ }
126
+ }
127
+ return null;
128
+ },
129
+ // findParent is a pure recursive function over `data` — no reactive deps needed
130
+ // eslint-disable-next-line react-hooks/exhaustive-deps
131
+ []
132
+ );
133
+
134
+ // ── Handlers ──────────────────────────────────────────────────────────────
135
+
136
+ const toggleExpand = useCallback((nodeId: string) => {
137
+ setExpanded(prev => {
138
+ const next = new Set(prev);
139
+ next.has(nodeId) ? next.delete(nodeId) : next.add(nodeId);
140
+ return next;
141
+ });
142
+ }, []);
143
+
144
+ const handleSelect = useCallback(
145
+ (node: TreeNode) => {
146
+ if (!isControlled) setInternalSelectedId(node.id);
147
+ onNodeSelect?.(node);
148
+ onNodeClick?.(node);
149
+ },
150
+ [isControlled, onNodeClick, onNodeSelect]
151
+ );
152
+
153
+ const handleKeyDown = useCallback(
154
+ (e: React.KeyboardEvent, node: TreeNode) => {
155
+ const visibleNodes = getVisibleNodes();
156
+ const idx = visibleNodes.findIndex(n => n.id === node.id);
157
+ const hasChildren = !!(node.children?.length);
158
+ const isExpanded = expanded.has(node.id);
159
+
160
+ const focusNode = (id: string) => {
161
+ nodeRefs.current.get(id)?.focus();
162
+ };
163
+
164
+ switch (e.key) {
165
+ case 'ArrowDown': {
166
+ e.preventDefault();
167
+ const next = visibleNodes[idx + 1];
168
+ if (next) focusNode(next.id);
169
+ break;
170
+ }
171
+ case 'ArrowUp': {
172
+ e.preventDefault();
173
+ const prev = visibleNodes[idx - 1];
174
+ if (prev) focusNode(prev.id);
175
+ break;
176
+ }
177
+ case 'ArrowRight': {
178
+ e.preventDefault();
179
+ if (hasChildren && !isExpanded) {
180
+ toggleExpand(node.id);
181
+ } else if (hasChildren && isExpanded && node.children?.length) {
182
+ focusNode(node.children[0].id);
183
+ }
184
+ break;
185
+ }
186
+ case 'ArrowLeft': {
187
+ e.preventDefault();
188
+ if (hasChildren && isExpanded) {
189
+ toggleExpand(node.id);
190
+ } else {
191
+ const parent = findParent(data, node.id);
192
+ if (parent) focusNode(parent.id);
193
+ }
194
+ break;
195
+ }
196
+ case 'Home': {
197
+ e.preventDefault();
198
+ if (visibleNodes.length > 0) focusNode(visibleNodes[0].id);
199
+ break;
200
+ }
201
+ case 'End': {
202
+ e.preventDefault();
203
+ const last = visibleNodes[visibleNodes.length - 1];
204
+ if (last) focusNode(last.id);
205
+ break;
206
+ }
207
+ case 'Enter':
208
+ case ' ': {
209
+ e.preventDefault();
210
+ if (hasChildren) toggleExpand(node.id);
211
+ handleSelect(node);
212
+ break;
213
+ }
214
+ }
215
+ },
216
+ [data, expanded, findParent, getVisibleNodes, handleSelect, toggleExpand]
217
+ );
218
+
219
+ return {
220
+ expanded,
221
+ effectiveSelectedId,
222
+ nodeRefs,
223
+ getNodeRef,
224
+ toggleExpand,
225
+ handleSelect,
226
+ handleKeyDown,
227
+ getVisibleNodes,
228
+ };
229
+ }
@@ -1,44 +1,29 @@
1
1
  import React, { createContext, useContext, useState, ReactNode } from 'react';
2
+ import type {
3
+ Message,
4
+ SearchResult,
5
+ SearchSource,
6
+ SearchCommand,
7
+ MockResponse,
8
+ } from '../components/assistant/xertica-assistant/types';
2
9
 
10
+ // Re-export shared types for backward compatibility
11
+ export type { Message, SearchResult, SearchSource, SearchCommand, MockResponse };
3
12
 
4
-
13
+ /**
14
+ * @deprecated Use `Suggestion` from `xertica-assistant/types` instead.
15
+ * Kept for backward compatibility with existing consumers.
16
+ */
5
17
  export interface Sugestao {
6
18
  id: string;
7
19
  texto: string;
8
20
  icon?: ReactNode;
9
21
  }
10
22
 
11
- export interface Message {
12
- id: string;
13
- type: 'user' | 'assistant' | 'system';
14
- content: string;
15
- timestamp: Date;
16
- isFavorite?: boolean;
17
- chartData?: any[];
18
- chartConfig?: any;
19
- attachmentType?: 'image' | 'video' | 'audio' | 'file' | 'document' | 'podcast' | 'search';
20
- attachmentName?: string;
21
- documentContent?: string;
22
- documentTitle?: string;
23
- tableData?: {
24
- caption?: string;
25
- headers: string[];
26
- rows: (string | ReactNode)[][];
27
- };
28
- audioUrl?: string;
29
- searchResults?: SearchResult[];
30
- searchSources?: SearchSource[];
31
- searchCommands?: SearchCommand[];
32
- evaluation?: 'like' | 'dislike';
33
- evaluationReason?: string;
34
- }
35
-
36
- export interface MockResponse {
37
- trigger: string | RegExp;
38
- response: string | Partial<Message>;
39
- delay?: number;
40
- }
41
-
23
+ /**
24
+ * A saved conversation in the AssistenteContext (Portuguese naming convention).
25
+ * Note: `Conversation` in `xertica-assistant/types` uses English naming.
26
+ */
42
27
  export interface Conversa {
43
28
  id: string;
44
29
  titulo: string;
@@ -48,28 +33,6 @@ export interface Conversa {
48
33
  favorita?: boolean;
49
34
  }
50
35
 
51
- export interface SearchResult {
52
- id: string;
53
- title: string;
54
- description: string;
55
- type: 'document' | 'file' | 'project' | 'conversation' | 'contact';
56
- path: string;
57
- relevance: number;
58
- lastModified?: string;
59
- }
60
-
61
- export interface SearchSource {
62
- name: string;
63
- count: number;
64
- }
65
-
66
- export interface SearchCommand {
67
- id: string;
68
- icon: string;
69
- label: string;
70
- description: string;
71
- }
72
-
73
36
  interface AssistenteContextType {
74
37
  historico: Message[];
75
38
  conversaAtual: string;
@@ -6,6 +6,7 @@ import {
6
6
  PALETTE,
7
7
  colorThemes,
8
8
  } from './theme-data';
9
+ import { hexToRgb } from '../utils/color-utils';
9
10
 
10
11
  interface BrandColorsContextType {
11
12
  colors: BrandColors;
@@ -26,11 +27,11 @@ interface BrandColorsProviderProps {
26
27
  useCustomTokens?: boolean;
27
28
  }
28
29
 
29
- export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
30
- children,
31
- defaultTheme,
32
- primaryColor,
33
- useCustomTokens = false
30
+ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
31
+ children,
32
+ defaultTheme,
33
+ primaryColor,
34
+ useCustomTokens = false
34
35
  }) => {
35
36
  const [currentTheme, setCurrentTheme] = useState<string>(() => {
36
37
  // Se primaryColor for passado, ignoramos o nome do tema
@@ -75,18 +76,6 @@ export const BrandColorsProvider: React.FC<BrandColorsProviderProps> = ({
75
76
  }
76
77
  }, [currentTheme, primaryColor]);
77
78
 
78
- // Função auxiliar para converter hex para rgb
79
- const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
80
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
81
- return result
82
- ? {
83
- r: parseInt(result[1], 16),
84
- g: parseInt(result[2], 16),
85
- b: parseInt(result[3], 16),
86
- }
87
- : null;
88
- };
89
-
90
79
  // Função para aplicar as cores baseadas no modo atual e tema
91
80
  const applyColors = React.useCallback(() => {
92
81
  if (useCustomTokens) return; // Skip if user wants to use local tokens.css
@@ -1,8 +1,8 @@
1
1
  import React, { createContext, useContext, useState, ReactNode, useCallback, useEffect } from 'react';
2
+ import { useIsMobile } from '../components/shared/use-mobile';
2
3
 
3
4
  const SIDEBAR_COOKIE_NAME = "sidebar_state";
4
5
  const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
5
- const SIDEBAR_KEYBOARD_SHORTCUT = "b";
6
6
 
7
7
  interface LayoutContextType {
8
8
  sidebarExpanded: boolean;
@@ -51,45 +51,19 @@ export function LayoutProvider({ children }: { children: ReactNode }) {
51
51
  const saved = getCookie(SIDEBAR_COOKIE_NAME);
52
52
  return saved === 'true';
53
53
  });
54
-
54
+
55
55
  const [assistenteExpanded, setAssistenteExpandedState] = useState(false);
56
56
  const [sidebarWidth, setSidebarWidth] = useState(256);
57
- const [isMobile, setIsMobile] = useState(false);
58
-
59
- // Detect mobile
60
- useEffect(() => {
61
- if (typeof window === 'undefined') return;
62
- const checkMobile = () => {
63
- setIsMobile(window.innerWidth < 768);
64
- };
65
- checkMobile();
66
- window.addEventListener('resize', checkMobile);
67
- return () => window.removeEventListener('resize', checkMobile);
68
- }, []);
57
+ const isMobile = useIsMobile();
69
58
 
70
59
  // Persist sidebar state
71
60
  useEffect(() => {
72
61
  setCookie(SIDEBAR_COOKIE_NAME, sidebarExpanded.toString());
73
62
  }, [sidebarExpanded]);
74
63
 
75
- // Keyboard shortcuts
76
- useEffect(() => {
77
- if (typeof window === 'undefined') return;
78
- const handleKeyDown = (event: KeyboardEvent) => {
79
- if (
80
- event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
81
- (event.metaKey || event.ctrlKey)
82
- ) {
83
- event.preventDefault();
84
- toggleSidebar();
85
- }
86
- };
87
-
88
- window.addEventListener("keydown", handleKeyDown);
89
- return () => window.removeEventListener("keydown", handleKeyDown);
90
- }, [sidebarExpanded, assistenteExpanded]); // Dependencies to ensure toggleSidebar uses latest state
91
-
92
64
  // Função para alternar a sidebar - fecha o assistente se estiver aberto
65
+ // Nota: atalhos de teclado (Ctrl+B) são registrados via useLayoutShortcuts()
66
+ // no componente raiz da aplicação — ver components/hooks/use-layout-shortcuts.ts
93
67
  const toggleSidebar = useCallback(() => {
94
68
  setSidebarExpandedState(prev => {
95
69
  const newState = !prev;