UNPKG

@kitn.ai/chat

Version:

Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.

814 lines (582 loc) 64 kB
<!-- AUTO-GENERATED by scripts/gen-llms.mjs — do not edit by hand. Run `npm run build`. --> # @kitn.ai/chat — Full Reference # @kitn.ai/chat > Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. 44 `kitn-*` custom elements: streaming responses, markdown + code rendering, reasoning/tool panels, attachments, conversation sidebar, voice input. Zero framework dependency for consumers; the SolidJS runtime it is authored in is bundled in, so the host needs nothing. ## Install ```bash npm install @kitn.ai/chat # SolidJS consumers also need the peer dep: npm install solid-js ``` ## #1 rule: array/object data goes on JS PROPERTIES, not HTML attributes This is the single most common mistake. Arrays and objects (`messages`, `models`, `context`, `suggestions`, `slashCommands`, …) MUST be assigned as JavaScript properties on the element. They CANNOT be passed as HTML attributes — an HTML attribute is always a string and will be ignored or mis-parsed. ```js const chat = document.querySelector('kc-chat'); chat.messages = [{ id: '1', role: 'assistant', content: 'Hi!' }]; // ✅ property ``` ```html <kc-chat messages="[...]"></kc-chat> <!-- ❌ never works --> ``` Only scalar values (string/number/boolean) work as attributes (e.g. `placeholder`, `loading`, `theme`). ## Two layers **Layer 1 — batteries-included web components** (`import '@kitn.ai/chat/elements'`): Drop an element into any framework (React, Vue, plain HTML). Data in via JS properties; interactions out via non-bubbling CustomEvents. - `<kc-chat>` — full chat UI (message list + prompt input). The primary starting point. - `<kc-conversations>` — sidebar conversation browser with group support. - `<kc-prompt-input>` — standalone composer with send button. **Layer 2 — composable primitives** (`import { … } from '@kitn.ai/chat'`): All 44 elements are also exported individually. Use them for custom layouts or features `<kc-chat>` does not expose (ChainOfThought, FeedbackBar, ThinkingBar, VoiceInput, …). Your bundler tree-shakes the rest. ## Key rules for the web components 1. **Array/object data = JS properties** (see above). Scalars may be attributes. 2. **Events are non-bubbling `CustomEvent`s** — listen directly on the element: `chat.addEventListener('kc-submit', (e) => console.log(e.detail.value))` 3. **`theme` attribute** (`'light' | 'dark' | 'auto'`) works on every element. Default `auto` follows `prefers-color-scheme`. 4. **Theming via CSS custom properties** — override `--kc-color-*` tokens on `:root`; they pierce Shadow DOM. ## ChatMessage schema (required for `<kc-chat>`) ```ts interface ChatMessage { id: string; role: 'user' | 'assistant'; content: string; reasoning?: { text: string; label?: string }; tools?: ToolPart[]; attachments?: AttachmentData[]; actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit')[]; } ``` ## Framework wiring **Plain HTML / CDN** ```html <script type="module" src="https://unpkg.com/@kitn.ai/chat/elements"></script> <kc-chat style="display:block;height:100vh"></kc-chat> <script type="module"> const chat = document.querySelector('kc-chat'); chat.messages = []; </script> ``` **React** — typed wrappers auto-set properties and expose `on<Event>` props: ```tsx import { Chat } from '@kitn.ai/chat/react'; <Chat messages={messages} onSubmit={(e) => send(e.detail.value)} /> ``` **Vue** — use the element directly; pass arrays via `.prop`: ```vue <kc-chat :messages.prop="messages" @kc-submit="send" /> ``` ## Theming ```css :root { --kc-color-background: #0f0f0f; --kc-color-primary: #7c3aed; --kc-color-muted: #1e1e1e; } ``` For plain HTML/CDN: `<link rel="stylesheet" href="…/@kitn.ai/chat/theme.tokens.css">`. For Tailwind builds: `@import "@kitn.ai/chat/theme.css"` in your CSS. ## Docs - Full element reference (all 44 elements, every prop/event): ./llms-full.txt — https://kitn.dev/llms-full.txt - Machine-readable Custom Elements Manifest: https://unpkg.com/@kitn.ai/chat/dist/custom-elements.json - Working examples: https://github.com/kitn-ai/chat/tree/main/examples - Storybook: https://storybook.kitn.dev - Repository: https://github.com/kitn-ai/chat --- ## How to build a chat app in 5 steps ### 1 — Install ```bash npm install @kitn.ai/chat ``` ### 2 — Pick your layer Drop-in: use `<kc-chat>` for a full chat UI in one tag (`import '@kitn.ai/chat/elements'`). Composable: combine `<kc-message>`, `<kc-prompt-input>`, `<kc-reasoning>`, … in your own markup. ### 3 — Handle `submit` and stream ```js import '@kitn.ai/chat/elements'; const chat = document.querySelector('kc-chat'); chat.messages = []; chat.addEventListener('kc-submit', async (e) => { const userText = e.detail.value; // Append the user message (new array — see streaming note) const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: userText }]; chat.messages = history; chat.loading = true; // Add an empty assistant placeholder to stream into const aid = crypto.randomUUID(); chat.messages = [...history, { id: aid, role: 'assistant', content: '' }]; let answer = ''; for await (const token of streamFromYourAPI(history)) { answer += token; chat.messages = chat.messages.map((m) => (m.id === aid ? { ...m, content: answer } : m)); } chat.loading = false; }); ``` ### 4 — Wire optional features - Reasoning: add `reasoning: { text: '…' }` to an assistant message. - Tool calls: add `tools: [{ type: 'search', state: 'output-available', input: {…}, output: {…} }]`. - Model switcher: `chat.models = [{ id: 'gpt-4o', name: 'GPT-4o' }]; chat.currentModel = 'gpt-4o';` — listen for `modelchange`. - Token meter: `chat.context = { usedTokens: 1200, maxTokens: 128000 };`. - History sidebar: add `<kc-conversations>`; listen for `select` and `newchat`. ### 5 — Theme Override `--kc-color-*` tokens on `:root` (they pierce Shadow DOM). --- ## Streaming recipe (critical) To update messages while streaming, **reassign a NEW array containing a NEW message object** on every chunk. Mutating an existing message object in place will NOT trigger a re-render: ```js // ✅ re-renders chat.messages = chat.messages.map((m) => (m.id === id ? { ...m, content: next } : m)); // ❌ does NOT re-render chat.messages[i].content = next; ``` The same rule applies to every array/object property (`models`, `context`, `suggestions`, …): replace, don't mutate. --- ## Element reference (44 elements, generated from custom-elements.json) Every element also accepts the `theme` attribute. Array/object properties are marked with a `—` attribute: they must be set as JS properties. ### `kc-artifact` / `Artifact` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `src` | `src` | `undefined \| string` | URL the preview iframe frames. Consumer-controlled. | | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other" }[]` | Files for the Code tab tree + each file's preview `url`. Set as a JS property (array). | | `tab` | `tab` | `undefined \| "preview" \| "code"` | Active tab: `preview` (default) or `code`. | | `activeFile` | `active-file` | `undefined \| string` | Selected file path — syncs the tree highlight, Code source, and preview. | | `sandbox` | `sandbox` | `undefined \| string` | iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`). | | `iframeTitle` | `iframe-title` | `undefined \| string` | Accessible title for the preview iframe. | | `maximized` | `maximized` | `undefined \| false \| true` | Reflects the artifact's own maximized view-state (usually driven by the protocol). | | `expandable` | `expandable` | `undefined \| false \| true` | Show the expand-to-fill button (OPT-IN). | | `openInTab` | `open-in-tab` | `undefined \| false \| true` | Show the open-in-new-tab button (OPT-IN). | | `noNav` | `no-nav` | `undefined \| false \| true` | Hide back/forward. | | `noReload` | `no-reload` | `undefined \| false \| true` | Hide reload. | | `noHome` | `no-home` | `undefined \| false \| true` | Hide home. | | `noPathField` | `no-path-field` | `undefined \| false \| true` | Hide the address field. | | `noTabs` | `no-tabs` | `undefined \| false \| true` | Hide the Preview\|Code toggle. | | `standalone` | `standalone` | `undefined \| false \| true` | Standalone chrome: rounded corners + border (else square, borderless in-panel). | | `readonlyPath` | `readonly-path` | `undefined \| false \| true` | Show the address but make it read-only (visible, nav-tracking, non-editable). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-file-select` | `CustomEvent<{ path: string }>` | Fired when a file is selected. `detail.path`. | | `kc-maximize-change` | `CustomEvent<{ maximized: false \| true }>` | Artifact's own maximize button toggled (consumer-observable; non-bubbling). | | `kc-navigate` | `CustomEvent<{ url: string }>` | Fired when the preview navigates. `detail.url` = the new location. | | `kc-tab-change` | `CustomEvent<{ tab: "preview" \| "code" }>` | Fired when the Preview\|Code tab changes. `detail.tab`. | --- ### `kc-attachments` / `Attachments` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `items` | — | `{ id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]` | The attachments to render. Set as a JS property (array). | | `variant` | `variant` | `undefined \| "grid" \| "inline" \| "list"` | Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. | | `hoverCard` | `hover-card` | `undefined \| false \| true` | Wrap each item in a hover card that previews its details. | | `removable` | `removable` | `undefined \| false \| true` | Show a remove button per item; clicking it fires a `kc-remove` event. | | `showMediaType` | `show-media-type` | `undefined \| false \| true` | Also show the media type beneath the filename (non-grid variants). | | `emptyText` | `empty-text` | `undefined \| string` | Text shown when `items` is empty. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-remove` | `CustomEvent<{ id: string }>` | A remove button was clicked. | --- ### `kc-card` / `Card` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. | | `description` | `description` | `undefined \| string` | Supporting text under the heading. Attribute: `description`. | | `errorMessage` | `error-message` | `undefined \| string` | When set, the card renders its inline error state instead of the body. Attribute: `error-message`. | | `dense` | `dense` | `undefined \| false \| true` | Compact spacing for dense lists. Attribute: `dense`. | _No events._ --- ### `kc-cards` / `Cards` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `cards` | — | `undefined \| { type: string; id: string; data: unknown; title?: undefined \| string; resolution?: undefined \| { kind: "action"; action: string; payload?: unknown; at?: undefined \| string } \| { kind: "submit"; data: unknown; at?: undefined \| string } }[]` | The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. | | `types` | — | `undefined \| Record<string, string>` | Optional type→tag overrides/additions (merged over the built-ins). Property: `el.types`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. | | `policy` | — | `undefined \| { onSubmit?: undefined \| (cardId: string, data: unknown) => void; onAction?: undefined \| (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: undefined \| (text: string, opts: { mode: "compose" \| "send"; context?: unknown; }) => void; onOpen?: undefined \| (url: string, target: "tab" \| "artifact") => void; onState?: undefined \| (cardId: string, patch: unknown) => void; onDismiss?: undefined \| (cardId: string) => void; onError?: undefined \| (cardId: string, message: string) => void; maxSendPromptMode?: undefined \| "compose" \| "send" }` | Optional CardPolicy handling child events. Property: `el.policy`. | _No events._ --- ### `kc-chain-of-thought` / `ChainOfThought` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `steps` | — | `{ label: string; content?: undefined \| string }[]` | The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). | _No events._ --- ### `kc-chat` / `Chat` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `messages` | — | `{ id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string } }[]` | The full message thread to render, newest last. Each entry carries its role, content, and optional reasoning/tools/attachments/actions. Set as a JS property (`el.messages = [...]`). | | `value` | `value` | `undefined \| string` | Controlled value of the input. When set, the host owns the input text and must update it on `kc-value-change`; leave unset for uncontrolled behavior. | | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. | | `loading` | `loading` | `undefined \| false \| true` | When true, shows the loading/streaming state and disables submit (use while awaiting the assistant's reply). | | `suggestions` | — | `undefined \| string[]` | Starter prompts shown above the input when the thread is empty. Clicking one follows `suggestionMode`. Set as a JS property. | | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | What clicking a suggestion does: `'submit'` (default) sends it immediately as if typed and submitted; `'fill'` just places it in the input. | | `persistSuggestions` | `persist-suggestions` | `undefined \| false \| true` | Keep suggestions visible after the conversation starts. By default suggestions are conversation starters and hide once `messages` is non-empty; set this to keep them always shown. Default false. | | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. | | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). | | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true. | | `chatTitle` | `chat-title` | `undefined \| string` | Optional header title shown on the left of the header. | | `models` | — | `undefined \| { id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kc-model-change` event fires on selection. | | `currentModel` | `current-model` | `undefined \| string` | The currently selected model id (pairs with `models`). | | `context` | — | `undefined \| { usedTokens: number; maxTokens: number; inputTokens?: undefined \| number; outputTokens?: undefined \| number; estimatedCost?: undefined \| number }` | Optional context-window token usage. When set, a Context token meter is shown in the header. | | `scrollButton` | `scroll-button` | `undefined \| false \| true` | Show the scroll-to-bottom button inside the scroll area. Default true. | | `headerStart` | `header-start` | `undefined \| false \| true` | Whether the host has `slot="header-start"` content (left of the title) — set by the `<kc-chat>` facade so a custom control forces the header open. | | `headerEnd` | `header-end` | `undefined \| false \| true` | Whether the host has `slot="header-end"` content (right of the controls). | | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the input toolbar; fires a `search` event. | | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the input toolbar; fires a `voice` event. | | `slashCommands` | — | `undefined \| { id: string; label: string; description?: undefined \| string; category?: undefined \| string }[]` | Slash commands — when set, typing `/` in the input opens the command palette and fires `kc-slash-select`. Set as a JS property. | | `slashActiveIds` | — | `undefined \| string[]` | Command ids to highlight as active in the palette. | | `slashCompact` | `slash-compact` | `undefined \| false \| true` | Single-line palette rows. | | `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-message-action` | `CustomEvent<{ messageId: string; action: string }>` | An action button on a message was clicked. `action` is the built-in name or custom id. | | `kc-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. | | `kc-search` | `CustomEvent<Record<string, never>>` | The Search button was clicked. | | `kc-slash-select` | `CustomEvent<{ command: { id: string; label: string; description?: undefined \| string; category?: undefined \| string } }>` | A slash command was chosen from the palette. | | `kc-submit` | `CustomEvent<{ value: string; attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | User submitted a message. | | `kc-suggestion-click` | `CustomEvent<{ value: string }>` | A suggestion chip was clicked (only in `suggestion-mode="fill"`). | | `kc-value-change` | `CustomEvent<{ value: string }>` | Fired on every input change. | | `kc-voice` | `CustomEvent<Record<string, never>>` | The Mic / voice button was clicked. | --- ### `kc-checkpoint` / `Checkpoint` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `label` | `label` | `undefined \| string` | Optional text beside the icon. | | `tooltip` | `tooltip` | `undefined \| string` | Tooltip on hover. | | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Visual button style. | | `size` | `size` | `undefined \| "sm" \| "lg" \| "md" \| "icon" \| "icon-sm"` | Button size (use an `icon*` size for an icon-only checkpoint). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-select` | `CustomEvent` | The checkpoint was clicked. | --- ### `kc-choice` / `Choice` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `data` | — | `undefined \| Record<string, unknown>` | The choice definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { prompt, options:[…], allowOther?, submitLabel? }`. Import `ChoiceCardData` from `@kitn.ai/chat` for the full shape. | | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. | | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. | | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. | _No events._ --- ### `kc-code-block` / `CodeBlock` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `code` | `code` | `string` | The source code to render. | | `language` | `language` | `undefined \| string` | Language grammar (e.g. `js`, `python`). Defaults to `tsx`. | | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name. | | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (renders plain text, no Shiki). | | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Code text sizing. | _No events._ --- ### `kc-confirm` / `Confirm` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `data` | — | `undefined \| Record<string, unknown>` | The confirm definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { body, tone, actions:[…] }`. Import `ConfirmCardData` from `@kitn.ai/chat` for the full shape. | | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. | | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. | | `autofocus` | `autofocus` | `undefined \| false \| true` | Focus the default action on mount (off by default — no focus-stealing). Attribute: `autofocus`. | | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. | _No events._ --- ### `kc-context` / `Context` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `context` | — | `undefined \| { usedTokens: number; maxTokens: number; inputTokens?: undefined \| number; outputTokens?: undefined \| number; reasoningTokens?: undefined \| number; cacheTokens?: undefined \| number; estimatedCost?: undefined \| number }` | Token-usage data. Set as a JS property. | | `warnThreshold` | `warn-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns yellow. Defaults to `0.7` (70%). | | `dangerThreshold` | `danger-threshold` | `undefined \| number` | Fraction (0–1) above which the meter turns red. Defaults to `0.9` (90%). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-threshold-change` | `CustomEvent<{ level: "ok" \| "warn" \| "danger" }>` | Fires when the computed severity level changes (ok → warn → danger or back). `detail.level` is `'ok'`, `'warn'`, or `'danger'`. | --- ### `kc-conversations` / `Conversations` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `groups` | — | `{ id: string; userId?: undefined \| string; teamId?: undefined \| string; name: string; sortOrder: number; createdAt: string }[]` | Pre-bucketed conversation groups (e.g. "Today", "Yesterday"), each with its own conversations. Use this when you want to control the grouping/headers yourself; otherwise pass a flat `conversations` array. Set as a JS property. | | `conversations` | — | `{ id: string; title: string; groupId?: undefined \| string; scope: { type: "document" \| "collection"; documentId?: undefined \| string; filters?: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }; messageCount: number; lastMessageAt: string; updatedAt: string }[]` | A flat list of conversation summaries; the component buckets them by recency for you. Ignored when `groups` is provided. Set as a JS property. | | `activeId` | `active-id` | `undefined \| string` | The id of the currently-open conversation, highlighted in the list. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-conversation-select` | `CustomEvent<{ id: string }>` | A conversation was selected. | | `kc-new-chat` | `CustomEvent<Record<string, never>>` | The "New chat" button was clicked. | | `kc-toggle-sidebar` | `CustomEvent<Record<string, never>>` | The sidebar toggle was clicked. | --- ### `kc-embed` / `Embed` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. | | `data` | — | `undefined \| { provider: "youtube" \| "vimeo" \| "generic"; id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; poster?: undefined \| string; start?: undefined \| number; aspectRatio?: undefined \| "16:9" \| "4:3" \| "1:1" \| "9:16" }` | The embed payload (provider + id/url + options). Set as a JS **property** (object). | _No events._ --- ### `kc-empty` / `Empty` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `emptyTitle` | `empty-title` | `undefined \| string` | Title text. Attribute: `empty-title` (`title` is a global HTML attribute). | | `description` | `description` | `undefined \| string` | Description text. | _No events._ --- ### `kc-feedback-bar` / `FeedbackBar` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `barTitle` | `bar-title` | `undefined \| string` | The banner label (e.g. "Was this helpful?"). Attribute: `bar-title` (`title` is avoided — it's a global HTML attribute). | | `collectDetail` | `collect-detail` | `undefined \| false \| true` | When set, a not-helpful vote opens an optional detail form before the thank-you confirmation. Attribute: `collect-detail`. | | `categories` | — | `undefined \| string[]` | Optional category chips for the detail form. Set as a JS property (array). | | `detailTitle` | `detail-title` | `undefined \| string` | Heading for the detail form. Attribute: `detail-title`. | | `detailPlaceholder` | `detail-placeholder` | `undefined \| string` | Placeholder for the detail comment box. Attribute: `detail-placeholder`. | | `submitLabel` | `submit-label` | `undefined \| string` | Submit button label in the detail form. Attribute: `submit-label`. | | `thanksMessage` | `thanks-message` | `undefined \| string` | Confirmation copy shown after a vote/submit. Attribute: `thanks-message`. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-close` | `CustomEvent` | The user dismissed the banner. | | `kc-feedback` | `CustomEvent<{ value: "helpful" \| "not-helpful" }>` | The user rated the response. `value` is `'helpful'` or `'not-helpful'`. | | `kc-feedback-detail` | `CustomEvent<{ value: "helpful" \| "not-helpful"; category?: undefined \| string; comment?: undefined \| string }>` | The user submitted the optional detail form (`collect-detail`). | --- ### `kc-file-tree` / `FileTree` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `files` | — | `{ path: string; url?: undefined \| string; code?: undefined \| string; language?: undefined \| string; type?: undefined \| "html" \| "pdf" \| "image" \| "other" }[]` | The files to render. Set as a JS property (array of `{ path, url?, code?, language?, type? }`). | | `activeFile` | `active-file` | `undefined \| string` | Selected file path — highlighted in the tree. | | `defaultExpanded` | — | `undefined \| string[]` | Folder paths expanded initially. Omit to start with all folders open. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-select` | `CustomEvent<{ path: string }>` | Fired when a file is selected. `detail.path` = the file's path. | --- ### `kc-file-upload` / `FileUpload` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `multiple` | `multiple` | `undefined \| false \| true` | Allow selecting multiple files (default true). | | `accept` | `accept` | `undefined \| string` | `accept` attribute for the file picker (e.g. `image/*`). | | `disabled` | `disabled` | `undefined \| false \| true` | Disable the dropzone — no clicking, no drag-and-drop. | | `label` | `label` | `undefined \| string` | Default dropzone label (overridable via the default slot). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-files-added` | `CustomEvent<{ files: File[] }>` | Files were picked or dropped. | --- ### `kc-form` / `Form` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `data` | — | `undefined \| Record<string, unknown>` | The form definition — a JSON Schema (`type:'object'`) + `x-kc-*` UI hints (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { type:'object', properties:{…} }`. Import the `FormDefinition` type from `@kitn.ai/chat` for the full shape (it is self-referential, so the element types it loosely). | | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted CardEvent. Attribute: `card-id`. | | `heading` | `heading` | `undefined \| string` | Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. | | `resolution` | — | `undefined \| Record<string, unknown>` | Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{…} }`. | _No events._ --- ### `kc-image` / `Image` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `base64` | `base64` | `undefined \| string` | Base64-encoded image data (pair with `media-type`). | | `bytes` | — | `undefined \| Uint8Array<ArrayBufferLike>` | Raw image bytes (set as a JS property). | | `alt` | `alt` | `undefined \| string` | Alt text. | | `mediaType` | `media-type` | `undefined \| string` | MIME type (default `image/png`). | _No events._ --- ### `kc-link-preview` / `LinkPreview` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `cardId` | `card-id` | `undefined \| string` | Stable card id correlating every emitted event. Set as an attribute or property. | | `data` | — | `undefined \| { url: string; title?: undefined \| string; description?: undefined \| string; image?: undefined \| string; imageAlt?: undefined \| string; favicon?: undefined \| string; domain?: undefined \| string; siteName?: undefined \| string }` | The link payload (OG metadata). Set as a JS **property** (object). | _No events._ --- ### `kc-loader` / `Loader` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `variant` | `variant` | `undefined \| "circular" \| "classic" \| "pulse" \| "pulse-dot" \| "dots" \| "typing" \| "wave" \| "bars" \| "terminal" \| "text-blink" \| "text-shimmer" \| "loading-dots"` | The animation style: `'circular' \| 'classic' \| 'pulse' \| 'pulse-dot' \| 'dots' \| 'typing' \| 'wave' \| 'bars' \| 'terminal' \| 'text-blink' \| 'text-shimmer' \| 'loading-dots'`. Defaults to `'circular'`. | | `size` | `size` | `undefined \| "sm" \| "lg" \| "md"` | Loader size: `'sm' \| 'md' \| 'lg'`. Defaults to `'md'`. | | `text` | `text` | `undefined \| string` | Label for the text-based variants. | _No events._ --- ### `kc-markdown` / `Markdown` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `content` | `content` | `string` | The markdown source to render. | | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing. | | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme for fenced code blocks. | | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting (no Shiki loads). | _No events._ --- ### `kc-message` / `Message` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `message` | — | `undefined \| { id: string; role: "user" \| "assistant"; content: string; reasoning?: undefined \| { text: string; label?: undefined \| string }; tools?: undefined \| { type: string; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string }[]; attachments?: undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]; actions?: undefined \| ("copy" \| "like" \| "dislike" \| "regenerate" \| "edit" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]; avatar?: undefined \| { src?: undefined \| string; fallback?: undefined \| string; alt?: undefined \| string } }` | The full message object. Set as a JS property. | | `role` | `role` | `undefined \| "user" \| "assistant"` | Convenience for simple cases when not passing a `message` object. | | `content` | `content` | `undefined \| string` | Convenience content (used when `message` is not set). | | `markdown` | `markdown` | `undefined \| false \| true` | Force markdown on/off. Defaults to on for assistant, off for user. | | `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing for the message body. | | `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name used for fenced code blocks in the content. | | `codeHighlight` | `code-highlight` | `undefined \| false \| true` | Disable syntax highlighting for code blocks (no Shiki loads). | | `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether the action bar is always visible (`'always'`, default) or only revealed on hover of the message row (`'hover'`). | | `avatarSrc` | `avatar-src` | `undefined \| string` | Convenience avatar image URL (used when `message.avatar` is not set). | | `avatarFallback` | `avatar-fallback` | `undefined \| string` | Convenience avatar fallback text (used when `message.avatar` is not set). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-message-action` | `CustomEvent<{ messageId: string; action: string }>` | An action button was clicked. `action` is the built-in name or custom id. | --- ### `kc-model-switcher` / `ModelSwitcher` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `models` | — | `{ id: string; name: string; provider?: undefined \| string; description?: undefined \| string; group?: undefined \| string }[]` | The selectable models. Set as a JS property (array). | | `currentModel` | `current-model` | `undefined \| string` | The currently-selected model id. Defaults to the first model. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-model-change` | `CustomEvent<{ modelId: string }>` | A model was selected. | --- ### `kc-popover` / `Popover` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `placement` | `placement` | `undefined \| "top" \| "right" \| "bottom" \| "left" \| "top-start" \| "top-end" \| "right-start" \| "right-end" \| "bottom-start" \| "bottom-end" \| "left-start" \| "left-end"` | Floating placement relative to the trigger (floating-ui placement). | | `gutter` | `gutter` | `undefined \| number` | Gap in px between the trigger and the panel. | | `open` | `open` | `undefined \| false \| true` | Controlled open state. Set as a JS property (`el.open = true`) to drive the popover from your app; omit for the default click-to-toggle behaviour. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-open-change` | `CustomEvent<{ open: false \| true }>` | The popover wants to open or close (click, Escape, or outside-click). | --- ### `kc-prompt-input` / `PromptInput` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `value` | `value` | `undefined \| string` | Controlled value of the input. When set, the host owns the text and must update it on `kc-value-change`; leave unset for uncontrolled behavior. | | `placeholder` | `placeholder` | `undefined \| string` | Placeholder text shown in the empty input. | | `disabled` | `disabled` | `undefined \| false \| true` | Disable the input and submit button entirely (non-interactive). | | `loading` | `loading` | `undefined \| false \| true` | Show the loading/streaming state and block submit (use while awaiting a reply). | | `suggestions` | — | `undefined \| string[]` | Starter prompts shown above the input. Clicking one follows `suggestionMode`. Set as a JS property. | | `suggestionMode` | `suggestion-mode` | `undefined \| "submit" \| "fill"` | What clicking a suggestion does: `'submit'` (default) sends it immediately as if typed and submitted; `'fill'` just places it in the input. | | `slashCommands` | — | `undefined \| { id: string; label: string; description?: undefined \| string; category?: undefined \| string }[]` | Slash commands — when set, typing `/` opens the command palette. Set as a JS property. | | `slashActiveIds` | — | `undefined \| string[]` | Command ids to highlight as active. | | `slashCompact` | `slash-compact` | `undefined \| false \| true` | Single-line palette rows. | | `search` | `search` | `undefined \| false \| true` | Show a Search (Globe) button in the left toolbar; clicking it fires a `search` event. | | `voice` | `voice` | `undefined \| false \| true` | Show a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event. | | `stoppable` | `stoppable` | `undefined \| false \| true` | When set and `loading` is true, the send button is replaced by a Stop button (square icon, "Stop" aria-label). Clicking it fires `kc-stop`. | | `attachments` | — | `undefined \| { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[]` | Attachments to seed the input with (so a consumer can pre-populate staged files without an upload). Set as a JS property; the element then manages its own attachment state from there (add via the paperclip, remove per chip). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-search` | `CustomEvent<Record<string, never>>` | The Search (Globe) toolbar button was clicked. | | `kc-slash-select` | `CustomEvent<{ command: { id: string; label: string; description?: undefined \| string; category?: undefined \| string } }>` | A slash command was chosen from the palette. | | `kc-stop` | `CustomEvent<Record<string, never>>` | The Stop button was clicked while `stoppable` and `loading` are both true. | | `kc-submit` | `CustomEvent<{ value: string; attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The user submitted the prompt (Enter or send button) with its attachments. | | `kc-suggestion-click` | `CustomEvent<{ value: string }>` | A suggestion was clicked while `suggestion-mode="fill"`. | | `kc-toolbar-action` | `CustomEvent<{ action: string }>` | A custom `<kc-action>` toolbar button was clicked. `action` is the `id` of the `<kc-action>` element that was clicked. | | `kc-value-change` | `CustomEvent<{ value: string }>` | The input text changed (fires on every keystroke). | | `kc-voice` | `CustomEvent<Record<string, never>>` | The Voice (Mic) toolbar button was clicked. | --- ### `kc-reasoning` / `Reasoning` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `text` | `text` | `string` | The reasoning text to display. | | `label` | `label` | `undefined \| string` | Trigger label. | | `open` | `open` | `undefined \| false \| true` | Controlled open state — set as a property (`el.open = true`). Omit for uncontrolled (the trigger toggles it). | | `streaming` | `streaming` | `undefined \| false \| true` | While true, auto-expands (and re-collapses when it flips false). | | `markdown` | `markdown` | `undefined \| false \| true` | Render `text` as markdown. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-open-change` | `CustomEvent<{ open: false \| true }>` | Open state changed (via the trigger or streaming auto-open). | --- ### `kc-remote` / `Remote` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `src` | `src` | `undefined \| string` | The remote card URL. Attribute: `src`. | | `providerOrigin` | `provider-origin` | `undefined \| string` | Exact provider origin (https: or http://localhost for dev). Attribute: `provider-origin`. | | `envelope` | — | `undefined \| Record<string, unknown>` | The card envelope to render. JS property only. | | `policy` | — | `undefined \| Record<string, unknown>` | Optional routing policy. JS property only. | _No events._ --- ### `kc-resizable` / `Resizable` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `orientation` | `orientation` | `undefined \| "horizontal" \| "vertical"` | Layout axis: `horizontal` (row, default) or `vertical` (column). | | `maximizedIndex` | — | `undefined \| null \| number` | Which item index is maximized (null = none). Declarative source of truth. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-change` | `CustomEvent<{ sizes: number[] }>` | Fired on drag-end / keyboard resize / visibility change. `detail.sizes` = panel sizes in percent. | | `kc-maximize-change` | `CustomEvent<{ maximized: false \| true; index: null \| number }>` | Observe layout maximize state. | --- ### `kc-resizable-item` / `ResizableItem` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `size` | `size` | `undefined \| string` | Initial main-axis size: `"280px"` (fixed) or `"25%"`/`25` (percent). Omitted → flexible. | | `min` | `min` | `undefined \| string` | Minimum size during resize (px or %). | | `max` | `max` | `undefined \| string` | Maximum size during resize (px or %). | | `locked` | `locked` | `undefined \| false \| true` | Fix this panel's size; adjacent dividers become non-draggable. | | `hidden` | `hidden` | `undefined \| false \| true` | Hide this panel; its divider is dropped and the rest reflow. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-change` | `CustomEvent<unknown>` | | | `kc-maximize-change` | `CustomEvent<unknown>` | | --- ### `kc-response-stream` / `ResponseStream` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `text` | — | `undefined \| string \| AsyncIterable<string>` | Text to stream. A string, or an `AsyncIterable<string>` (set as a JS property — async iterables can't be HTML attributes). | | `mode` | `mode` | `undefined \| "typewriter" \| "fade"` | Reveal animation. | | `speed` | `speed` | `undefined \| number` | Characters/segments per tick. | | `as` | `as` | `undefined \| string` | Element tag to render as. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-complete` | `CustomEvent` | Streaming finished. | --- ### `kc-scope-picker` / `ScopePicker` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `availableAuthors` | — | `string[]` | Authors to offer as scope filters. Set as a JS property. | | `availableTags` | — | `string[]` | Tags to offer as scope filters. Set as a JS property. | | `currentLabel` | `current-label` | `undefined \| string` | The label shown on the trigger for the active scope. | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-scope-change` | `CustomEvent<{ filters: undefined \| { tags?: undefined \| string[]; authors?: undefined \| string[]; contentType?: undefined \| "transcript" \| "markdown"; dateRange?: undefined \| { from: string; to: string } } }>` | A scope was chosen (`undefined` filters = "All Content"). | --- ### `kc-scroll-button` / `ScrollButton` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `for` | `for` | `undefined \| string` | CSS id of the scroll container to control. When omitted the element walks up the DOM (outside its own shadow root) to find the nearest scrollable ancestor. Mirrors the `for` convention of `<label for="...">`. | | `variant` | `variant` | `undefined \| "ghost" \| "default" \| "outline"` | Button visual variant: `'outline' \| 'ghost' \| 'default'`. Defaults to `'outline'`. | | `size` | `size` | `undefined \| "sm" \| "lg" \| "md" \| "icon" \| "icon-sm"` | Button size token. Defaults to `'icon'` (square). | **Events** (non-bubbling `CustomEvent`s — listen directly on the element): | Event | `detail` type | Description | |---|---|---| | `kc-scroll` | `CustomEvent` | Emitted when the user clicks the button and `scrollToBottom()` is called. Carries no detail — consumers use it to know a manual scroll occurred. | --- ### `kc-skills` / `Skills` **Properties** (every element also accepts `theme="light|dark|auto"`; only scalar props work as HTML attributes): | Property | Attribute | Type | Description | |---|---|---|---| | `skills` | — | `{ id: