xertica-ui 1.6.2 → 1.7.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 (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/cli.ts +2 -1
  3. package/bin/generate-tokens.ts +24 -0
  4. package/components/assistant/code-block/CodeBlock.tsx +1 -0
  5. package/components/assistant/formatted-document/FormattedDocument.tsx +2 -2
  6. package/components/assistant/modern-chat-input/ModernChatInput.tsx +6 -0
  7. package/components/assistant/xertica-assistant/xertica-assistant.tsx +9 -0
  8. package/components/blocks/audio-player/AudioPlayer.tsx +10 -7
  9. package/components/blocks/document-editor/DocumentEditor.tsx +7 -0
  10. package/components/blocks/podcast-player/PodcastPlayer.tsx +86 -65
  11. package/components/brand/language-selector/LanguageSelector.tsx +4 -1
  12. package/components/brand/language-selector/language-selector.mdx +2 -2
  13. package/components/brand/theme-toggle/ThemeToggle.tsx +1 -0
  14. package/components/brand/xertica-logo/XerticaLogo.tsx +1 -0
  15. package/components/brand/xertica-orbe/XerticaOrbe.tsx +1 -0
  16. package/components/examples/ApiKeyMapExample.tsx +1 -0
  17. package/components/examples/DrawingMapExample.tsx +1 -1
  18. package/components/examples/MapGmpExample.tsx +2 -1
  19. package/components/examples/MapShowcase.tsx +2 -0
  20. package/components/layout/header/header.tsx +5 -1
  21. package/components/layout/sidebar/sidebar.tsx +15 -1
  22. package/components/layout/sidebar-primitive/sidebar-primitive.tsx +1 -0
  23. package/components/media/AudioPlayer.tsx +18 -12
  24. package/components/media/FloatingMediaWrapper.tsx +4 -0
  25. package/components/media/VideoPlayer.tsx +6 -0
  26. package/components/pages/template-content/TemplateContent.tsx +18 -11
  27. package/components/ui/calendar/calendar.tsx +3 -1
  28. package/components/ui/page-header/page-header.tsx +1 -0
  29. package/components/ui/rich-text-editor/rich-text-editor.tsx +11 -8
  30. package/components/ui/search/search.tsx +2 -0
  31. package/dist/cli.js +26 -2
  32. package/dist/index.es.js +227 -115
  33. package/dist/index.umd.js +227 -115
  34. package/dist/xertica-ui.css +1 -1
  35. package/docs/components/input.md +13 -0
  36. package/docs/components/podcast-player.md +2 -0
  37. package/docs/getting-started.md +47 -0
  38. package/llms-full.txt +15 -0
  39. package/llms.txt +11 -5
  40. package/package.json +1 -1
  41. package/styles/xertica/tokens.css +17 -0
  42. package/templates/package.json +2 -2
  43. package/templates/src/styles/index.css +91 -47
  44. package/templates/src/styles/xertica/tokens.css +17 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.7.0] — 2026-04-23
11
+
12
+ ### Fixed
13
+ - **CLI CSS Theme Import** — Corrected `@theme` → `@theme inline` in both the library's `index.css` and the generated template `src/styles/index.css`. Plain `@theme {}` was causing Tailwind v4 to resolve color tokens statically at build time using the library's defaults, preventing consumer `tokens.css` overrides from propagating into utility classes (`bg-destructive`, `bg-primary`, alert colors, etc.).
14
+ - **Dark Mode `--primary`** — Added missing `--primary: var(--xertica-primary)` in the dark mode block of `tokens.css` (library, template, and generator). Components using `bg-primary` were not picking up the dark mode brand color.
15
+ - **Dark Mode Chart Tokens** — Generator (`bin/generate-tokens.ts`) now includes `--chart-1` through `--chart-5` in the dark mode section for all generated `tokens.css` files.
16
+
17
+ ### Added
18
+ - **CLI `update` command** — `npx xertica-ui@latest update` prompts the user to select a new color theme and overwrites `src/styles/xertica/tokens.css` with the newly generated tokens, preserving all other project files.
19
+ - **Template `@theme inline` mapping** — Generated `src/styles/index.css` now includes a complete `@theme inline {}` block mapping all tokens (sidebar, charts, gradients, brand, radii) to CSS variable aliases, ensuring full theme coverage for consumer projects.
20
+ - **Documentation** — `docs/getting-started.md` updated with CLI `update` command reference and a new "CSS Setup (Critical)" section explaining the `@theme inline` requirement.
21
+
22
+ ---
23
+
10
24
  ## [1.6.0] — 2026-04-20
11
25
 
12
26
  ### Added
package/bin/cli.ts CHANGED
@@ -18,7 +18,7 @@ const program = new Command();
18
18
  program
19
19
  .name('xertica-ui')
20
20
  .description('CLI to initialize Xertica UI projects')
21
- .version('1.0.0');
21
+ .version('1.7.0');
22
22
 
23
23
  program
24
24
  .command('init')
@@ -243,6 +243,7 @@ ${routes.join('\n')}
243
243
 
244
244
  program
245
245
  .command('update')
246
+ .alias('update-theme')
246
247
  .description('Update theme tokens in your project')
247
248
  .action(async () => {
248
249
  const targetDir = process.cwd();
@@ -66,6 +66,14 @@ export const generateTokensCss = (theme: ColorTheme): string => {
66
66
  --destructive: rgba(239, 68, 68, 1);
67
67
  --destructive-foreground: rgba(250, 250, 250, 1);
68
68
 
69
+ /* Semantic Status Colors */
70
+ --success: rgba(5, 150, 105, 1);
71
+ --success-foreground: rgba(250, 250, 250, 1);
72
+ --info: rgba(37, 99, 235, 1);
73
+ --info-foreground: rgba(250, 250, 250, 1);
74
+ --warning: rgba(245, 158, 11, 1);
75
+ --warning-foreground: rgba(24, 24, 27, 1);
76
+
69
77
  --border: rgba(228, 228, 231, 1);
70
78
  --input: rgba(244, 244, 245, 0.5);
71
79
  --input-background: rgba(244, 244, 245, 0.5);
@@ -162,6 +170,7 @@ export const generateTokensCss = (theme: ColorTheme): string => {
162
170
  .dark {
163
171
  /* Brand Tokens */
164
172
  --xertica-primary: ${rgba(colors.primaryDarkMode)};
173
+ --primary: var(--xertica-primary);
165
174
 
166
175
  /* Semantic Colors */
167
176
  --background: rgba(5, 5, 5, 1);
@@ -189,12 +198,27 @@ export const generateTokensCss = (theme: ColorTheme): string => {
189
198
  --destructive: rgba(239, 68, 68, 1);
190
199
  --destructive-foreground: rgba(250, 250, 250, 1);
191
200
 
201
+ /* Semantic Status Colors */
202
+ --success: rgba(34, 197, 94, 1);
203
+ --success-foreground: rgba(5, 5, 5, 1);
204
+ --info: rgba(96, 165, 250, 1);
205
+ --info-foreground: rgba(5, 5, 5, 1);
206
+ --warning: rgba(251, 191, 36, 1);
207
+ --warning-foreground: rgba(5, 5, 5, 1);
208
+
192
209
  --border: rgba(63, 63, 70, 1);
193
210
  --input: rgba(39, 39, 42, 0.5);
194
211
  --input-background: rgba(39, 39, 42, 0.5);
195
212
  --ring: ${rgba(colors.primaryDarkMode, 0.5)};
196
213
 
197
214
  --elevation-sm: 0px 0px 48px 0px rgba(0, 0, 0, 0.3);
215
+
216
+ /* Charts */
217
+ --chart-1: ${rgba(colors.chart1)};
218
+ --chart-2: ${rgba(colors.chart2)};
219
+ --chart-3: ${rgba(colors.chart3)};
220
+ --chart-4: ${rgba(colors.chart4)};
221
+ --chart-5: ${rgba(colors.chart5)};
198
222
 
199
223
  /* Sidebar */
200
224
  --sidebar: ${rgba(colors.sidebarDark)};
@@ -229,6 +229,7 @@ export const CodeBlock = ({
229
229
  size="icon"
230
230
  className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity z-10 bg-background/80 hover:bg-background"
231
231
  onClick={handleCopy}
232
+ aria-label={copied ? "Copiado" : "Copiar código"}
232
233
  >
233
234
  {copied ? (
234
235
  <Check className="w-4 h-4 text-[var(--toast-success-icon)]" />
@@ -51,8 +51,8 @@ export function FormattedDocument({ content, maxPreviewLength = 500, className =
51
51
  html = html.replace(/^\d+\. (.*$)/gim, '<li class="ml-4 my-0.5">$1</li>');
52
52
 
53
53
  // Checkboxes
54
- html = html.replace(/- \[ \] (.*$)/gim, '<div class="flex items-center gap-2 ml-4 my-1"><input type="checkbox" disabled class="rounded w-3 h-3" /> <span class="text-sm">$1</span></div>');
55
- html = html.replace(/- \[x\] (.*$)/gim, '<div class="flex items-center gap-2 ml-4 my-1"><input type="checkbox" checked disabled class="rounded w-3 h-3" /> <span class="text-sm">$1</span></div>');
54
+ html = html.replace(/- \[ \] (.*$)/gim, '<div class="flex items-center gap-2 ml-4 my-1"><input type="checkbox" disabled class="rounded w-3 h-3" aria-label="$1" /> <span class="text-sm">$1</span></div>');
55
+ html = html.replace(/- \[x\] (.*$)/gim, '<div class="flex items-center gap-2 ml-4 my-1"><input type="checkbox" checked disabled class="rounded w-3 h-3" aria-label="$1" /> <span class="text-sm">$1</span></div>');
56
56
 
57
57
  // Horizontal rule
58
58
  html = html.replace(/^---$/gim, '<hr class="my-3 border-border" />');
@@ -318,6 +318,7 @@ export function ModernChatInput({
318
318
  <button
319
319
  onClick={handleRemoveAction}
320
320
  className="ml-1 hover:bg-white/20 rounded-full p-0.5 transition-colors"
321
+ aria-label="Remover ação"
321
322
  >
322
323
  <X className="w-3 h-3" />
323
324
  </button>
@@ -338,6 +339,7 @@ export function ModernChatInput({
338
339
  onFocus={handleFocus}
339
340
  onBlur={handleBlur}
340
341
  placeholder={placeholder}
342
+ aria-label={placeholder || "Mensagem para o Xertica"}
341
343
  disabled={disabled}
342
344
  className="w-full bg-transparent border-0 outline-none resize-none text-foreground placeholder-muted-foreground text-sm leading-5 min-h-[20px] max-h-[100px] overflow-y-auto scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent"
343
345
  style={{
@@ -363,6 +365,7 @@ export function ModernChatInput({
363
365
  variant="ghost"
364
366
  size="sm"
365
367
  className="h-8 w-8 p-0 rounded-full text-muted-foreground hover:bg-accent hover:text-foreground transition-all duration-200"
368
+ aria-label="Ver ações"
366
369
  >
367
370
  <Plus className="w-4 h-4" />
368
371
  </Button>
@@ -431,6 +434,7 @@ export function ModernChatInput({
431
434
  size="sm"
432
435
  onClick={onFileUpload}
433
436
  className="h-8 w-8 p-0 rounded-full text-muted-foreground hover:bg-accent hover:text-foreground transition-all duration-200"
437
+ aria-label="Anexar arquivo"
434
438
  >
435
439
  <Paperclip className="w-4 h-4" />
436
440
  </Button>
@@ -453,6 +457,7 @@ export function ModernChatInput({
453
457
  ? 'bg-destructive hover:bg-destructive/90 text-white animate-pulse'
454
458
  : 'text-muted-foreground hover:bg-accent hover:text-foreground'
455
459
  }`}
460
+ aria-label={isRecording ? "Parar gravação" : "Sugerir com voz"}
456
461
  >
457
462
  <Mic className="w-4 h-4" />
458
463
  </Button>
@@ -480,6 +485,7 @@ export function ModernChatInput({
480
485
  ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg shadow-primary/30 hover:shadow-primary/40'
481
486
  : 'bg-muted text-muted-foreground cursor-not-allowed'
482
487
  }`}
488
+ aria-label="Enviar mensagem"
483
489
  >
484
490
  <motion.div
485
491
  animate={{
@@ -846,6 +846,7 @@ export function XerticaAssistant({
846
846
  onFileAttach(file);
847
847
  }
848
848
  }}
849
+ aria-label="Upload de documento"
849
850
  />
850
851
  <input
851
852
  ref={audioInputRef}
@@ -858,6 +859,7 @@ export function XerticaAssistant({
858
859
  onFileAttach(file);
859
860
  }
860
861
  }}
862
+ aria-label="Upload de áudio"
861
863
  />
862
864
 
863
865
  {/* Main Assistant Container */}
@@ -895,6 +897,7 @@ export function XerticaAssistant({
895
897
  size="icon"
896
898
  className="h-8 w-8 rounded-full"
897
899
  onClick={() => setEditingDocument(null)}
900
+ aria-label="Fechar editor de documento"
898
901
  >
899
902
  <X className="w-4 h-4" />
900
903
  </Button>
@@ -948,6 +951,7 @@ export function XerticaAssistant({
948
951
  onClick={onNavigateFullPage}
949
952
  className="h-8 w-8 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-full"
950
953
  title="Expandir assistente"
954
+ aria-label="Expandir assistente"
951
955
  >
952
956
  <Maximize2 className="w-4 h-4" />
953
957
  </Button>
@@ -961,6 +965,7 @@ export function XerticaAssistant({
961
965
  "h-8 w-8 p-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground rounded-full",
962
966
  !isExpanded && "w-10 h-10" // Slightly larger touch target when collapsed if desired, or keep consistent
963
967
  )}
968
+ aria-label={isExpanded ? "Recolher assistente" : "Expandir assistente"}
964
969
  >
965
970
  {isExpanded ? (
966
971
  <ChevronRight className="w-4 h-4" />
@@ -981,6 +986,7 @@ export function XerticaAssistant({
981
986
  <button
982
987
  onClick={handleToggle}
983
988
  className="w-10 h-10 rounded-full flex items-center justify-center hover:bg-accent/50 transition-colors duration-200 cursor-pointer mx-auto"
989
+ aria-label="Abrir assistente"
984
990
  >
985
991
  <XerticaOrbe size={32} />
986
992
  </button>
@@ -1547,6 +1553,7 @@ export function XerticaAssistant({
1547
1553
  )}
1548
1554
  onClick={() => handleEvaluationClick(msg.id, 'like')}
1549
1555
  title="Gostei"
1556
+ aria-label="Gostei"
1550
1557
  >
1551
1558
  <ThumbsUp className="h-3.5 w-3.5" />
1552
1559
  </Button>
@@ -1561,6 +1568,7 @@ export function XerticaAssistant({
1561
1568
  msg.evaluation === 'dislike' && "text-red-600 bg-red-100 dark:bg-red-900/20"
1562
1569
  )}
1563
1570
  title="Não gostei"
1571
+ aria-label="Não gostei"
1564
1572
  >
1565
1573
  <ThumbsDown className="h-3.5 w-3.5" />
1566
1574
  </Button>
@@ -1763,6 +1771,7 @@ export function XerticaAssistant({
1763
1771
  <Textarea
1764
1772
  className="min-h-[100px]"
1765
1773
  placeholder={evaluationState.category ? "Comentário adicional..." : "Descreva o motivo..."}
1774
+ aria-label={evaluationState.category ? "Comentário adicional" : "Descreva o motivo"}
1766
1775
  value={evaluationState.reason}
1767
1776
  onChange={(e) => setEvaluationState(prev => ({ ...prev, reason: e.target.value }))}
1768
1777
  rows={4}
@@ -136,6 +136,7 @@ export function AudioPlayer({
136
136
  <button
137
137
  onClick={togglePlay}
138
138
  className="text-muted-foreground hover:text-foreground transition-colors focus:outline-none hover:scale-105 active:scale-95 transform duration-100"
139
+ aria-label={isPlaying ? "Pausar" : "Reproduzir"}
139
140
  >
140
141
  {isPlaying ? (
141
142
  <PauseCircle className="w-10 h-10" strokeWidth={1.5} />
@@ -154,6 +155,7 @@ export function AudioPlayer({
154
155
  value={[current]}
155
156
  onValueChange={(val) => setCurrent(val[0])}
156
157
  className="cursor-pointer"
158
+ aria-label="Progresso da reprodução"
157
159
  />
158
160
  <span className="text-xs text-muted-foreground font-mono shrink-0 w-10">
159
161
  {formatTime(duration)}
@@ -165,7 +167,7 @@ export function AudioPlayer({
165
167
  <div className="flex items-center gap-1 md:gap-3 shrink-0">
166
168
  {/* Volume */}
167
169
  <div className="hidden lg:flex items-center gap-2 w-28 mr-2 group">
168
- <button onClick={toggleMute} className="text-muted-foreground hover:text-foreground">
170
+ <button onClick={toggleMute} className="text-muted-foreground hover:text-foreground" aria-label={isMuted || volume[0] === 0 ? "Ativar som" : "Desativar som"}>
169
171
  {isMuted || volume[0] === 0 ? <VolumeX className="w-5 h-5" /> : <Volume2 className="w-5 h-5" />}
170
172
  </button>
171
173
  <Slider
@@ -174,6 +176,7 @@ export function AudioPlayer({
174
176
  value={volume}
175
177
  onValueChange={setVolume}
176
178
  className="w-full opacity-60 group-hover:opacity-100 transition-opacity"
179
+ aria-label="Volume"
177
180
  />
178
181
  </div>
179
182
 
@@ -181,7 +184,7 @@ export function AudioPlayer({
181
184
  <TooltipProvider>
182
185
  <Tooltip>
183
186
  <TooltipTrigger asChild>
184
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
187
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Restart">
185
188
  <RotateCcw className="w-4 h-4" />
186
189
  </Button>
187
190
  </TooltipTrigger>
@@ -192,7 +195,7 @@ export function AudioPlayer({
192
195
  <TooltipProvider>
193
196
  <Tooltip>
194
197
  <TooltipTrigger asChild>
195
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
198
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Velocidade de reprodução">
196
199
  <Gauge className="w-4 h-4" />
197
200
  </Button>
198
201
  </TooltipTrigger>
@@ -203,7 +206,7 @@ export function AudioPlayer({
203
206
  <TooltipProvider>
204
207
  <Tooltip>
205
208
  <TooltipTrigger asChild>
206
- <Button variant="ghost" size="icon" className="text-[var(--chart-3)] hover:text-[var(--chart-3)]/80 hover:bg-accent rounded-full h-9 w-9">
209
+ <Button variant="ghost" size="icon" className="text-[var(--chart-3)] hover:text-[var(--chart-3)]/80 hover:bg-accent rounded-full h-9 w-9" aria-label="Informações sobre novas atualizações">
207
210
  <Info className="w-4 h-4 fill-current" />
208
211
  </Button>
209
212
  </TooltipTrigger>
@@ -216,7 +219,7 @@ export function AudioPlayer({
216
219
  <TooltipProvider>
217
220
  <Tooltip>
218
221
  <TooltipTrigger asChild>
219
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
222
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Atualizar versão">
220
223
  <RefreshCw className="w-4 h-4" />
221
224
  </Button>
222
225
  </TooltipTrigger>
@@ -224,13 +227,13 @@ export function AudioPlayer({
224
227
  </Tooltip>
225
228
  </TooltipProvider>
226
229
 
227
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
230
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Baixar áudio">
228
231
  <Download className="w-4 h-4" />
229
232
  </Button>
230
233
 
231
234
  <div className="w-px h-8 bg-border mx-1" />
232
235
 
233
- <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
236
+ <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Fechar player">
234
237
  <X className="w-5 h-5" />
235
238
  </Button>
236
239
  </div>
@@ -292,6 +292,7 @@ export function DocumentEditor({ initialContent, initialTitle = 'Novo Documento'
292
292
  onChange={(e) => setTitle(e.target.value)}
293
293
  className="flex-1 bg-transparent border-none outline-none text-sm font-medium text-foreground min-w-0"
294
294
  placeholder="Título do documento"
295
+ aria-label="Título do documento"
295
296
  />
296
297
  </div>
297
298
  <div className="flex items-center gap-1 flex-shrink-0 ml-2">
@@ -302,6 +303,7 @@ export function DocumentEditor({ initialContent, initialTitle = 'Novo Documento'
302
303
  disabled={!canUndo}
303
304
  className="h-8 w-8 p-0"
304
305
  title="Desfazer (Ctrl+Z)"
306
+ aria-label="Desfazer (Ctrl+Z)"
305
307
  >
306
308
  <Undo className="w-4 h-4" />
307
309
  </Button>
@@ -312,6 +314,7 @@ export function DocumentEditor({ initialContent, initialTitle = 'Novo Documento'
312
314
  disabled={!canRedo}
313
315
  className="h-8 w-8 p-0"
314
316
  title="Refazer (Ctrl+Y)"
317
+ aria-label="Refazer (Ctrl+Y)"
315
318
  >
316
319
  <Redo className="w-4 h-4" />
317
320
  </Button>
@@ -321,6 +324,7 @@ export function DocumentEditor({ initialContent, initialTitle = 'Novo Documento'
321
324
  size="sm"
322
325
  onClick={onClose}
323
326
  className="h-8 w-8 p-0"
327
+ aria-label="Fechar editor"
324
328
  >
325
329
  <X className="w-4 h-4" />
326
330
  </Button>
@@ -404,6 +408,9 @@ export function DocumentEditor({ initialContent, initialTitle = 'Novo Documento'
404
408
  <div
405
409
  ref={editorRef}
406
410
  contentEditable
411
+ role="textbox"
412
+ aria-multiline="true"
413
+ aria-label={title || "Conteúdo do documento"}
407
414
  onInput={handleInput}
408
415
  onCompositionStart={handleCompositionStart}
409
416
  onCompositionEnd={handleCompositionEnd}
@@ -130,6 +130,7 @@ export function PodcastPlayer({
130
130
 
131
131
  const playerContent = (
132
132
  <div
133
+ data-podcast-player
133
134
  className={cn(
134
135
  "backdrop-blur-md transition-all duration-300 ease-in-out shadow-[var(--elevation-sm)]",
135
136
  isFixed
@@ -144,88 +145,108 @@ export function PodcastPlayer({
144
145
  "opacity-100 translate-y-0"
145
146
  ]
146
147
  )}
147
- style={isFixed && isViewport ? {
148
- left: isMobile ? 0 : leftOffset,
149
- right: isMobile ? 0 : assistantWidth
150
- } : {}}
151
148
  >
149
+ {/* Mobile-first: Full width on mobile, offsets on desktop */}
150
+ <style dangerouslySetInnerHTML={{ __html: `
151
+ [data-podcast-player] {
152
+ left: 0 !important;
153
+ right: 0 !important;
154
+ }
155
+ @media (min-width: 768px) {
156
+ [data-podcast-player] {
157
+ left: ${leftOffset}px !important;
158
+ right: ${assistantWidth}px !important;
159
+ }
160
+ }
161
+ `}} />
162
+
152
163
  {/* Desktop Layout: Horizontal */}
153
164
  <div className="hidden md:flex h-[72px] px-4 md:px-6 items-center justify-between gap-4">
154
165
 
155
- {/* Left: Info */}
156
- <div className="flex flex-col min-w-0 w-[180px] lg:w-[240px] shrink-0">
157
- <div className="flex items-center gap-2 mb-0.5">
158
- <Radio className="w-3 h-3 text-[var(--chart-4)] animate-pulse" />
159
- <h4 className="text-sm md:text-base truncate font-medium" title={title}>
166
+ {/* Left: Info - More flexible on smaller screens */}
167
+ <div className="flex flex-col min-w-0 w-32 lg:w-48 xl:w-60 shrink-0">
168
+ <div className="flex items-center gap-1.5 mb-0.5">
169
+ <Radio className="w-3 h-3 text-[var(--chart-4)] animate-pulse shrink-0" />
170
+ <h4 className="text-sm md:text-sm lg:text-base truncate font-medium" title={title}>
160
171
  {title}
161
172
  </h4>
162
173
  </div>
163
174
  <div className="flex items-center gap-1">
164
- <span className="truncate text-xs text-muted-foreground">{subtitle}</span>
165
- <ExternalLink className="w-3 h-3 cursor-pointer hover:text-foreground ml-1 opacity-70 hover:opacity-100" />
175
+ <span className="truncate text-[10px] lg:text-xs text-muted-foreground">{subtitle}</span>
176
+ <ExternalLink className="w-3 h-3 cursor-pointer hover:text-foreground ml-1 opacity-70 hover:opacity-100 hidden sm:block" />
166
177
  </div>
167
178
  </div>
168
179
 
169
- {/* Center: Controls */}
170
- <div className="flex-1 flex items-center justify-center gap-4 max-w-3xl">
180
+ {/* Center: Controls - Flexibly shrinking slider */}
181
+ <div className="flex-1 flex items-center justify-center gap-2 lg:gap-4 min-w-0">
171
182
  <button
172
183
  onClick={togglePlay}
173
- className="text-muted-foreground hover:text-foreground transition-colors focus:outline-none hover:scale-105 active:scale-95 transform duration-100"
184
+ className="text-muted-foreground hover:text-foreground transition-colors focus:outline-none hover:scale-105 active:scale-95 transform duration-100 shrink-0"
174
185
  aria-label={isPlaying ? "Pause" : "Play"}
175
186
  >
176
187
  {isPlaying ? (
177
- <PauseCircle className="w-10 h-10" strokeWidth={1.5} />
188
+ <PauseCircle className="w-8 h-8 lg:w-10 lg:h-10" strokeWidth={1.5} />
178
189
  ) : (
179
- <PlayCircle className="w-10 h-10" strokeWidth={1.5} />
190
+ <PlayCircle className="w-8 h-8 lg:w-10 lg:h-10" strokeWidth={1.5} />
180
191
  )}
181
192
  </button>
182
193
 
183
- <div className="flex-1 flex items-center gap-3">
184
- <span className="text-xs text-muted-foreground font-mono shrink-0 w-10 text-right">
194
+ <div className="flex-1 flex items-center gap-2 lg:gap-3 min-w-0 max-w-2xl">
195
+ <span className="text-[10px] lg:text-xs text-muted-foreground font-mono shrink-0 w-8 lg:w-10 text-right">
185
196
  {formatTime(current)}
186
197
  </span>
187
- <Slider
188
- defaultValue={[0]}
189
- max={duration}
190
- value={[current]}
191
- onValueChange={(val) => setCurrent(val[0])}
192
- className="cursor-pointer"
193
- aria-label="Progress bar"
194
- />
195
- <span className="text-xs text-muted-foreground font-mono shrink-0 w-10">
198
+ <div className="flex-1 min-w-[40px]">
199
+ <Slider
200
+ defaultValue={[0]}
201
+ max={duration}
202
+ value={[current]}
203
+ onValueChange={(val) => setCurrent(val[0])}
204
+ className="cursor-pointer"
205
+ aria-label="Progress bar"
206
+ />
207
+ </div>
208
+ <span className="text-[10px] lg:text-xs text-muted-foreground font-mono shrink-0 w-8 lg:w-10">
196
209
  {formatTime(duration)}
197
210
  </span>
198
211
  </div>
199
212
  </div>
200
213
 
201
- {/* Right: Actions */}
202
- <div className="flex items-center gap-1 md:gap-3 shrink-0">
203
- {/* Volume */}
204
- <div className="hidden lg:flex items-center gap-2 w-28 mr-2 group">
214
+ {/* Right: Actions - Collapsing secondary controls on smaller desktop/inline screens */}
215
+ <div className="flex items-center gap-1 lg:gap-3 shrink-0">
216
+ {/* Volume: Hide slider on smaller desktop or inline to save space */}
217
+ <div className={cn(
218
+ "flex items-center gap-2 group transition-all",
219
+ variant === 'inline' ? "w-8 overflow-hidden hover:w-28" : "w-10 lg:w-28"
220
+ )}>
205
221
  <button
206
222
  onClick={toggleMute}
207
- className="text-muted-foreground hover:text-foreground"
223
+ className="text-muted-foreground hover:text-foreground shrink-0"
208
224
  aria-label={isMuted ? "Unmute" : "Mute"}
209
225
  >
210
- {isMuted || volume[0] === 0 ? <VolumeX className="w-5 h-5" /> : <Volume2 className="w-5 h-5" />}
226
+ {isMuted || volume[0] === 0 ? <VolumeX className="w-4 h-4 lg:w-5 lg:h-5" /> : <Volume2 className="w-4 h-4 lg:w-5 lg:h-5" />}
211
227
  </button>
212
- <Slider
213
- defaultValue={[80]}
214
- max={100}
215
- value={volume}
216
- onValueChange={setVolume}
217
- className="w-full opacity-60 group-hover:opacity-100 transition-opacity"
218
- aria-label="Volume control"
219
- />
228
+ <div className={cn(
229
+ "flex-1 transition-all overflow-hidden",
230
+ variant === 'inline' ? "opacity-0 group-hover:opacity-100" : "hidden lg:block"
231
+ )}>
232
+ <Slider
233
+ defaultValue={[80]}
234
+ max={100}
235
+ value={volume}
236
+ onValueChange={setVolume}
237
+ className="w-full"
238
+ aria-label="Volume control"
239
+ />
240
+ </div>
220
241
  </div>
221
242
 
222
243
  <div className="flex items-center gap-1">
223
- {/* Desktop: Restart & Speed */}
224
- <div className={cn("hidden md:flex items-center gap-1", variant === 'inline' && "!hidden")}>
244
+ {/* Desktop-only extended actions, hidden on small horizontal or inline */}
245
+ <div className={cn("hidden xl:flex items-center gap-1", variant === 'inline' && "!hidden")}>
225
246
  <TooltipProvider>
226
247
  <Tooltip>
227
248
  <TooltipTrigger asChild>
228
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
249
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8 lg:h-9 lg:w-9" aria-label="Reiniciar">
229
250
  <RotateCcw className="w-4 h-4" />
230
251
  </Button>
231
252
  </TooltipTrigger>
@@ -236,7 +257,7 @@ export function PodcastPlayer({
236
257
  <TooltipProvider>
237
258
  <Tooltip>
238
259
  <TooltipTrigger asChild>
239
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
260
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8 lg:h-9 lg:w-9" aria-label="Velocidade de reprodução">
240
261
  <Gauge className="w-4 h-4" />
241
262
  </Button>
242
263
  </TooltipTrigger>
@@ -249,7 +270,7 @@ export function PodcastPlayer({
249
270
  <TooltipProvider>
250
271
  <Tooltip>
251
272
  <TooltipTrigger asChild>
252
- <Button variant="ghost" size="icon" className="text-[var(--chart-4)] hover:text-[var(--chart-4)]/80 hover:bg-accent rounded-full h-9 w-9">
273
+ <Button variant="ghost" size="icon" className="text-[var(--chart-4)] hover:text-[var(--chart-4)]/80 hover:bg-accent rounded-full h-9 w-9" aria-label="Mais informações">
253
274
  <Info className="w-4 h-4 fill-current" />
254
275
  </Button>
255
276
  </TooltipTrigger>
@@ -264,7 +285,7 @@ export function PodcastPlayer({
264
285
  <TooltipProvider>
265
286
  <Tooltip>
266
287
  <TooltipTrigger asChild>
267
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
288
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Atualizar versão">
268
289
  <RefreshCw className="w-4 h-4" />
269
290
  </Button>
270
291
  </TooltipTrigger>
@@ -272,18 +293,18 @@ export function PodcastPlayer({
272
293
  </Tooltip>
273
294
  </TooltipProvider>
274
295
 
275
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
276
- <Download className="w-4 h-4" />
277
- </Button>
296
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Baixar áudio">
297
+ <Download className="w-4 h-4" />
298
+ </Button>
278
299
  </div>
279
300
 
280
301
  {/* Mobile: Menu (containing Restart, Speed, Refresh, Download) */}
281
302
  <div className={cn("md:hidden", variant === 'inline' && "!block")}>
282
303
  <DropdownMenu>
283
304
  <DropdownMenuTrigger asChild>
284
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
285
- <MoreHorizontal className="w-5 h-5" />
286
- </Button>
305
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Mais opções">
306
+ <MoreHorizontal className="w-5 h-5" />
307
+ </Button>
287
308
  </DropdownMenuTrigger>
288
309
  <DropdownMenuContent align="end" className="w-48">
289
310
  <DropdownMenuItem className="cursor-pointer">
@@ -308,9 +329,9 @@ export function PodcastPlayer({
308
329
 
309
330
  <div className="w-px h-8 bg-border mx-1" />
310
331
 
311
- <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9">
312
- <X className="w-5 h-5" />
313
- </Button>
332
+ <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-9 w-9" aria-label="Fechar player">
333
+ <X className="w-5 h-5" />
334
+ </Button>
314
335
  </div>
315
336
  </div>
316
337
  </div>
@@ -332,9 +353,9 @@ export function PodcastPlayer({
332
353
  </div>
333
354
  </div>
334
355
 
335
- <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8 shrink-0">
336
- <X className="w-4 h-4" />
337
- </Button>
356
+ <Button onClick={onClose} variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8 shrink-0" aria-label="Fechar player">
357
+ <X className="w-4 h-4" />
358
+ </Button>
338
359
  </div>
339
360
 
340
361
  {/* Middle: Progress Bar */}
@@ -394,9 +415,9 @@ export function PodcastPlayer({
394
415
  <TooltipProvider>
395
416
  <Tooltip>
396
417
  <TooltipTrigger asChild>
397
- <Button variant="ghost" size="icon" className="text-[var(--chart-4)] hover:text-[var(--chart-4)]/80 hover:bg-accent rounded-full h-8 w-8">
398
- <Info className="w-4 h-4 fill-current" />
399
- </Button>
418
+ <Button variant="ghost" size="icon" className="text-[var(--chart-4)] hover:text-[var(--chart-4)]/80 hover:bg-accent rounded-full h-8 w-8" aria-label="Mais informações">
419
+ <Info className="w-4 h-4 fill-current" />
420
+ </Button>
400
421
  </TooltipTrigger>
401
422
  <TooltipContent className="max-w-[300px]">
402
423
  <p className="text-xs">We identified new events in this process since the last podcast update.</p>
@@ -406,9 +427,9 @@ export function PodcastPlayer({
406
427
 
407
428
  <DropdownMenu>
408
429
  <DropdownMenuTrigger asChild>
409
- <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8">
410
- <MoreHorizontal className="w-4 h-4" />
411
- </Button>
430
+ <Button variant="ghost" size="icon" className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-full h-8 w-8" aria-label="Mais opções">
431
+ <MoreHorizontal className="w-4 h-4" />
432
+ </Button>
412
433
  </DropdownMenuTrigger>
413
434
  <DropdownMenuContent align="end" className="w-48">
414
435
  <DropdownMenuItem className="cursor-pointer">
@@ -42,7 +42,10 @@ export function LanguageSelector({
42
42
 
43
43
  return (
44
44
  <Select value={language} onValueChange={handleLanguageChange}>
45
- <SelectTrigger className={`h-9 gap-2 px-3 border-none shadow-none focus:ring-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground ${className}`}>
45
+ <SelectTrigger
46
+ className={`h-9 gap-2 px-3 border-none shadow-none focus:ring-0 text-muted-foreground hover:bg-accent hover:text-accent-foreground ${className}`}
47
+ aria-label="Selecionar idioma"
48
+ >
46
49
  {showIcon && <Globe className="h-4 w-4 shrink-0" />}
47
50
  <SelectValue placeholder="Idioma">
48
51
  <span className="text-sm">{getLabel(language)}</span>