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.
- package/CHANGELOG.md +14 -0
- package/bin/cli.ts +2 -1
- package/bin/generate-tokens.ts +24 -0
- package/components/assistant/code-block/CodeBlock.tsx +1 -0
- package/components/assistant/formatted-document/FormattedDocument.tsx +2 -2
- package/components/assistant/modern-chat-input/ModernChatInput.tsx +6 -0
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +9 -0
- package/components/blocks/audio-player/AudioPlayer.tsx +10 -7
- package/components/blocks/document-editor/DocumentEditor.tsx +7 -0
- package/components/blocks/podcast-player/PodcastPlayer.tsx +86 -65
- package/components/brand/language-selector/LanguageSelector.tsx +4 -1
- package/components/brand/language-selector/language-selector.mdx +2 -2
- package/components/brand/theme-toggle/ThemeToggle.tsx +1 -0
- package/components/brand/xertica-logo/XerticaLogo.tsx +1 -0
- package/components/brand/xertica-orbe/XerticaOrbe.tsx +1 -0
- package/components/examples/ApiKeyMapExample.tsx +1 -0
- package/components/examples/DrawingMapExample.tsx +1 -1
- package/components/examples/MapGmpExample.tsx +2 -1
- package/components/examples/MapShowcase.tsx +2 -0
- package/components/layout/header/header.tsx +5 -1
- package/components/layout/sidebar/sidebar.tsx +15 -1
- package/components/layout/sidebar-primitive/sidebar-primitive.tsx +1 -0
- package/components/media/AudioPlayer.tsx +18 -12
- package/components/media/FloatingMediaWrapper.tsx +4 -0
- package/components/media/VideoPlayer.tsx +6 -0
- package/components/pages/template-content/TemplateContent.tsx +18 -11
- package/components/ui/calendar/calendar.tsx +3 -1
- package/components/ui/page-header/page-header.tsx +1 -0
- package/components/ui/rich-text-editor/rich-text-editor.tsx +11 -8
- package/components/ui/search/search.tsx +2 -0
- package/dist/cli.js +26 -2
- package/dist/index.es.js +227 -115
- package/dist/index.umd.js +227 -115
- package/dist/xertica-ui.css +1 -1
- package/docs/components/input.md +13 -0
- package/docs/components/podcast-player.md +2 -0
- package/docs/getting-started.md +47 -0
- package/llms-full.txt +15 -0
- package/llms.txt +11 -5
- package/package.json +1 -1
- package/styles/xertica/tokens.css +17 -0
- package/templates/package.json +2 -2
- package/templates/src/styles/index.css +91 -47
- 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.
|
|
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();
|
package/bin/generate-tokens.ts
CHANGED
|
@@ -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-
|
|
157
|
-
<div className="flex items-center gap-
|
|
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
|
|
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
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
|
203
|
-
{/* Volume */}
|
|
204
|
-
<div className=
|
|
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
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
|
224
|
-
<div className={cn("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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
|
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>
|