# Xertica UI - Technical Documentation (Consolidated) > This file contains the full technical specification for all 85 components of Xertica UI. > It is designed to be read by AI agents to provide a single source of truth context. --- START COMPONENT: ACCORDION --- # Accordion ## Overview A vertically stacked set of collapsible sections. Each section has a clickable trigger header that reveals or hides its content. Ideal for FAQs, configuration settings, and dense navigation trees where only one (or a few) section needs to be visible at a time. --- ## When to Use - FAQ sections - Settings panels with multiple categories - Navigable tree structures inside a sidebar - Reducing visual density by hiding supplementary information ## When NOT to Use - Switching between views — use `` instead. - A single expandable section — use `` instead. --- ## Anatomy ``` Question or Title Content that is revealed ``` --- ## Props ### Accordion | Prop | Type | Default | Required | Description | | --------------- | ------------------------ | ------- | -------- | --------------------------------------------------------------------- | | `type` | `'single' \| 'multiple'` | — | **Yes** | Single: one open at a time. Multiple: all can be open simultaneously. | | `collapsible` | `boolean` | `false` | No | When `type="single"`, allows closing the open item by re-clicking. | | `defaultValue` | `string \| string[]` | — | No | Initially open item(s) | | `value` | `string \| string[]` | — | No | Controlled open item(s) | | `onValueChange` | `(v) => void` | — | No | Change handler | ### AccordionItem | Prop | Type | Required | Description | | ------- | -------- | -------- | ------------------------------- | | `value` | `string` | **Yes** | Unique identifier for this item | --- ## Examples ### FAQ (Single, Collapsible) ```tsx import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from 'xertica-ui/ui'; Is this library accessible? Yes. Built entirely on Radix UI with full ARIA support and keyboard navigation. How do I apply custom styles? Override the `className` on triggers or content. Classes are merged via `tailwind-merge`. ; ``` ### Multiple Open ```tsx Notification Settings {/* switches */} Privacy Settings {/* options */} ``` --- ## AI Rules - `AccordionItem` **requires** a unique non-empty `value` string — without it, open/close tracking breaks. - The animated `ChevronDown` icon is **already built into** `AccordionTrigger`. Do not add another chevron icon. - `type` is **required** — never omit it. - Use `type="single" collapsible` for FAQ-style lists. Use `type="multiple"` for settings panels where users may need multiple sections open. - Do not use Accordion for switching primary views — use `Tabs` for that. --- ## Related Components - [`Collapsible`](./collapsible.md) — For a single expandable section - [`Tabs`](./tabs.md) — For view switching --- END COMPONENT: ACCORDION --- --- START COMPONENT: ALERT-DIALOG --- # AlertDialog ## Overview A specialized modal for **irreversible destructive actions** — delete, revoke, reset, permanently disable. Unlike `Dialog`, `AlertDialog` enforces that the user must explicitly confirm or cancel; clicking outside does not dismiss it. --- ## When to Use - Deleting a record - Revoking access or permissions - Permanently disabling or deactivating an entity - Any action that cannot be undone ## When NOT to Use - Non-destructive confirmations — use `Dialog` instead. - Quick succinct confirmations that don't require explanation — use `toast` with an undo option instead. --- ## Anatomy ``` ← always first (left) ← destructive action (right) ``` --- ## Props ### AlertDialog | Prop | Type | Description | | -------------- | ------------------------- | --------------------- | | `open` | `boolean` | Controlled open state | | `onOpenChange` | `(open: boolean) => void` | State handler | ### AlertDialogTrigger | Prop | Type | Description | | --------- | --------- | ------------------------ | | `asChild` | `boolean` | Renders as child element | ### AlertDialogAction | Prop | Type | Description | | ----------- | ------------ | ----------------------------------------------- | | `className` | `string` | Style the action button (typically destructive) | | `onClick` | `() => void` | Action to execute on confirm | --- ## Examples ### Delete Confirmation ```tsx import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, Button, } from 'xertica-ui/ui'; Are you absolutely sure? This action cannot be undone. This will permanently delete your account and remove all associated data. Cancel Delete ; ``` --- ## AI Rules - `AlertDialogTitle` is **required** — omitting it will trigger an accessibility error. - `AlertDialogDescription` is **required** — it explains what will happen and cannot be undone. - `AlertDialogCancel` must appear **before** `AlertDialogAction` in the footer — left to right: Cancel → Confirm. - Style the `AlertDialogAction` with `className="bg-destructive text-destructive-foreground hover:bg-destructive/90"` for destructive actions. - Clicking outside the dialog does NOT dismiss it — this is intentional to prevent accidental dismissal during destructive operations. - Never omit the Cancel option — users must always have a safe exit. --- ## Related Components - [`Dialog`](./dialog.md) — For non-destructive modals - [`Button`](./button.md) — The trigger button - [`Sonner`](./sonner.md) — For toast confirmations with undo --- END COMPONENT: ALERT-DIALOG --- --- START COMPONENT: ALERT --- # Alert ## Overview An inline message banner used to communicate status, feedback, or important contextual information directly within the page content — without interrupting the user flow. Unlike `Sonner` (toast notifications), alerts are persistent and visible at all times. **Optimization**: The Xertica UI `Alert` component automatically renders a semantic icon based on the chosen `variant`. You do not need to add icons manually unless you want to override the default. --- ## When to Use - Inline warnings about data state ("This record is read-only") - Information banners about required actions ("Please verify your email") - Success confirmation areas within a form or page section - Error messages from API responses or forms --- ## Variants | Variant | Color | Default Icon | Usage | | ------------- | ------------ | ------------- | ----------------------------- | | `default` | Neutral | Info | General information | | `info` | Blue accent | Info | Informational context | | `success` | Green accent | CheckCircle | Confirmation or success state | | `warning` | Amber accent | AlertTriangle | Cautionary information | | `destructive` | Red | XCircle | Errors, critical warnings | --- ## Anatomy ```tsx Title Detailed message here. ``` --- ## Props ### Alert | Prop | Type | Default | Description | | ----------- | ---------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- | | `variant` | `'default' \| 'info' \| 'success' \| 'warning' \| 'destructive'` | `'default'` | Visual style and automatic icon | | `icon` | `ReactNode` | — | Overrides the default semantic icon. Set to `null` to hide the icon area. | | `className` | `string` | — | Additional CSS classes | --- ## Examples ### Informational (Automatic Icon) ```tsx import { Alert, AlertDescription, AlertTitle } from 'xertica-ui/ui'; Note This data is refreshed every 24 hours. Last updated: today at 08:00. ; ``` ### Warning ```tsx Attention Required Your subscription expires in 3 days. Renew to avoid service interruption. ``` ### Custom Icon ```tsx import { Terminal } from 'lucide-react'; }> System Prompt Executing automated deployment script... ; ``` --- ## AI Rules - **Do not manually add icons inside ``** — let the component handle them via `variant` or `icon` prop. - Always include an `AlertTitle` for clear information hierarchy. - Use `destructive` for errors and critical failure states. - Place alerts close to the relevant content (e.g., top of a form or below a header). --- ## Related Components - [`Sonner`](./sonner.md) — For transient toast notifications - [`AlertDialog`](./alert-dialog.md) — For destructive confirmation modals --- END COMPONENT: ALERT --- --- START COMPONENT: ASPECT-RATIO --- # AspectRatio ## Overview A container that maintains a fixed width-to-height ratio regardless of the available space. Prevents layout shifts when loading images, videos, or charts. Use when content must always fill a specific shape. --- ## When to Use - Image thumbnails or banners that must maintain 16:9, 4:3, or 1:1 ratios - Video embeds - Map or chart containers with fixed proportions --- ## Props | Prop | Type | Default | Description | | ----------- | -------- | ------- | ------------------------------------------------ | | `ratio` | `number` | `1` | Width/height ratio (e.g., `16/9` for widescreen) | | `className` | `string` | — | Applied to the container | --- ## Examples ### Image with 16:9 Ratio ```tsx import { AspectRatio } from 'xertica-ui/ui';
Hero
; ``` ### Square Avatar Banner ```tsx ``` --- ## AI Rules - The outer container must have a defined width — `AspectRatio` sets the height automatically based on `ratio`. - The child content must use `w-full h-full` to fill the ratio container. - Never use `` without an explicit outer width constraint. --- END COMPONENT: ASPECT-RATIO --- --- START COMPONENT: ASSISTANT-CHART --- # AssistantChart A specialized data visualization component optimized for display within the AI Assistant panel. It provides a pre-configured responsive BarChart using the library's design system tokens. --- ## Import ```tsx import { AssistantChart } from 'xertica-ui/ui'; ``` --- ## Usage ```tsx const chartData = [ { month: 'January', desktop: 186, mobile: 80 }, { month: 'February', desktop: 305, mobile: 200 }, ]; const chartConfig = { desktop: { label: 'Desktop', color: 'var(--chart-1)' }, mobile: { label: 'Mobile', color: 'var(--chart-2)' }, }; ; ``` --- ## Props | Prop | Type | Default | Description | | -------- | ------------- | ------------ | ------------------------------------------------ | | `data` | `any[]` | _(required)_ | The array of objects containing chart values. | | `config` | `ChartConfig` | _(required)_ | Mapping of data keys to labels and color tokens. | --- ## AI Rules > [!IMPORTANT] > > - **Data Format**: Your data objects can use **any key names** — pass them via `chartData` and reference them in `chartConfig`. The keys `month`, `desktop`, and `mobile` are only examples from the Storybook demo; they are not required. > - **Container Size**: This component is optimized for the narrow width of the AI Assistant sidebar (`~400px`). It uses `min-h-[200px]` to maintain readability. --- END COMPONENT: ASSISTANT-CHART --- --- START COMPONENT: ASSISTANT --- # XerticaAssistant --- ## Overview Full-featured AI chat assistant with support for: - Chat conversations with history and favorites - Rich message rendering (Markdown, code, tables, charts, documents) - Audio/podcast generation from text - Document editing workspace - Three layout modes: `collapsed`, `expanded`, `fullPage` - Demo mode (mock AI responses without API key) The component ships in **two usage patterns**: | Pattern | When to use | | ---------------------- | ---------------------------------------------------------------------------- | | `` | Drop-in assistant panel — zero configuration needed | | `useAssistant()` | Headless hook — bring your own UI while reusing all state and business logic | > **CLI scaffold:** When running `npx xertica-ui@latest init`, the assistant is included by default. To opt out during init, answer `no` at the "Include AI Assistant?" prompt. To add or remove it later from an existing project, run `npx xertica-ui update` → **Assistant**. The CLI will copy or delete `src/features/assistant/` and `src/pages/AssistantPage.tsx`, and regenerate `AuthGuard.tsx`, `HomePage.tsx`, and `TemplatePage.tsx` accordingly. --- ## Props | Prop | Type | Default | Required | Description | | ------------------------- | ------------------------------------------------------------------------- | ------------ | ------------------------------------------- | ------------------------------------------------------------ | | `mode` | `AssistantMode` | `'expanded'` | No | Layout mode: `'collapsed'`, `'expanded'`, `'fullPage'` | | `isExpanded` | `boolean` | `true` | No | Whether panel is expanded (mode='expanded' only) | | `onToggle` | `() => void` | — | No | Callback when panel is toggled | | `defaultTab` | `'chat' \| 'historico' \| 'favoritos'` | `'chat'` | No | Initially selected tab | | `demoMode` | `boolean` | `true` | No | Enable demo mode (mock AI responses) | | `customResponses` | `MockResponse[]` | — | No | Custom mock responses for demo mode | | `onNavigateSettings` | `() => void` | — | No | Navigate to settings page | | `onNavigateFullPage` | `() => void` | — | No | Navigate to full-page view | | `userName` | `string` | `'Usuário'` | No | Display name of logged-in user | | `initialMessages` | `Message[]` | — | Pre-loaded messages to hydrate conversation | | `savedConversations` | `Conversation[]` | — | Previously saved conversations | | `suggestions` | `Suggestion[]` | — | Suggested prompts shown in empty state | | `onSendMessage` | `(message: string) => void` | — | Callback when user sends message | | `onFileAttach` | `(file: File) => void` | — | Callback when user attaches file | | `isProcessing` | `boolean` | `false` | No | Shows typing indicator | | `width` | `string` | — | No | Custom panel width (expanded mode) | | `height` | `string` | — | No | Custom panel height (fullPage mode) | | `className` | `string` | — | No | Additional CSS classes | | `mobileFloating` | `boolean` | — | No | Show as floating button on mobile | | `responseGenerator` | `(message: string) => Promise>` | — | No | Custom response generator function | | `richSuggestions` | `Suggestion[]` | — | No | Extended suggestions (shown after clicking "Mais sugestões") | | `onRichAction` | `(actionId: string, actionText: string) => void` | — | No | Callback when user clicks rich suggestion | | `onEvaluation` | `(messageId: string, type: 'like' \| 'dislike', reason?: string) => void` | — | No | Callback when user rates a message | | `feedbackOptions` | `string[]` | — | No | Negative feedback categories | | `showHistory` | `boolean` | `true` | No | Show conversation history tab and collapsed icon | | `showFavorites` | `boolean` | `true` | No | Show favorites tab and collapsed icon | | `enableAudioInput` | `boolean` | `true` | No | Enable voice recording / audio upload | | `enableFileAttachment` | `boolean` | `true` | No | Enable file attachment button | | `enableDocumentCreation` | `boolean` | `true` | No | Enable "Create document" action in chat input | | `enablePodcastGeneration` | `boolean` | `true` | No | Enable podcast generation (action chip + per-message button) | | `enableSearch` | `boolean` | `true` | No | Enable "Search" action in chat input | --- ## Types ```typescript type AssistantMode = 'collapsed' | 'expanded' | 'fullPage'; type AssistantTab = 'chat' | 'historico' | 'favoritos'; type MessageType = 'user' | 'assistant' | 'system'; type AttachmentType = 'file' | 'audio' | 'image' | 'video' | 'document' | 'podcast' | 'search'; type SearchResultType = 'document' | 'project' | 'conversation' | 'file' | 'contact'; interface Message { id: string; type: MessageType; content: string; timestamp: Date; isFavorite?: boolean; attachmentType?: AttachmentType; attachmentName?: string; documentContent?: string; documentTitle?: string; audioUrl?: string; searchResults?: SearchResult[]; chartData?: any[]; chartConfig?: any; tableData?: { caption?: string; headers: string[]; rows: (string | React.ReactNode)[][] }; evaluation?: 'like' | 'dislike'; evaluationReason?: string; } interface Conversation { id: string; title: string; lastMessage?: string; timestamp: string; isFavorite: boolean; messages: Message[]; } interface Suggestion { id: string; text: string; icon?: React.ReactNode; } interface SearchResult { id: string; type: SearchResultType; title: string; description: string; path: string; relevance: number; lastModified?: string; } ``` --- ## Layout Modes ### Expanded (default) Resizable side panel (420px default) that sits flush against the right side of the screen. ### FullPage Dedicated full-page view with centered content container (`max-w-6xl`). ### Collapsed Icon-only sidebar strip with tooltip tooltips. --- ## Mobile Behavior - Desktop: Sits flush against right side, mutually exclusive with `` - Mobile: Transitions to floating Action Button (bottom-right), expands to full-screen overlay when clicked --- ## Document Editor Mode When the assistant generates a full document, a `RichTextEditor` slides precisely from the right border overlaying the page. --- ## Examples ### Basic Integration (Demo Mode) ```tsx import { XerticaAssistant } from 'xertica-ui/assistant'; ; ``` ### With Real API ```tsx import { XerticaAssistant } from 'xertica-ui/assistant'; console.log(msg)} responseGenerator={async msg => { const response = await callGeminiAPI(apiKey, msg); return response; }} />; ``` ### FullPage Mode ```tsx ``` --- ## Demo Mode Responses The component includes built-in mock responses for common requests: - Document creation: Generates markdown documents - Search: Returns mock search results - Summary: Generates summary tables - Podcast: Generates audio attachments - Charts: Renders data charts Use `customResponses` to override defaults. --- ## Feature Flags Each assistant capability can be individually toggled via boolean props. All flags default to `true`. ### Chat Only (no extras) ```tsx ``` ### Custom Configuration ```tsx // Only document creation and file attachment ``` --- ## Headless Hook — `useAssistant()` 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. ### Import ```tsx import { useAssistant } from 'xertica-ui/assistant'; ``` ### Hook Props The hook accepts the same props as `XerticaAssistantProps` (minus purely visual props like `width`, `height`, `className`, `mobileFloating`, `onNavigateSettings`, `onNavigateFullPage`, `userName`, and feature flags). | Prop | Type | Default | Description | | -------------------- | ------------------------------------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------- | | `mode` | `AssistantMode` | `'expanded'` | Layout mode | | `isExpanded` | `boolean` | `true` | Controlled expansion state | | `onToggle` | `() => void` | — | Toggle callback | | `defaultTab` | `AssistantTab` | `'chat'` | Initially selected tab | | `demoMode` | `boolean` | `true` | Enable demo mode | | `customResponses` | `MockResponse[]` | `[]` | Custom mock responses | | `initialMessages` | `Message[]` | `[]` | Pre-loaded messages — hydrated **once** on mount (one-shot). Subsequent changes to this prop do not reset the conversation | | `savedConversations` | `Conversation[]` | `[]` | Previously saved conversations | | `suggestions` | `Suggestion[]` | — | Suggested prompts | | `onSendMessage` | `(message: string) => void` | — | Send message callback | | `isProcessing` | `boolean` | `false` | Shows typing indicator | | `responseGenerator` | `(message: string) => Promise>` | — | Custom response generator | | `richSuggestions` | `Suggestion[]` | `[]` | Extended suggestions | | `onRichAction` | `(actionId: string, actionText: string) => void` | — | Rich suggestion callback | | `onEvaluation` | `(messageId: string, type: 'like' \| 'dislike', reason?: string) => void` | — | Evaluation callback | ### Hook Return Value | Property | Type | Description | | ------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------------- | | `isFullPage` | `boolean` | Whether mode is `'fullPage'` | | `isExpanded` | `boolean` | Current expansion state | | `isMobile` | `boolean` | Whether viewport is mobile | | `abaSelecionada` | `AssistantTab` | Currently selected tab | | `setAbaSelecionada` | `(tab: AssistantTab) => void` | Change selected tab | | `mensagens` | `Message[]` | Current conversation messages | | `setMensagens` | `Dispatch>` | Update messages | | `mensagem` | `string` | Current input value | | `setMensagem` | `(value: string) => void` | Update input value | | `conversas` | `Conversation[]` | All saved conversations | | `conversaAtual` | `string \| null` | ID of the active conversation | | `conversasFiltradas` | `Conversation[]` | Conversations filtered by active tab | | `copiedId` | `string \| null` | ID of the message currently showing "copied" state | | `generatingPodcastId` | `string \| null` | ID of the message generating a podcast | | `executingCommand` | `string \| null` | ID of the search command being executed | | `savedSearches` | `string[]` | IDs of saved search messages | | `editingDocument` | `{ content: string; title: string } \| null` | Document being edited | | `setEditingDocument` | `(doc \| null) => void` | Open/close document editor | | `showMoreSuggestions` | `boolean` | Whether extended suggestions are visible | | `setShowMoreSuggestions` | `(show: boolean) => void` | Toggle extended suggestions | | `evaluationState` | `EvaluationState` | State of the feedback dialog | | `setEvaluationState` | `Dispatch<...>` | Update evaluation state | | `sugestoes` | `Suggestion[]` | Resolved suggestions (prop or defaults) | | `messagesEndRef` | `RefObject` | Attach to the scroll anchor at the bottom of the message list | | `fileInputRef` | `RefObject` | Attach to the hidden file input | | `audioInputRef` | `RefObject` | Attach to the hidden audio input | | `handleToggle` | `() => void` | Toggle expansion | | `handleExpandWithTab` | `(tab: AssistantTab) => void` | Expand and switch to a specific tab | | `handleEnviarMensagem` | `(arg?: string \| ActionType) => Promise` | Send a message | | `handleToggleFavorite` | `(messageId: string) => void` | Toggle message favorite | | `handleCopyMessage` | `(content: string, messageId: string) => Promise` | Copy message to clipboard | | `handleGeneratePodcast` | `(messageId: string, content: string) => Promise` | Generate podcast from message | | `handleDownloadDocument` | `(content: string, fileName: string) => void` | Download document as `.md` | | `handleDownloadPodcast` | `(audioUrl: string, fileName: string) => void` | Download podcast as `.mp3` | | `handleEditDocument` | `(content: string, title: string) => void` | Open document editor | | `handleNovaConversa` | `() => void` | Start a new conversation | | `handleSelecionarConversa` | `(conversaId: string) => void` | Load a saved conversation | | `handleToggleFavoritaConversa` | `(conversaId: string) => void` | Toggle conversation favorite | | `handleOpenSearchResult` | `(result: SearchResult) => void` | Open a search result | | `handleExecuteSearchCommand` | `(commandId, searchTerm, results, messageId) => Promise` | Execute a search command | | `handleRichSuggestionClick` | `(suggestion: Suggestion) => void` | Handle rich suggestion click | | `handleEvaluationClick` | `(messageId: string, type: 'like' \| 'dislike') => void` | Handle message evaluation | | `openFeedbackDialog` | `(messageId: string, category: string \| null) => void` | Open dislike feedback dialog | | `handleSubmitDislike` | `() => void` | Submit dislike feedback | ### Headless Hook Example ```tsx import { useAssistant } from 'xertica-ui/assistant'; function MyMinimalAssistant() { const { mensagens, mensagem, setMensagem, handleEnviarMensagem, isExpanded, handleToggle, messagesEndRef, } = useAssistant({ demoMode: true }); return (
{/* Header */}
AI Assistant
{/* Messages */}
{mensagens.map(msg => (
{msg.content}
))}
{/* Input */}
setMensagem(e.target.value)} onKeyDown={e => e.key === 'Enter' && handleEnviarMensagem()} placeholder="Type a message..." className="flex-1 border rounded px-3 py-2 text-sm" />
); } ``` --- ## AI Rules - Use `demoMode={true}` for development without API key - Use `responseGenerator` for custom AI integration - In fullPage mode, welcome message shows "Olá, {userName}" (not "Assistente Xertica") - Tab switching (chat/historico/favoritos) hides navigation tabs in fullPage mode - Feature flags are independent and can be combined freely; all default to `true` - When `showHistory` or `showFavorites` is `false`, both the collapsed icon and expanded tab are removed - 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 - ALWAYS use `useAssistant()` when building a custom assistant UI — never re-implement message state or response generation manually - The `messagesEndRef` from the hook MUST be attached to a div at the bottom of the message list for auto-scroll to work - `handleEnviarMensagem()` handles both demo mode responses and `responseGenerator` — do not call `responseGenerator` directly - `handleEnviarMensagem` accepts `arg?: string | ActionType` — pass a string to send a specific message, pass an `ActionType` (`'document'`, `'podcast'`, `'search'`) to trigger an action, or pass nothing to send the current `mensagem` input value - `initialMessages` is hydrated **once** (one-shot) — passing a new array after mount does not reset the conversation. To reset, call `setMensagens([])` directly - Auto-scroll is conditional: `messagesEndRef.scrollIntoView` is only called when the user is within 120px of the bottom of the chat container — preserves scroll position during history review - **`suggestions`, `richSuggestions`, and `feedbackOptions` should come from `useAssistantConfig()` (React Query)** — never hardcode them in the page component. Use the fallback factory functions from `features/assistant/data/mock.ts` while the query loads ## Dynamic Configuration with React Query In projects using the `features/` architecture, assistant configuration (suggestions, feedback options) is fetched via `useAssistantConfig()`: ```tsx import { useAssistantConfig, getMockRichSuggestions, getMockFeedbackOptions } from '../features/assistant'; function MyPage() { const { data: assistantConfig } = useAssistantConfig(); return ( ); } ``` To connect to a real configuration API, replace `fetchAssistantConfig` in `features/assistant/data/mock.ts`: ```ts // features/assistant/data/mock.ts export async function fetchAssistantConfig(): Promise { // Replace with real endpoint: return fetch('/api/assistant/config').then(r => r.json()); } ``` --- END COMPONENT: ASSISTANT --- --- START COMPONENT: AUDIO-PLAYER --- # AudioPlayer A unified, premium media player designed for both quick audio previews and long-form podcasts. It supports embedded (Card) and global (Bar) layouts, featuring fluid transitions and intelligent responsiveness. --- ## Import ```tsx import { AudioPlayer } from 'xertica-ui/media'; ``` --- ## Basic Usage ### Embedded with Auto-Float (Card) Ideal for in-page content. The player automatically "floats" if the user scrolls away while listening. ```tsx ``` ### Global Bar (Bar) Ideal for continuous playback across the entire application. ```tsx setOpen(false)} /> ``` --- ## Props | Prop | Type | Default | Description | | ----------------- | ------------------------ | ----------- | ------------------------------------------------------------- | | `variant` | `'card' \| 'bar'` | `'card'` | Determines if the player is an embedded card or a fixed bar. | | `colorVariant` | `'default' \| 'primary'` | `'default'` | Visual style (`primary` is ideal for brand-focused podcasts). | | `isOpen` | `boolean` | `true` | (Bar only) Whether the player bar is open or closed. | | `enableAutoFloat` | `boolean` | `true` | (Card only) Whether to float when scrolling out of viewport. | | `title` | `string` | — | Track title. | | `artist` | `string` | — | Author or subtitle. | | `src` | `string` | — | Audio file URL. | | `duration` | `number` | — | Total duration in seconds. | --- ## Features and Behaviors ### Dynamic Responsiveness The `AudioPlayer` doesn't just hide elements—it **reallocates** them. Depending on available screen space: - **Large Screens (Desktop)**: All controls (volume, speed, download) are visible on the bar. - **Medium/Small Screens**: Controls like volume and speed automatically move to the secondary options menu (`...`) to prevent visual clutter. ### Playback Speed Supports speed adjustments: `0.5x`, `1x`, `1.5x`, and `2x`. The control adapts dynamically to the screen width. ### Layout Integration The `bar` variant automatically detects the state of the **Sidebar** and **AI Assistant** via `LayoutContext`, adjusting its margins to ensure it never covers vital system elements. --- ## Headless Hook: `useAudioPlayer` For advanced use cases where you need a fully custom player UI, use the `useAudioPlayer` headless hook directly: ```tsx import { useAudioPlayer } from 'xertica-ui/hooks'; function MyCustomPlayer({ src }: { src: string }) { const { audioRef, containerRef, isPlaying, togglePlay, currentTime, duration, formatTime, onPlay, onPause, onEnded, onTimeUpdate, onLoadedMetadata, } = useAudioPlayer({ src, variant: 'card' }); return (
); } ``` See [`hooks.md`](./hooks.md#useaudioplayer) for the complete `useAudioPlayer` API reference. --- ## Implementation Notes (v2.1.9+) ### Latest-Ref Pattern in mode-switch effect The hook uses the **latest-ref pattern** to avoid stale closure problems when switching between floating and embedded modes: ```ts const currentTimeRef = useRef(currentTime); const isPlayingRef = useRef(isPlaying); useEffect(() => { currentTimeRef.current = currentTime; }, [currentTime]); useEffect(() => { isPlayingRef.current = isPlaying; }, [isPlaying]); // mode-switch effect — reads from refs, not from state closure useEffect(() => { const audio = audioRef.current; if (audio) { if (Math.abs(audio.currentTime - currentTimeRef.current) > 0.5) audio.currentTime = currentTimeRef.current; if (isPlayingRef.current) audio.play().catch(err => console.log('Playback interrupted:', err)); } }, [isFloating, variant]); ``` This eliminates the need for the `eslint-disable-next-line react-hooks/exhaustive-deps` comment that previously suppressed the stale-closure warning. When building a custom player with `useAudioPlayer`, you do not need to replicate this pattern — it is handled internally. --- ## AI Best Practices > [!IMPORTANT] > > - **Branding Variants**: Use `colorVariant="primary"` for high-relevance or branded content (like Podcasts). Use the default style for utility audios. > - **Manual Floating**: In Card mode, users can click the "Maximize" icon to manually force floating mode. > - **Accessibility**: All buttons have appropriate ARIA labels. Always provide a descriptive `title` for each audio track. > - **Custom UI**: Use `useAudioPlayer` from `xertica-ui/hooks` only when you need a fully custom player interface. For standard playback, always use `` directly. --- END COMPONENT: AUDIO-PLAYER --- --- START COMPONENT: AVATAR --- # Avatar ## Overview Displays a user's identity visually — either as a profile photo or as an initials-based fallback. Used in headers, sidebars, comment threads, and table rows to represent people. --- ## When to Use - User profile representation in headers and sidebars - Author attribution in activity feeds and comment lists - List items that represent people or organizations --- ## Anatomy ``` ← Actual photo ← Shown when image fails or hasn't loaded ``` --- ## Props ### Avatar | Prop | Type | Default | Required | Description | | ----------- | -------- | ------- | -------- | ------------------------------------------------------------ | | `className` | `string` | — | No | Size and shape overrides (default: `h-10 w-10 rounded-full`) | ### AvatarImage | Prop | Type | Required | Description | | ----- | -------- | -------- | --------------------------- | | `src` | `string` | No | Image URL | | `alt` | `string` | No | Accessible alternative text | ### AvatarFallback | Prop | Type | Description | | ----------- | ----------- | -------------------------------------- | | `className` | `string` | Style the fallback background and text | | `children` | `ReactNode` | Initials string or fallback icon | | `delayMs` | `number` | Delay before fallback appears (ms) | --- ## Examples ### With Image + Fallback Initials ```tsx import { Avatar, AvatarImage, AvatarFallback } from 'xertica-ui/ui'; JD ; ``` ### Small (in table rows) ```tsx {user.name.charAt(0).toUpperCase()} ``` ### With Icon Fallback ```tsx import { User } from 'lucide-react'; ; ``` --- ## AI Rules - Always include `` — never render `` alone. If the image fails to load, the component will be blank without a fallback. - The fallback should be initials (`user.name.charAt(0)`) or a `lucide-react` user icon — never an external image. - Size the Avatar using `className="h-8 w-8"` (small) or `className="h-10 w-10"` (default). Do not use Tailwind `w-` and `h-` classes that don't correspond to standard rem multiples. --- ## Related Components - [`Header`](./header.md) — Avatar is used in the user profile dropdown - [`Sidebar`](./sidebar.md) — Avatar in the sidebar footer --- END COMPONENT: AVATAR --- --- START COMPONENT: BADGE --- # Badge ## Overview A compact, inline label used to communicate status, category, or count. Badges are always non-interactive by default — they display information, not trigger actions. --- ## When to Use - Status indicators: "Active", "Inactive", "Pending", "Error" - Category tags in lists and tables - Count indicators (notifications, pending items) - Feature flags or role labels ## When NOT to Use - For clickable filters — use `Toggle` or `Button` instead. - For destructive inline actions — use a `Button variant="destructive"`. --- ## Variants | Variant | Usage | | ------------- | ----------------------------------------- | | `default` | Filled primary color — main labels | | `secondary` | Muted fill — secondary or neutral labels | | `outline` | Bordered, transparent — subtle status | | `destructive` | Red — error or danger status | | `success` | Soft green — positive or completed status | | `info` | Soft blue — informational status | | `warning` | Soft amber — caution or pending status | --- ## Props | Prop | Type | Default | Required | Description | | ----------- | -------------------------------------------------------------------------------------------- | ----------- | -------- | ---------------------------- | | `variant` | `'default' \| 'secondary' \| 'outline' \| 'destructive' \| 'success' \| 'info' \| 'warning'` | `'default'` | No | Visual style | | `asChild` | `boolean` | `false` | No | Renders as the child element | | `className` | `string` | — | No | Additional CSS classes | --- ## Examples ```tsx import { Badge } from 'xertica-ui/ui'; Active Draft Pending Error Completed Info Caution ``` ### In Table Cells ```tsx Active ``` --- ## AI Rules - Status labels in tables and lists must ALWAYS use `` — never plain text or colored ``. - Use semantic variants to convey meaning: `success` for completed/positive, `info` for informational, `warning` for caution, `destructive` for errors. - Never invent a custom variant by styling a badge with raw colors. Use only the 7 documented variants. - When you need a custom color label (e.g., brand-specific), apply `className` with semantic token classes: `className="bg-primary/10 text-primary border-primary/20"`. --- ## Related Components - [`Button`](./button.md) — When the label needs to be clickable - [`NotificationBadge`](./notification-badge.md) — For overlay dot/count indicators on icons - [`Table`](./table.md) — Common context for Badge usage --- END COMPONENT: BADGE --- --- START COMPONENT: BRANDING --- # Branding — Xertica UI 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. --- ## Brand Components Overview | Component | Import | Purpose | | ------------------ | ------------------ | ------------------------------------------------------- | | `XerticaProvider` | `xertica-ui/brand` | Root provider for all brand, theme, and layout contexts | | `ThemeToggle` | `xertica-ui/brand` | Self-contained dark/light mode switcher | | `LanguageSelector` | `xertica-ui/brand` | Standalone language dropdown (pt-BR / en / es) | | `XerticaLogo` | `xertica-ui/brand` | Full horizontal Xertica logotype SVG | | `XerticaXLogo` | `xertica-ui/brand` | Compact X-mark logo variant | | `XerticaOrbe` | `xertica-ui/brand` | Animated orb brand mark | --- ## Color Theme System ### How It Works 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`. ``` tokens.css → :root { --primary: ...; --background: ...; } → .dark { --primary: ...; --background: ...; } → @theme inline { --color-primary: var(--primary); } → Tailwind utilities: bg-primary, text-foreground, etc. ``` ### Available Theme Presets 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. ### Customizing Colors Programmatically Use `useBrandColors` to read and modify brand colors at runtime: ```tsx import { useBrandColors } from 'xertica-ui/hooks'; function BrandCustomizer() { const { colors, setBrandColor } = useBrandColors(); return (
setBrandColor('primary', e.target.value)} />
); } ``` Changes are applied immediately as CSS variables on `:root`. --- ## Dark / Light Mode ### `ThemeToggle` 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. ```tsx import { ThemeToggle } from 'xertica-ui/brand'; // Place in Header actions or Sidebar footer ; ``` **Props:** | Prop | Type | Default | Description | | ----------- | ---------------------- | ------- | ---------------------- | | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Button size | | `className` | `string` | — | Additional CSS classes | ### `useTheme` Hook For programmatic theme control: ```tsx import { useTheme } from 'xertica-ui/hooks'; function ThemeController() { const { theme, setTheme, toggleTheme } = useTheme(); return ; } ``` > **Note**: `useTheme` requires `` in the tree. `ThemeToggle` works without any provider. --- ## Language Selector ### `LanguageSelector` A standalone dropdown for switching the UI language preference. ```tsx import { LanguageSelector } from 'xertica-ui/brand'; // Typically placed in PageHeader or Sidebar footer ; ``` The selected language is persisted in `localStorage` under `xertica_language`. Access the current value via `useLanguage()`: ```tsx import { useLanguage } from 'xertica-ui/hooks'; function MyComponent() { const { language, availableLanguages, isMonolingual } = useLanguage(); // language is `string` (e.g. 'pt-BR', 'en', 'fr') — the set is runtime-configurable } ``` The set of available languages is configured at runtime via the `availableLanguages` prop on `` (defaults to `pt-BR`, `en`, `es`). See [`docs/i18n.md`](../i18n.md) for the full extension API. When only one language is configured, the `LanguageSelector` auto-hides. --- ## Logo Components ### `XerticaLogo` Full horizontal logotype. Use in the Sidebar header or login page. ```tsx import { XerticaLogo } from 'xertica-ui/brand'; ; ``` ### `XerticaXLogo` Compact X-mark variant. Use when space is limited (collapsed sidebar, favicon-like contexts). ```tsx import { XerticaXLogo } from 'xertica-ui/brand'; ; ``` ### `XerticaOrbe` Animated orb brand mark. Use as a decorative element or loading indicator. ```tsx import { XerticaOrbe } from 'xertica-ui/brand'; ; ``` --- ## `XerticaProvider` The root provider that initializes all brand, theme, layout, and service contexts. Must wrap the entire application once. ```tsx import { XerticaProvider } from 'xertica-ui/brand'; import 'xertica-ui/style.css'; ReactDOM.createRoot(document.getElementById('root')!).render( ); ``` `XerticaProvider` initializes: - `ThemeProvider` — dark/light mode - `LanguageProvider` — language preference - `BrandColorsProvider` — brand color tokens - `LayoutProvider` — sidebar and assistant state - `AssistenteProvider` — AI assistant context - `ApiKeyProvider` — API key management - `GoogleMapsLoaderProvider` — lazy Maps API loading - `TooltipProvider` — Radix tooltip context - `Toaster` — Sonner toast notifications **Props:** | Prop | Type | Default | Description | | --------------------- | ----------- | ------------ | ------------------------------------------- | | `children` | `ReactNode` | _(required)_ | Application content | | `googleMapsApiKey` | `string` | — | Google Maps API key (activates Maps loader) | | `initialApiKey` | `string` | — | Pre-set Xertica API key | | `initialGeminiApiKey` | `string` | — | Pre-set Gemini API key | --- ## CSS Token Structure The complete token set is defined in `tokens.css` (generated by the CLI). Key token groups: ```css :root { /* Colors */ --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --destructive: 0 84.2% 60.2%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; /* Chart colors */ --chart-1: 221.2 83.2% 53.3%; --chart-2: 142.1 76.2% 36.3%; --chart-3: 47.9 95.8% 53.1%; --chart-4: 280.1 65.3% 60%; --chart-5: 24.6 95% 53.1%; /* Sidebar */ --sidebar: 222.2 84% 4.9%; --sidebar-foreground: 210 40% 98%; /* Shape */ --radius: 0.5rem; --radius-button: 0.375rem; } ``` --- ## AI Rules > [!IMPORTANT] > > - **`XerticaProvider` is required once**: Place it at the root of your application. Never nest multiple `XerticaProvider` instances. > - **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. > - **`ThemeToggle` is self-contained**: It does not need `useTheme` or any context. Use it directly in any component without provider requirements. > - **Logo sizing via `className`**: Size logo components using Tailwind height/width classes (`h-8`, `w-8`). Do not use `width`/`height` HTML attributes. > - **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()`. --- END COMPONENT: BRANDING --- --- START COMPONENT: BREADCRUMB --- # Breadcrumb ## Overview A hierarchical navigation trail showing the user's current position in the application's page structure. Rendered in the `Header` component via the `breadcrumbs` prop. Each item in the chain can optionally be a clickable link. --- ## When to Use - In the application header to show the current page's position in the hierarchy - In pages with deep navigation structures (Dashboard > Reports > Q4 2024) --- ## Anatomy ``` Home Current Page ``` --- ## Sub-Components | Component | Description | | --------------------- | ------------------------------------------------------ | | `Breadcrumb` | Root wrapper (renders `