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,298 @@
1
+ import { useState, useRef, useEffect, useCallback } from 'react';
2
+ import { useIsMobile } from '../../shared/use-mobile';
3
+ import { useOptionalLayout } from '../../../contexts/LayoutContext';
4
+
5
+ export interface UseAudioPlayerProps {
6
+ /** Source URL of the audio file */
7
+ src?: string;
8
+ /** Whether to play automatically on mount */
9
+ autoPlay?: boolean;
10
+ /** Initial playback time in seconds */
11
+ initialTime?: number;
12
+ /** Total duration in seconds (optional — loaded from metadata if not provided) */
13
+ initialDuration?: number;
14
+ /** Display variant: 'card' (standard floating) or 'bar' (global fixed bar) */
15
+ variant?: 'card' | 'bar';
16
+ /** Whether the player is currently visible and open (only for 'bar' variant) */
17
+ isOpen?: boolean;
18
+ /** Whether the player should automatically float when scrolled out of view */
19
+ enableAutoFloat?: boolean;
20
+ /** Callback fired when user manually closes the floating player */
21
+ onCloseFloating?: () => void;
22
+ }
23
+
24
+ export interface UseAudioPlayerReturn {
25
+ // ── Refs ──────────────────────────────────────────────────────────────────
26
+ audioRef: React.RefObject<HTMLAudioElement>;
27
+ containerRef: React.RefObject<HTMLDivElement>;
28
+
29
+ // ── Playback state ────────────────────────────────────────────────────────
30
+ isPlaying: boolean;
31
+ currentTime: number;
32
+ duration: number;
33
+ volume: number;
34
+ isMuted: boolean;
35
+ playbackSpeed: number;
36
+
37
+ // ── UI state ──────────────────────────────────────────────────────────────
38
+ isFloating: boolean;
39
+ isManualFloating: boolean;
40
+ isVisible: boolean;
41
+ isMobile: boolean;
42
+ enableAutoFloatLocal: boolean;
43
+
44
+ // ── Layout context (for 'bar' variant positioning) ────────────────────────
45
+ sidebarExpanded: boolean;
46
+ sidebarWidth: number;
47
+ assistenteExpanded: boolean;
48
+
49
+ // ── Handlers ──────────────────────────────────────────────────────────────
50
+ togglePlay: () => void;
51
+ toggleMute: () => void;
52
+ handleSeek: (value: number[]) => void;
53
+ handleVolumeChange: (value: number[]) => void;
54
+ handleSetFloating: (floating: boolean) => void;
55
+ handleEnableManualFloat: () => void;
56
+ setPlaybackSpeed: (speed: number) => void;
57
+ resetAudio: () => void;
58
+
59
+ // ── Audio element event handlers (for use in <audio> JSX) ─────────────────
60
+ onPlay: () => void;
61
+ onPause: () => void;
62
+ onEnded: () => void;
63
+ onTimeUpdate: (e: React.SyntheticEvent<HTMLAudioElement>) => void;
64
+ onLoadedMetadata: (e: React.SyntheticEvent<HTMLAudioElement>) => void;
65
+
66
+ // ── Utilities ─────────────────────────────────────────────────────────────
67
+ formatTime: (time: number) => string;
68
+ }
69
+
70
+ /**
71
+ * `useAudioPlayer` — Headless hook for the AudioPlayer component.
72
+ *
73
+ * Encapsulates all state management and side effects for audio playback,
74
+ * including auto-float on scroll, mobile detection, and layout context integration.
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * import { useAudioPlayer } from './use-audio-player';
79
+ *
80
+ * function MyCustomPlayer({ src }: { src: string }) {
81
+ * const { isPlaying, togglePlay, formatTime, currentTime, duration } = useAudioPlayer({ src });
82
+ * return (
83
+ * <div>
84
+ * <button onClick={togglePlay}>{isPlaying ? 'Pause' : 'Play'}</button>
85
+ * <span>{formatTime(currentTime)} / {formatTime(duration)}</span>
86
+ * </div>
87
+ * );
88
+ * }
89
+ * ```
90
+ */
91
+ export function useAudioPlayer({
92
+ src,
93
+ autoPlay = false,
94
+ initialTime = 0,
95
+ initialDuration,
96
+ variant = 'card',
97
+ isOpen = true,
98
+ enableAutoFloat = true,
99
+ onCloseFloating,
100
+ }: UseAudioPlayerProps = {}): UseAudioPlayerReturn {
101
+ const audioRef = useRef<HTMLAudioElement>(null);
102
+ const containerRef = useRef<HTMLDivElement>(null);
103
+
104
+ // Layout context for 'bar' variant positioning
105
+ const layout = useOptionalLayout();
106
+ const sidebarExpanded = layout?.sidebarExpanded ?? false;
107
+ const sidebarWidth = layout?.sidebarWidth ?? 256;
108
+ const assistenteExpanded = layout?.assistenteExpanded ?? false;
109
+
110
+ // Mobile detection via shared hook (replaces inline window.innerWidth < 768)
111
+ const isMobile = useIsMobile();
112
+
113
+ // ── Playback state ──────────────────────────────────────────────────────────
114
+ const [isPlaying, setIsPlaying] = useState(false);
115
+ const [volume, setVolume] = useState(0.8);
116
+ const [isMuted, setIsMuted] = useState(false);
117
+ const [currentTime, setCurrentTime] = useState(initialTime);
118
+ const [duration, setDuration] = useState(initialDuration || 0);
119
+ const [playbackSpeed, setPlaybackSpeedState] = useState(1);
120
+
121
+ // ── Float state ─────────────────────────────────────────────────────────────
122
+ const [isFloating, setIsFloating] = useState(false);
123
+ const [isManualFloating, setIsManualFloating] = useState(false);
124
+ const [enableAutoFloatLocal, setEnableAutoFloatLocal] = useState(enableAutoFloat);
125
+
126
+ // ── Visibility state ────────────────────────────────────────────────────────
127
+ const [isVisible, setIsVisible] = useState(variant === 'card');
128
+
129
+ // ── Effects ─────────────────────────────────────────────────────────────────
130
+
131
+ // Handle visibility for 'bar' variant
132
+ useEffect(() => {
133
+ if (variant === 'bar') {
134
+ if (isOpen) {
135
+ setIsVisible(true);
136
+ } else {
137
+ const timer = setTimeout(() => setIsVisible(false), 300);
138
+ return () => clearTimeout(timer);
139
+ }
140
+ } else {
141
+ setIsVisible(true);
142
+ }
143
+ }, [isOpen, variant]);
144
+
145
+ // Sync audio ref with state updates (volume, mute, speed)
146
+ useEffect(() => {
147
+ const audio = audioRef.current;
148
+ if (!audio) return;
149
+ audio.volume = volume;
150
+ audio.muted = isMuted;
151
+ audio.playbackRate = playbackSpeed;
152
+ }, [volume, isMuted, playbackSpeed, src]);
153
+
154
+ // Auto-float on scroll logic (only for 'card' variant)
155
+ useEffect(() => {
156
+ if (variant !== 'card' || !enableAutoFloatLocal) return;
157
+ const container = containerRef.current;
158
+ if (!container) return;
159
+ if (typeof IntersectionObserver === 'undefined') return;
160
+
161
+ const observer = new IntersectionObserver(
162
+ ([entry]) => {
163
+ if (isPlaying && !entry.isIntersecting && !isFloating) {
164
+ if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
165
+ setIsFloating(true);
166
+ } else if (entry.isIntersecting && isFloating && !isManualFloating) {
167
+ if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
168
+ setIsFloating(false);
169
+ setIsManualFloating(false);
170
+ }
171
+ },
172
+ { threshold: 0.2 }
173
+ );
174
+
175
+ observer.observe(container);
176
+ return () => observer.disconnect();
177
+ }, [isPlaying, isFloating, isManualFloating, variant, enableAutoFloatLocal]);
178
+
179
+ // Restore state when switching modes (remounting audio element)
180
+ useEffect(() => {
181
+ const audio = audioRef.current;
182
+ if (audio) {
183
+ if (Math.abs(audio.currentTime - currentTime) > 0.5) {
184
+ audio.currentTime = currentTime;
185
+ }
186
+ if (isPlaying) {
187
+ audio.play().catch(err => console.log('Playback interrupted:', err));
188
+ }
189
+ }
190
+ // eslint-disable-next-line react-hooks/exhaustive-deps
191
+ }, [isFloating, variant]);
192
+
193
+ // ── Handlers ────────────────────────────────────────────────────────────────
194
+
195
+ const handleSetFloating = useCallback((floating: boolean) => {
196
+ if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
197
+ setIsFloating(floating);
198
+ if (!floating) {
199
+ setIsManualFloating(false);
200
+ if (onCloseFloating) onCloseFloating();
201
+ }
202
+ }, [onCloseFloating]);
203
+
204
+ const togglePlay = useCallback(() => {
205
+ if (audioRef.current) {
206
+ if (isPlaying) {
207
+ audioRef.current.pause();
208
+ } else {
209
+ audioRef.current.play().catch(err => console.log('Playback error:', err));
210
+ }
211
+ }
212
+ }, [isPlaying]);
213
+
214
+ const toggleMute = useCallback(() => {
215
+ setIsMuted(prev => !prev);
216
+ }, []);
217
+
218
+ const handleSeek = useCallback((value: number[]) => {
219
+ if (audioRef.current) {
220
+ audioRef.current.currentTime = value[0];
221
+ setCurrentTime(value[0]);
222
+ }
223
+ }, []);
224
+
225
+ const handleVolumeChange = useCallback((value: number[]) => {
226
+ const newVolume = value[0];
227
+ setVolume(newVolume);
228
+ setIsMuted(newVolume === 0);
229
+ }, []);
230
+
231
+ const handleEnableManualFloat = useCallback(() => {
232
+ setEnableAutoFloatLocal(true);
233
+ setIsManualFloating(true);
234
+ if (audioRef.current) setCurrentTime(audioRef.current.currentTime);
235
+ setIsFloating(true);
236
+ }, []);
237
+
238
+ const setPlaybackSpeed = useCallback((speed: number) => {
239
+ setPlaybackSpeedState(speed);
240
+ }, []);
241
+
242
+ const resetAudio = useCallback(() => {
243
+ if (audioRef.current) {
244
+ audioRef.current.currentTime = 0;
245
+ }
246
+ }, []);
247
+
248
+ const formatTime = useCallback((time: number): string => {
249
+ if (isNaN(time) || time === Infinity || !isFinite(time)) return '0:00';
250
+ const minutes = Math.floor(time / 60);
251
+ const seconds = Math.floor(time % 60);
252
+ return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
253
+ }, []);
254
+
255
+ // ── Audio element event handlers ─────────────────────────────────────────────
256
+ const onPlay = useCallback(() => setIsPlaying(true), []);
257
+ const onPause = useCallback(() => setIsPlaying(false), []);
258
+ const onEnded = useCallback(() => setIsPlaying(false), []);
259
+ const onTimeUpdate = useCallback((e: React.SyntheticEvent<HTMLAudioElement>) => {
260
+ setCurrentTime(e.currentTarget.currentTime);
261
+ }, []);
262
+ const onLoadedMetadata = useCallback((e: React.SyntheticEvent<HTMLAudioElement>) => {
263
+ setDuration(e.currentTarget.duration);
264
+ }, []);
265
+
266
+ return {
267
+ audioRef,
268
+ containerRef,
269
+ isPlaying,
270
+ currentTime,
271
+ duration,
272
+ volume,
273
+ isMuted,
274
+ playbackSpeed,
275
+ isFloating,
276
+ isManualFloating,
277
+ isVisible,
278
+ isMobile,
279
+ enableAutoFloatLocal,
280
+ sidebarExpanded,
281
+ sidebarWidth,
282
+ assistenteExpanded,
283
+ togglePlay,
284
+ toggleMute,
285
+ handleSeek,
286
+ handleVolumeChange,
287
+ handleSetFloating,
288
+ handleEnableManualFloat,
289
+ setPlaybackSpeed,
290
+ resetAudio,
291
+ onPlay,
292
+ onPause,
293
+ onEnded,
294
+ onTimeUpdate,
295
+ onLoadedMetadata,
296
+ formatTime,
297
+ };
298
+ }
@@ -48,7 +48,7 @@ export function HomePage({ user, onLogout }: HomePageProps) {
48
48
  const [localSidebarExpanded, setLocalSidebarExpanded] = React.useState(false);
49
49
  const [localAssistantExpanded, setLocalAssistantExpanded] = React.useState(false);
50
50
  const sidebarExpanded = layout?.sidebarExpanded ?? localSidebarExpanded;
51
- const sidebarWidth = layout?.sidebarWidth ?? 256;
51
+ const sidebarWidth = layout?.sidebarWidth ?? 280;
52
52
  const assistenteExpanded = layout?.assistenteExpanded ?? localAssistantExpanded;
53
53
  const toggleSidebar = layout?.toggleSidebar ?? (() => setLocalSidebarExpanded((value) => !value));
54
54
  const toggleAssistente = layout?.toggleAssistente ?? (() => setLocalAssistantExpanded((value) => !value));
@@ -72,7 +72,7 @@ interface TemplateContentProps {
72
72
  export function TemplateContent({ user, onLogout, onSettings }: TemplateContentProps) {
73
73
  const layout = useOptionalLayout();
74
74
  const [localSidebarExpanded, setLocalSidebarExpanded] = useState(false);
75
- const [localSidebarWidth, setLocalSidebarWidth] = useState(256);
75
+ const [localSidebarWidth, setLocalSidebarWidth] = useState(280);
76
76
  const sidebarExpanded = layout?.sidebarExpanded ?? localSidebarExpanded;
77
77
  const sidebarWidth = layout?.sidebarWidth ?? localSidebarWidth;
78
78
  const setSidebarWidth = layout?.setSidebarWidth ?? setLocalSidebarWidth;
@@ -118,7 +118,7 @@ export function TemplateContent({ user, onLogout, onSettings }: TemplateContentP
118
118
  <div className="p-2 sm:p-4 md:p-6">
119
119
  <div className="max-w-6xl mx-auto space-y-8">
120
120
  {/* Page header */}
121
- <PageHeader
121
+ <PageHeader
122
122
  title="Página de Template"
123
123
  subtitle="Template completo com componentes do Design System"
124
124
  />
@@ -413,10 +413,10 @@ export function TemplateContent({ user, onLogout, onSettings }: TemplateContentP
413
413
  <div className="mb-4">
414
414
  <div className="relative">
415
415
  <Search className="absolute left-3 top-3 h-4 w-4 text-muted-foreground" />
416
- <Input
417
- placeholder="Buscar usuários..."
416
+ <Input
417
+ placeholder="Buscar usuários..."
418
418
  aria-label="Buscar usuários"
419
- className="pl-10"
419
+ className="pl-10"
420
420
  />
421
421
  </div>
422
422
  </div>
@@ -32,7 +32,7 @@ export function TemplatePage({ user, onLogout }: TemplatePageProps) {
32
32
  const [localSidebarExpanded, setLocalSidebarExpanded] = React.useState(false);
33
33
  const [localAssistantExpanded, setLocalAssistantExpanded] = React.useState(false);
34
34
  const sidebarExpanded = layout?.sidebarExpanded ?? localSidebarExpanded;
35
- const sidebarWidth = layout?.sidebarWidth ?? 256;
35
+ const sidebarWidth = layout?.sidebarWidth ?? 280;
36
36
  const assistenteExpanded = layout?.assistenteExpanded ?? localAssistantExpanded;
37
37
  const toggleSidebar = layout?.toggleSidebar ?? (() => setLocalSidebarExpanded((value) => !value));
38
38
  const toggleAssistente = layout?.toggleAssistente ?? (() => setLocalAssistantExpanded((value) => !value));
@@ -56,10 +56,10 @@ export function TemplatePage({ user, onLogout }: TemplatePageProps) {
56
56
  navigate={navigate}
57
57
  routes={routes}
58
58
  />
59
- <TemplateContent
60
- user={user}
61
- onLogout={onLogout}
62
- onSettings={() => navigate('/settings')}
59
+ <TemplateContent
60
+ user={user}
61
+ onLogout={onLogout}
62
+ onSettings={() => navigate('/settings')}
63
63
  />
64
64
  <XerticaAssistant
65
65
  isExpanded={assistenteExpanded}
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
3
+ import { cn } from './utils';
4
+
5
+ /**
6
+ * CustomTooltipContent — Tooltip com portal e seta estilo sidebar/assistente.
7
+ *
8
+ * Substitui os componentes `SidebarTooltipContent` e `AssistantTooltipContent`
9
+ * que eram definidos inline em `sidebar.tsx` e `xertica-assistant.tsx`.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { Tooltip, TooltipTrigger } from '../ui/tooltip';
14
+ * import { CustomTooltipContent } from '../shared/CustomTooltipContent';
15
+ *
16
+ * <Tooltip>
17
+ * <TooltipTrigger asChild><Button>Hover me</Button></TooltipTrigger>
18
+ * <CustomTooltipContent side="right">Minha dica</CustomTooltipContent>
19
+ * </Tooltip>
20
+ * ```
21
+ */
22
+ export function CustomTooltipContent({
23
+ className,
24
+ sideOffset = 0,
25
+ children,
26
+ ...props
27
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
28
+ return (
29
+ <TooltipPrimitive.Portal>
30
+ <TooltipPrimitive.Content
31
+ data-slot="tooltip-content"
32
+ sideOffset={sideOffset}
33
+ className={cn(
34
+ 'bg-popover text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95',
35
+ 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
36
+ 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',
37
+ 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
38
+ 'z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
39
+ className,
40
+ )}
41
+ {...props}
42
+ >
43
+ {children}
44
+ <TooltipPrimitive.Arrow
45
+ className="fill-popover z-50 drop-shadow-sm"
46
+ width={8}
47
+ height={4}
48
+ />
49
+ </TooltipPrimitive.Content>
50
+ </TooltipPrimitive.Portal>
51
+ );
52
+ }
@@ -1,5 +1,5 @@
1
1
  /** Width of the sidebar when expanded (Desktop) */
2
- export const SIDEBAR_EXPANDED_WIDTH = 256;
2
+ export const SIDEBAR_EXPANDED_WIDTH = 280;
3
3
  /** Width of the sidebar when collapsed (Desktop) */
4
4
  export const SIDEBAR_COLLAPSED_WIDTH = 80;
5
5
  /** Width of the assistant panel when expanded (Desktop) */