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.
- package/CHANGELOG.md +46 -0
- package/README.md +1 -1
- package/bin/cli.ts +1 -1
- package/bin/generate-tokens.ts +13 -7
- package/components/assistant/xertica-assistant/index.ts +2 -0
- package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +97 -0
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -0
- package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -0
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +86 -0
- package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +77 -0
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +573 -0
- package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +65 -0
- package/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.tsx +41 -0
- package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +98 -0
- package/components/assistant/xertica-assistant/parts/index.ts +16 -0
- package/components/assistant/xertica-assistant/types.ts +139 -0
- package/components/assistant/xertica-assistant/use-assistant.ts +559 -0
- package/components/assistant/xertica-assistant/xertica-assistant.stories.tsx +200 -0
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +198 -1460
- package/components/brand/theme-toggle/ThemeToggle.tsx +8 -27
- package/components/hooks/index.ts +3 -0
- package/components/hooks/use-layout-shortcuts.ts +46 -0
- package/components/layout/sidebar/index.ts +2 -0
- package/components/layout/sidebar/sidebar.stories.tsx +160 -8
- package/components/layout/sidebar/sidebar.tsx +606 -497
- package/components/layout/sidebar/use-sidebar.ts +104 -0
- package/components/media/audio-player/AudioPlayer.tsx +131 -206
- package/components/media/audio-player/use-audio-player.ts +298 -0
- package/components/pages/home-page/HomePage.tsx +1 -1
- package/components/pages/template-content/TemplateContent.tsx +5 -5
- package/components/pages/template-page/TemplatePage.tsx +5 -5
- package/components/shared/CustomTooltipContent.tsx +52 -0
- package/components/shared/layout-constants.ts +1 -1
- package/components/ui/chart/chart.stories.tsx +966 -7
- package/components/ui/chart/chart.tsx +918 -45
- package/components/ui/file-upload/file-upload.stories.tsx +100 -0
- package/components/ui/file-upload/file-upload.tsx +14 -74
- package/components/ui/file-upload/index.ts +1 -0
- package/components/ui/file-upload/use-file-upload.ts +181 -0
- package/components/ui/pagination/index.ts +2 -0
- package/components/ui/pagination/pagination.stories.tsx +94 -0
- package/components/ui/pagination/use-pagination.ts +194 -0
- package/components/ui/rich-text-editor/index.ts +2 -0
- package/components/ui/rich-text-editor/rich-text-editor.stories.tsx +129 -1
- package/components/ui/rich-text-editor/rich-text-editor.tsx +86 -305
- package/components/ui/rich-text-editor/use-rich-text-editor.ts +439 -0
- package/components/ui/stepper/index.ts +3 -1
- package/components/ui/stepper/stepper.stories.tsx +116 -0
- package/components/ui/stepper/stepper.tsx +4 -4
- package/components/ui/stepper/use-stepper.ts +137 -0
- package/components/ui/tree-view/index.ts +4 -1
- package/components/ui/tree-view/tree-view.stories.tsx +110 -4
- package/components/ui/tree-view/tree-view.tsx +17 -125
- package/components/ui/tree-view/use-tree-view.ts +229 -0
- package/contexts/AssistenteContext.tsx +17 -54
- package/contexts/BrandColorsContext.tsx +6 -17
- package/contexts/LayoutContext.tsx +5 -31
- package/dist/AssistantChart-BAudAfne.cjs +3591 -0
- package/dist/AssistantChart-BP8upjMk.js +3565 -0
- package/dist/AudioPlayer-1ypwE2Wh.cjs +936 -0
- package/dist/AudioPlayer-DuKXrCfy.js +937 -0
- package/dist/CustomTooltipContent-DHjkY0ww.js +40 -0
- package/dist/CustomTooltipContent-c_K-DWRr.cjs +56 -0
- package/dist/LanguageContext-BwhwC3G2.js +657 -0
- package/dist/LanguageContext-DvUt5jBg.cjs +656 -0
- package/dist/LayoutContext-BDmcZfMH.cjs +84 -0
- package/dist/LayoutContext-dbQvdC4O.js +85 -0
- package/dist/ThemeContext-RTy1m2Uq.js +82 -0
- package/dist/ThemeContext-bSzuOit2.cjs +81 -0
- package/dist/VerifyEmailPage-C_ihbcth.js +2828 -0
- package/dist/VerifyEmailPage-Dt7zgA4w.cjs +2827 -0
- package/dist/XerticaProvider-CW9hpCdF.cjs +39 -0
- package/dist/XerticaProvider-siSt9uG2.js +40 -0
- package/dist/XerticaXLogo-D8jf0SNv.cjs +214 -0
- package/dist/XerticaXLogo-fAJMy3H4.js +215 -0
- package/dist/assistant.cjs.js +2 -1
- package/dist/assistant.es.js +3 -2
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +14 -8
- package/dist/components/assistant/xertica-assistant/index.d.ts +2 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantCollapsedView.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantConversationList.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.d.ts +19 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantHeader.d.ts +11 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantMessageBubble.d.ts +29 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTabBar.d.ts +13 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantTypingIndicator.d.ts +4 -0
- package/dist/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.d.ts +17 -0
- package/dist/components/assistant/xertica-assistant/parts/index.d.ts +16 -0
- package/dist/components/assistant/xertica-assistant/types.d.ts +106 -0
- package/dist/components/assistant/xertica-assistant/use-assistant.d.ts +125 -0
- package/dist/components/assistant/xertica-assistant/xertica-assistant.d.ts +8 -97
- package/dist/components/hooks/index.d.ts +3 -0
- package/dist/components/hooks/use-layout-shortcuts.d.ts +22 -0
- package/dist/components/layout/sidebar/index.d.ts +2 -0
- package/dist/components/layout/sidebar/sidebar.d.ts +80 -0
- package/dist/components/layout/sidebar/use-sidebar.d.ts +22 -0
- package/dist/components/media/audio-player/AudioPlayer.d.ts +4 -1
- package/dist/components/media/audio-player/use-audio-player.d.ts +72 -0
- package/dist/components/shared/CustomTooltipContent.d.ts +20 -0
- package/dist/components/shared/layout-constants.d.ts +1 -1
- package/dist/components/ui/alert/alert.d.ts +1 -1
- package/dist/components/ui/badge/badge.d.ts +1 -1
- package/dist/components/ui/button/button.d.ts +2 -2
- package/dist/components/ui/chart/chart.d.ts +162 -5
- package/dist/components/ui/file-upload/file-upload.d.ts +2 -0
- package/dist/components/ui/file-upload/index.d.ts +1 -0
- package/dist/components/ui/file-upload/use-file-upload.d.ts +49 -0
- package/dist/components/ui/pagination/index.d.ts +2 -0
- package/dist/components/ui/pagination/use-pagination.d.ts +78 -0
- package/dist/components/ui/rich-text-editor/index.d.ts +2 -0
- package/dist/components/ui/rich-text-editor/use-rich-text-editor.d.ts +107 -0
- package/dist/components/ui/stepper/index.d.ts +3 -1
- package/dist/components/ui/stepper/stepper.d.ts +2 -2
- package/dist/components/ui/stepper/use-stepper.d.ts +60 -0
- package/dist/components/ui/tree-view/index.d.ts +4 -1
- package/dist/components/ui/tree-view/tree-view.d.ts +4 -6
- package/dist/components/ui/tree-view/use-tree-view.d.ts +60 -0
- package/dist/contexts/AssistenteContext.d.ts +10 -49
- package/dist/hooks.cjs.js +30 -10
- package/dist/hooks.es.js +25 -4
- package/dist/index.cjs.js +20 -9
- package/dist/index.es.js +38 -27
- package/dist/layout.cjs.js +82 -1
- package/dist/layout.es.js +83 -2
- package/dist/media.cjs.js +1 -1
- package/dist/media.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/rich-text-editor-BmsjY03B.js +2949 -0
- package/dist/rich-text-editor-GS2kpTAK.cjs +2966 -0
- package/dist/sidebar-CVUGHOS_.cjs +756 -0
- package/dist/sidebar-CmvwjnVb.js +757 -0
- package/dist/ui.cjs.js +12 -2
- package/dist/ui.es.js +24 -14
- package/dist/use-audio-player-Bkh23vQ3.js +177 -0
- package/dist/use-audio-player-Dn1NR9xN.cjs +176 -0
- package/dist/utils/color-utils.d.ts +51 -0
- package/dist/xertica-assistant-BMqdyRVi.js +2082 -0
- package/dist/xertica-assistant-Bj3vBCq_.cjs +2081 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/ai-usage.md +28 -10
- package/docs/architecture-improvements.md +463 -0
- package/docs/architecture.md +77 -1
- package/docs/components/assistant-chart.md +1 -1
- package/docs/components/assistant.md +159 -0
- package/docs/components/audio-player.md +46 -0
- package/docs/components/branding.md +251 -0
- package/docs/components/chart.md +354 -39
- package/docs/components/code-block.md +108 -0
- package/docs/components/file-upload.md +119 -2
- package/docs/components/formatted-document.md +113 -0
- package/docs/components/hooks.md +430 -0
- package/docs/components/image-with-fallback.md +106 -0
- package/docs/components/map-layers.md +140 -0
- package/docs/components/modern-chat-input.md +163 -0
- package/docs/components/pages.md +351 -0
- package/docs/components/pagination.md +187 -0
- package/docs/components/rich-text-editor.md +164 -0
- package/docs/components/sidebar.md +153 -4
- package/docs/components/stepper.md +157 -12
- package/docs/components/tree-view.md +164 -6
- package/docs/doc-audit.md +223 -0
- package/docs/getting-started.md +155 -1
- package/docs/guidelines.md +14 -8
- package/docs/layout.md +2 -2
- package/docs/llms.md +29 -9
- package/docs/patterns/detail-page.md +276 -0
- package/docs/patterns/settings.md +346 -0
- package/docs/patterns/wizard.md +217 -0
- package/guidelines/Guidelines.md +5 -3
- package/llms.txt +1 -1
- package/package.json +10 -10
- package/styles/xertica/tokens.css +41 -12
- package/templates/CLAUDE.md +16 -6
- package/templates/guidelines/Guidelines.md +16 -4
- package/templates/package.json +3 -3
- package/templates/src/styles/xertica/tokens.css +39 -10
- package/utils/color-utils.ts +72 -0
|
@@ -12,6 +12,13 @@ Full-featured AI chat assistant with support for:
|
|
|
12
12
|
- Three layout modes: `collapsed`, `expanded`, `fullPage`
|
|
13
13
|
- Demo mode (mock AI responses without API key)
|
|
14
14
|
|
|
15
|
+
The component ships in **two usage patterns**:
|
|
16
|
+
|
|
17
|
+
| Pattern | When to use |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `<XerticaAssistant />` | Drop-in assistant panel — zero configuration needed |
|
|
20
|
+
| `useAssistant()` | Headless hook — bring your own UI while reusing all state and business logic |
|
|
21
|
+
|
|
15
22
|
---
|
|
16
23
|
|
|
17
24
|
## Props
|
|
@@ -227,6 +234,155 @@ Each assistant capability can be individually toggled via boolean props. All fla
|
|
|
227
234
|
|
|
228
235
|
---
|
|
229
236
|
|
|
237
|
+
## Headless Hook — `useAssistant()`
|
|
238
|
+
|
|
239
|
+
Use the headless hook when you need full control over the assistant's UI — custom layout, custom styling, or embedding the assistant logic inside a larger component.
|
|
240
|
+
|
|
241
|
+
### Import
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
import { useAssistant } from 'xertica-ui/assistant';
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Hook Props
|
|
248
|
+
|
|
249
|
+
The hook accepts the same props as `XerticaAssistantProps` (minus purely visual props like `width`, `height`, `className`, `mobileFloating`, `onNavigateSettings`, `onNavigateFullPage`, `userName`, and feature flags).
|
|
250
|
+
|
|
251
|
+
| Prop | Type | Default | Description |
|
|
252
|
+
|---|---|---|---|
|
|
253
|
+
| `mode` | `AssistantMode` | `'expanded'` | Layout mode |
|
|
254
|
+
| `isExpanded` | `boolean` | `true` | Controlled expansion state |
|
|
255
|
+
| `onToggle` | `() => void` | — | Toggle callback |
|
|
256
|
+
| `defaultTab` | `AssistantTab` | `'chat'` | Initially selected tab |
|
|
257
|
+
| `demoMode` | `boolean` | `true` | Enable demo mode |
|
|
258
|
+
| `customResponses` | `MockResponse[]` | `[]` | Custom mock responses |
|
|
259
|
+
| `initialMessages` | `Message[]` | `[]` | Pre-loaded messages |
|
|
260
|
+
| `savedConversations` | `Conversation[]` | `[]` | Previously saved conversations |
|
|
261
|
+
| `suggestions` | `Suggestion[]` | — | Suggested prompts |
|
|
262
|
+
| `onSendMessage` | `(message: string) => void` | — | Send message callback |
|
|
263
|
+
| `isProcessing` | `boolean` | `false` | Shows typing indicator |
|
|
264
|
+
| `responseGenerator` | `(message: string) => Promise<string \| Partial<Message>>` | — | Custom response generator |
|
|
265
|
+
| `richSuggestions` | `Suggestion[]` | `[]` | Extended suggestions |
|
|
266
|
+
| `onRichAction` | `(actionId: string, actionText: string) => void` | — | Rich suggestion callback |
|
|
267
|
+
| `onEvaluation` | `(messageId: string, type: 'like' \| 'dislike', reason?: string) => void` | — | Evaluation callback |
|
|
268
|
+
|
|
269
|
+
### Hook Return Value
|
|
270
|
+
|
|
271
|
+
| Property | Type | Description |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| `isFullPage` | `boolean` | Whether mode is `'fullPage'` |
|
|
274
|
+
| `isExpanded` | `boolean` | Current expansion state |
|
|
275
|
+
| `isMobile` | `boolean` | Whether viewport is mobile |
|
|
276
|
+
| `abaSelecionada` | `AssistantTab` | Currently selected tab |
|
|
277
|
+
| `setAbaSelecionada` | `(tab: AssistantTab) => void` | Change selected tab |
|
|
278
|
+
| `mensagens` | `Message[]` | Current conversation messages |
|
|
279
|
+
| `setMensagens` | `Dispatch<SetStateAction<Message[]>>` | Update messages |
|
|
280
|
+
| `mensagem` | `string` | Current input value |
|
|
281
|
+
| `setMensagem` | `(value: string) => void` | Update input value |
|
|
282
|
+
| `conversas` | `Conversation[]` | All saved conversations |
|
|
283
|
+
| `conversaAtual` | `string \| null` | ID of the active conversation |
|
|
284
|
+
| `conversasFiltradas` | `Conversation[]` | Conversations filtered by active tab |
|
|
285
|
+
| `copiedId` | `string \| null` | ID of the message currently showing "copied" state |
|
|
286
|
+
| `generatingPodcastId` | `string \| null` | ID of the message generating a podcast |
|
|
287
|
+
| `executingCommand` | `string \| null` | ID of the search command being executed |
|
|
288
|
+
| `savedSearches` | `string[]` | IDs of saved search messages |
|
|
289
|
+
| `editingDocument` | `{ content: string; title: string } \| null` | Document being edited |
|
|
290
|
+
| `setEditingDocument` | `(doc \| null) => void` | Open/close document editor |
|
|
291
|
+
| `showMoreSuggestions` | `boolean` | Whether extended suggestions are visible |
|
|
292
|
+
| `setShowMoreSuggestions` | `(show: boolean) => void` | Toggle extended suggestions |
|
|
293
|
+
| `evaluationState` | `EvaluationState` | State of the feedback dialog |
|
|
294
|
+
| `setEvaluationState` | `Dispatch<...>` | Update evaluation state |
|
|
295
|
+
| `sugestoes` | `Suggestion[]` | Resolved suggestions (prop or defaults) |
|
|
296
|
+
| `messagesEndRef` | `RefObject<HTMLDivElement>` | Attach to the scroll anchor at the bottom of the message list |
|
|
297
|
+
| `fileInputRef` | `RefObject<HTMLInputElement>` | Attach to the hidden file input |
|
|
298
|
+
| `audioInputRef` | `RefObject<HTMLInputElement>` | Attach to the hidden audio input |
|
|
299
|
+
| `handleToggle` | `() => void` | Toggle expansion |
|
|
300
|
+
| `handleExpandWithTab` | `(tab: AssistantTab) => void` | Expand and switch to a specific tab |
|
|
301
|
+
| `handleEnviarMensagem` | `(arg?: string \| ActionType) => Promise<void>` | Send a message |
|
|
302
|
+
| `handleToggleFavorite` | `(messageId: string) => void` | Toggle message favorite |
|
|
303
|
+
| `handleCopyMessage` | `(content: string, messageId: string) => Promise<void>` | Copy message to clipboard |
|
|
304
|
+
| `handleGeneratePodcast` | `(messageId: string, content: string) => Promise<void>` | Generate podcast from message |
|
|
305
|
+
| `handleDownloadDocument` | `(content: string, fileName: string) => void` | Download document as `.md` |
|
|
306
|
+
| `handleDownloadPodcast` | `(audioUrl: string, fileName: string) => void` | Download podcast as `.mp3` |
|
|
307
|
+
| `handleEditDocument` | `(content: string, title: string) => void` | Open document editor |
|
|
308
|
+
| `handleNovaConversa` | `() => void` | Start a new conversation |
|
|
309
|
+
| `handleSelecionarConversa` | `(conversaId: string) => void` | Load a saved conversation |
|
|
310
|
+
| `handleToggleFavoritaConversa` | `(conversaId: string) => void` | Toggle conversation favorite |
|
|
311
|
+
| `handleOpenSearchResult` | `(result: SearchResult) => void` | Open a search result |
|
|
312
|
+
| `handleExecuteSearchCommand` | `(commandId, searchTerm, results, messageId) => Promise<void>` | Execute a search command |
|
|
313
|
+
| `handleRichSuggestionClick` | `(suggestion: Suggestion) => void` | Handle rich suggestion click |
|
|
314
|
+
| `handleEvaluationClick` | `(messageId: string, type: 'like' \| 'dislike') => void` | Handle message evaluation |
|
|
315
|
+
| `openFeedbackDialog` | `(messageId: string, category: string \| null) => void` | Open dislike feedback dialog |
|
|
316
|
+
| `handleSubmitDislike` | `() => void` | Submit dislike feedback |
|
|
317
|
+
|
|
318
|
+
### Headless Hook Example
|
|
319
|
+
|
|
320
|
+
```tsx
|
|
321
|
+
import { useAssistant } from 'xertica-ui/assistant';
|
|
322
|
+
|
|
323
|
+
function MyMinimalAssistant() {
|
|
324
|
+
const {
|
|
325
|
+
mensagens,
|
|
326
|
+
mensagem,
|
|
327
|
+
setMensagem,
|
|
328
|
+
handleEnviarMensagem,
|
|
329
|
+
isExpanded,
|
|
330
|
+
handleToggle,
|
|
331
|
+
messagesEndRef,
|
|
332
|
+
} = useAssistant({ demoMode: true });
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<div className="flex flex-col h-full border rounded-lg overflow-hidden">
|
|
336
|
+
{/* Header */}
|
|
337
|
+
<div className="flex items-center justify-between p-3 border-b">
|
|
338
|
+
<span className="font-semibold">AI Assistant</span>
|
|
339
|
+
<button onClick={handleToggle}>
|
|
340
|
+
{isExpanded ? 'Collapse' : 'Expand'}
|
|
341
|
+
</button>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
{/* Messages */}
|
|
345
|
+
<div className="flex-1 overflow-y-auto p-4 space-y-3">
|
|
346
|
+
{mensagens.map(msg => (
|
|
347
|
+
<div
|
|
348
|
+
key={msg.id}
|
|
349
|
+
className={msg.type === 'user' ? 'text-right' : 'text-left'}
|
|
350
|
+
>
|
|
351
|
+
<span className={`inline-block px-3 py-2 rounded-lg text-sm ${
|
|
352
|
+
msg.type === 'user'
|
|
353
|
+
? 'bg-primary text-primary-foreground'
|
|
354
|
+
: 'bg-muted'
|
|
355
|
+
}`}>
|
|
356
|
+
{msg.content}
|
|
357
|
+
</span>
|
|
358
|
+
</div>
|
|
359
|
+
))}
|
|
360
|
+
<div ref={messagesEndRef} />
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
{/* Input */}
|
|
364
|
+
<div className="flex gap-2 p-3 border-t">
|
|
365
|
+
<input
|
|
366
|
+
value={mensagem}
|
|
367
|
+
onChange={e => setMensagem(e.target.value)}
|
|
368
|
+
onKeyDown={e => e.key === 'Enter' && handleEnviarMensagem()}
|
|
369
|
+
placeholder="Type a message..."
|
|
370
|
+
className="flex-1 border rounded px-3 py-2 text-sm"
|
|
371
|
+
/>
|
|
372
|
+
<button
|
|
373
|
+
onClick={() => handleEnviarMensagem()}
|
|
374
|
+
className="px-4 py-2 bg-primary text-primary-foreground rounded text-sm"
|
|
375
|
+
>
|
|
376
|
+
Send
|
|
377
|
+
</button>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
230
386
|
## AI Rules
|
|
231
387
|
|
|
232
388
|
- Use `demoMode={true}` for development without API key
|
|
@@ -236,3 +392,6 @@ Each assistant capability can be individually toggled via boolean props. All fla
|
|
|
236
392
|
- Feature flags are independent and can be combined freely; all default to `true`
|
|
237
393
|
- When `showHistory` or `showFavorites` is `false`, both the collapsed icon and expanded tab are removed
|
|
238
394
|
- When both `showHistory` and `showFavorites` are `false`, the entire tab navigation bar is hidden (including the Chat tab) — there is no point showing a single tab with no alternatives
|
|
395
|
+
- ALWAYS use `useAssistant()` when building a custom assistant UI — never re-implement message state or response generation manually
|
|
396
|
+
- The `messagesEndRef` from the hook MUST be attached to a div at the bottom of the message list for auto-scroll to work
|
|
397
|
+
- `handleEnviarMensagem()` handles both demo mode responses and `responseGenerator` — do not call `responseGenerator` directly
|
|
@@ -72,9 +72,55 @@ The `bar` variant automatically detects the state of the **Sidebar** and **AI As
|
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
75
|
+
## Headless Hook: `useAudioPlayer`
|
|
76
|
+
|
|
77
|
+
For advanced use cases where you need a fully custom player UI, use the `useAudioPlayer` headless hook directly:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { useAudioPlayer } from 'xertica-ui/hooks';
|
|
81
|
+
|
|
82
|
+
function MyCustomPlayer({ src }: { src: string }) {
|
|
83
|
+
const {
|
|
84
|
+
audioRef,
|
|
85
|
+
containerRef,
|
|
86
|
+
isPlaying,
|
|
87
|
+
togglePlay,
|
|
88
|
+
currentTime,
|
|
89
|
+
duration,
|
|
90
|
+
formatTime,
|
|
91
|
+
onPlay,
|
|
92
|
+
onPause,
|
|
93
|
+
onEnded,
|
|
94
|
+
onTimeUpdate,
|
|
95
|
+
onLoadedMetadata,
|
|
96
|
+
} = useAudioPlayer({ src, variant: 'card' });
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div ref={containerRef}>
|
|
100
|
+
<audio
|
|
101
|
+
ref={audioRef}
|
|
102
|
+
src={src}
|
|
103
|
+
onPlay={onPlay}
|
|
104
|
+
onPause={onPause}
|
|
105
|
+
onEnded={onEnded}
|
|
106
|
+
onTimeUpdate={onTimeUpdate}
|
|
107
|
+
onLoadedMetadata={onLoadedMetadata}
|
|
108
|
+
/>
|
|
109
|
+
<button onClick={togglePlay}>{isPlaying ? 'Pause' : 'Play'}</button>
|
|
110
|
+
<span>{formatTime(currentTime)} / {formatTime(duration)}</span>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
See [`hooks.md`](./hooks.md#useaudioplayer) for the complete `useAudioPlayer` API reference.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
75
120
|
## AI Best Practices
|
|
76
121
|
|
|
77
122
|
> [!IMPORTANT]
|
|
78
123
|
> - **Branding Variants**: Use `colorVariant="primary"` for high-relevance or branded content (like Podcasts). Use the default style for utility audios.
|
|
79
124
|
> - **Manual Floating**: In Card mode, users can click the "Maximize" icon to manually force floating mode.
|
|
80
125
|
> - **Accessibility**: All buttons have appropriate ARIA labels. Always provide a descriptive `title` for each audio track.
|
|
126
|
+
> - **Custom UI**: Use `useAudioPlayer` from `xertica-ui/hooks` only when you need a fully custom player interface. For standard playback, always use `<AudioPlayer>` directly.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Branding — Xertica UI
|
|
2
|
+
|
|
3
|
+
The branding system in Xertica UI provides tools for applying, customizing, and switching the visual identity of an application — including color themes, logos, language, and dark/light mode.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Brand Components Overview
|
|
8
|
+
|
|
9
|
+
| Component | Import | Purpose |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `XerticaProvider` | `xertica-ui/brand` | Root provider for all brand, theme, and layout contexts |
|
|
12
|
+
| `ThemeToggle` | `xertica-ui/brand` | Self-contained dark/light mode switcher |
|
|
13
|
+
| `LanguageSelector` | `xertica-ui/brand` | Standalone language dropdown (pt-BR / en / es) |
|
|
14
|
+
| `XerticaLogo` | `xertica-ui/brand` | Full horizontal Xertica logotype SVG |
|
|
15
|
+
| `XerticaXLogo` | `xertica-ui/brand` | Compact X-mark logo variant |
|
|
16
|
+
| `XerticaOrbe` | `xertica-ui/brand` | Animated orb brand mark |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Color Theme System
|
|
21
|
+
|
|
22
|
+
### How It Works
|
|
23
|
+
|
|
24
|
+
Xertica UI uses CSS custom properties (design tokens) for all colors. The theme is applied by injecting token values at `:root` and `.dark`. Tailwind v4 maps these tokens via `@theme inline`.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
tokens.css → :root { --primary: ...; --background: ...; }
|
|
28
|
+
→ .dark { --primary: ...; --background: ...; }
|
|
29
|
+
→ @theme inline { --color-primary: var(--primary); }
|
|
30
|
+
→ Tailwind utilities: bg-primary, text-foreground, etc.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Available Theme Presets
|
|
34
|
+
|
|
35
|
+
The CLI (`npx xertica-ui@latest init` or `npx xertica-ui@latest update-theme`) offers multiple color presets. Each preset defines a complete set of tokens for both light and dark modes.
|
|
36
|
+
|
|
37
|
+
### Customizing Colors Programmatically
|
|
38
|
+
|
|
39
|
+
Use `useBrandColors` to read and modify brand colors at runtime:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useBrandColors } from 'xertica-ui/hooks';
|
|
43
|
+
|
|
44
|
+
function BrandCustomizer() {
|
|
45
|
+
const { colors, setBrandColor } = useBrandColors();
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="space-y-2">
|
|
49
|
+
<label>Primary Color</label>
|
|
50
|
+
<input
|
|
51
|
+
type="color"
|
|
52
|
+
value={colors.primary}
|
|
53
|
+
onChange={e => setBrandColor('primary', e.target.value)}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Changes are applied immediately as CSS variables on `:root`.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Dark / Light Mode
|
|
65
|
+
|
|
66
|
+
### `ThemeToggle`
|
|
67
|
+
|
|
68
|
+
A self-contained button that toggles between light and dark mode. Does not require any context provider — it reads and writes `document.documentElement.classList` directly.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { ThemeToggle } from 'xertica-ui/brand';
|
|
72
|
+
|
|
73
|
+
// Place in Header actions or Sidebar footer
|
|
74
|
+
<ThemeToggle size="sm" />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Props:**
|
|
78
|
+
|
|
79
|
+
| Prop | Type | Default | Description |
|
|
80
|
+
|---|---|---|---|
|
|
81
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size |
|
|
82
|
+
| `className` | `string` | — | Additional CSS classes |
|
|
83
|
+
|
|
84
|
+
### `useTheme` Hook
|
|
85
|
+
|
|
86
|
+
For programmatic theme control:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import { useTheme } from 'xertica-ui/hooks';
|
|
90
|
+
|
|
91
|
+
function ThemeController() {
|
|
92
|
+
const { theme, setTheme, toggleTheme } = useTheme();
|
|
93
|
+
return (
|
|
94
|
+
<button onClick={toggleTheme}>
|
|
95
|
+
Mode: {theme}
|
|
96
|
+
</button>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
> **Note**: `useTheme` requires `<XerticaProvider>` in the tree. `ThemeToggle` works without any provider.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Language Selector
|
|
106
|
+
|
|
107
|
+
### `LanguageSelector`
|
|
108
|
+
|
|
109
|
+
A standalone dropdown for switching the UI language preference.
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
import { LanguageSelector } from 'xertica-ui/brand';
|
|
113
|
+
|
|
114
|
+
// Typically placed in PageHeader or Sidebar footer
|
|
115
|
+
<LanguageSelector />
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The selected language is persisted in `localStorage` under `xertica_language`. Access the current value via `useLanguage()`:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
import { useLanguage } from 'xertica-ui/hooks';
|
|
122
|
+
|
|
123
|
+
function MyComponent() {
|
|
124
|
+
const { language } = useLanguage(); // 'pt-BR' | 'en' | 'es'
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Logo Components
|
|
131
|
+
|
|
132
|
+
### `XerticaLogo`
|
|
133
|
+
|
|
134
|
+
Full horizontal logotype. Use in the Sidebar header or login page.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { XerticaLogo } from 'xertica-ui/brand';
|
|
138
|
+
|
|
139
|
+
<XerticaLogo className="h-8" />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `XerticaXLogo`
|
|
143
|
+
|
|
144
|
+
Compact X-mark variant. Use when space is limited (collapsed sidebar, favicon-like contexts).
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
import { XerticaXLogo } from 'xertica-ui/brand';
|
|
148
|
+
|
|
149
|
+
<XerticaXLogo className="w-8 h-8" />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### `XerticaOrbe`
|
|
153
|
+
|
|
154
|
+
Animated orb brand mark. Use as a decorative element or loading indicator.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { XerticaOrbe } from 'xertica-ui/brand';
|
|
158
|
+
|
|
159
|
+
<XerticaOrbe size={64} />
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## `XerticaProvider`
|
|
165
|
+
|
|
166
|
+
The root provider that initializes all brand, theme, layout, and service contexts. Must wrap the entire application once.
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { XerticaProvider } from 'xertica-ui/brand';
|
|
170
|
+
import 'xertica-ui/style.css';
|
|
171
|
+
|
|
172
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
173
|
+
<React.StrictMode>
|
|
174
|
+
<XerticaProvider>
|
|
175
|
+
<App />
|
|
176
|
+
</XerticaProvider>
|
|
177
|
+
</React.StrictMode>
|
|
178
|
+
);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`XerticaProvider` initializes:
|
|
182
|
+
- `ThemeProvider` — dark/light mode
|
|
183
|
+
- `LanguageProvider` — language preference
|
|
184
|
+
- `BrandColorsProvider` — brand color tokens
|
|
185
|
+
- `LayoutProvider` — sidebar and assistant state
|
|
186
|
+
- `AssistenteProvider` — AI assistant context
|
|
187
|
+
- `ApiKeyProvider` — API key management
|
|
188
|
+
- `GoogleMapsLoaderProvider` — lazy Maps API loading
|
|
189
|
+
- `TooltipProvider` — Radix tooltip context
|
|
190
|
+
- `Toaster` — Sonner toast notifications
|
|
191
|
+
|
|
192
|
+
**Props:**
|
|
193
|
+
|
|
194
|
+
| Prop | Type | Default | Description |
|
|
195
|
+
|---|---|---|---|
|
|
196
|
+
| `children` | `ReactNode` | _(required)_ | Application content |
|
|
197
|
+
| `googleMapsApiKey` | `string` | — | Google Maps API key (activates Maps loader) |
|
|
198
|
+
| `initialApiKey` | `string` | — | Pre-set Xertica API key |
|
|
199
|
+
| `initialGeminiApiKey` | `string` | — | Pre-set Gemini API key |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## CSS Token Structure
|
|
204
|
+
|
|
205
|
+
The complete token set is defined in `tokens.css` (generated by the CLI). Key token groups:
|
|
206
|
+
|
|
207
|
+
```css
|
|
208
|
+
:root {
|
|
209
|
+
/* Colors */
|
|
210
|
+
--background: 0 0% 100%;
|
|
211
|
+
--foreground: 222.2 84% 4.9%;
|
|
212
|
+
--primary: 221.2 83.2% 53.3%;
|
|
213
|
+
--primary-foreground: 210 40% 98%;
|
|
214
|
+
--secondary: 210 40% 96.1%;
|
|
215
|
+
--muted: 210 40% 96.1%;
|
|
216
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
217
|
+
--accent: 210 40% 96.1%;
|
|
218
|
+
--destructive: 0 84.2% 60.2%;
|
|
219
|
+
--border: 214.3 31.8% 91.4%;
|
|
220
|
+
--input: 214.3 31.8% 91.4%;
|
|
221
|
+
--ring: 221.2 83.2% 53.3%;
|
|
222
|
+
--card: 0 0% 100%;
|
|
223
|
+
--card-foreground: 222.2 84% 4.9%;
|
|
224
|
+
|
|
225
|
+
/* Chart colors */
|
|
226
|
+
--chart-1: 221.2 83.2% 53.3%;
|
|
227
|
+
--chart-2: 142.1 76.2% 36.3%;
|
|
228
|
+
--chart-3: 47.9 95.8% 53.1%;
|
|
229
|
+
--chart-4: 280.1 65.3% 60%;
|
|
230
|
+
--chart-5: 24.6 95% 53.1%;
|
|
231
|
+
|
|
232
|
+
/* Sidebar */
|
|
233
|
+
--sidebar: 222.2 84% 4.9%;
|
|
234
|
+
--sidebar-foreground: 210 40% 98%;
|
|
235
|
+
|
|
236
|
+
/* Shape */
|
|
237
|
+
--radius: 0.5rem;
|
|
238
|
+
--radius-button: 0.375rem;
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## AI Rules
|
|
245
|
+
|
|
246
|
+
> [!IMPORTANT]
|
|
247
|
+
> - **`XerticaProvider` is required once**: Place it at the root of your application. Never nest multiple `XerticaProvider` instances.
|
|
248
|
+
> - **Never use raw hex/rgb colors**: All colors must use semantic tokens (`bg-primary`, `text-destructive`, `border-border`). Raw values like `#3b82f6` or `rgb(59, 130, 246)` are forbidden.
|
|
249
|
+
> - **`ThemeToggle` is self-contained**: It does not need `useTheme` or any context. Use it directly in any component without provider requirements.
|
|
250
|
+
> - **Logo sizing via `className`**: Size logo components using Tailwind height/width classes (`h-8`, `w-8`). Do not use `width`/`height` HTML attributes.
|
|
251
|
+
> - **Language is preference only**: `LanguageSelector` stores a preference. The library UI renders in Portuguese regardless. Implement your own i18n logic using the `language` value from `useLanguage()`.
|