@kitn.ai/ui
Version:
Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.
117 lines (116 loc) • 6.1 kB
TypeScript
import { ComposerDoc } from '../primitives/composer-model';
import { AttachmentData } from './attachments';
import { MessageActionDetail } from '../primitives/message-feedback';
import { TriggerDef } from './composer';
import { ChatMessage } from '../elements/chat-types';
import { ProseSize } from '../primitives/chat-config';
import { ModelOption } from '../types';
export interface ChatThreadContextUsage {
usedTokens: number;
maxTokens: number;
inputTokens?: number;
outputTokens?: number;
estimatedCost?: number;
}
export interface ChatThreadProps {
/** Extra classes for the thread root (e.g. `h-full`). */
class?: 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 = [...]`). */
messages: ChatMessage[];
/** Value of the input. A **string** is controlled (the host owns the text and
* updates it on `kai-value-change`). A **ComposerDoc** is a one-time seed that
* pre-populates pills; the user then edits freely. Leave unset for uncontrolled. */
value?: string | ComposerDoc;
/** Placeholder text shown in the empty input. */
placeholder?: string;
/** When true, shows the loading/streaming state and disables submit (use while
* awaiting the assistant's reply). */
loading?: boolean;
/** Starter prompts shown above the input when the thread is empty. Clicking one
* follows `suggestionMode`. Set as a JS property. */
suggestions?: string[];
/** What clicking a suggestion does: `'submit'` (default) sends it immediately
* as if typed and submitted; `'fill'` just places it in the input. */
suggestionMode?: 'submit' | 'fill';
/** 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. */
persistSuggestions?: boolean;
/** Body/prose font scale for rendered markdown (`'xs' | 'sm' | 'base' | 'lg'`).
* Defaults to `'sm'`. */
proseSize?: ProseSize;
/** Shiki theme name for syntax-highlighted code blocks (e.g.
* `'github-dark-dimmed'`). */
codeTheme?: string;
/** Enable Shiki syntax highlighting in code blocks. Turn off to render plain
* `<pre>` blocks (lighter, no highlighter load). Default true. */
codeHighlight?: boolean;
/** Optional header title shown on the left of the header. */
chatTitle?: string;
/** Optional model list. When set (>1 model) a ModelSwitcher is shown in the
* header and a `kai-model-change` event fires on selection. */
models?: ModelOption[];
/** The currently selected model id (pairs with `models`). */
currentModel?: string;
/** Optional context-window token usage. When set, a Context token meter is
* shown in the header. */
context?: ChatThreadContextUsage;
/** Show the scroll-to-bottom button inside the scroll area. Default true. */
scrollButton?: boolean;
/** Whether the host has `slot="header-start"` content (left of the title) —
* set by the `<kai-chat>` facade so a custom control forces the header open. */
headerStart?: boolean;
/** Whether the host has `slot="header-end"` content (right of the controls). */
headerEnd?: boolean;
/** REPLACE — full custom header in place of the built-in title/model/context bar. */
headerFull?: boolean;
/** INJECT — left sidebar column (e.g. a conversation list / your own nav). */
sidebar?: boolean;
/** REPLACE — custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). */
empty?: boolean;
/** REPLACE — full custom composer in place of the built-in prompt input. The
* projected content wires its own submit (the data-flow boundary). */
composer?: boolean;
/** INJECT — accessory row just above the composer (e.g. extra actions). */
composerActions?: boolean;
/** INJECT — footer row below the composer (disclaimers, token meter, …). */
footer?: boolean;
/** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
search?: boolean;
/** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
voice?: boolean;
/** Rich entity triggers — each `{ char, kind, items }` opens a caret-anchored
* menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a
* JS property; forwarded to the input. */
triggers?: TriggerDef[];
/** Default icon per entity kind (kind → image src) for pills/menu items. */
kindIcons?: Record<string, string>;
/** Whether each message's action bar is always visible (`'always'`, default)
* or only revealed on hover of that message row (`'hover'`). */
actionsReveal?: 'always' | 'hover';
onValueChange?: (value: string) => void;
onSubmit?: (detail: {
value: string;
attachments: AttachmentData[];
}) => void;
onAttachmentsChange?: (attachments: AttachmentData[]) => void;
onSuggestionClick?: (value: string) => void;
onModelChange?: (modelId: string) => void;
onMessageAction?: (detail: MessageActionDetail) => void;
onSearch?: () => void;
onVoice?: () => void;
/** Receive the imperative controller once mounted. The kai-chat facade forwards
* these as element methods (focus/clear/send/scrollToBottom). */
controllerRef?: (controller: ChatThreadController) => void;
}
/** Imperative handle exposed via `controllerRef` — the input half of the chat's
* interaction surface, forwarded onto `<kai-chat>` as instance methods. */
export interface ChatThreadController {
focus(options?: FocusOptions): void;
clear(): void;
send(): void;
scrollToBottom(behavior?: ScrollBehavior): void;
}
export declare function ChatThread(props: ChatThreadProps): import("solid-js").JSX.Element;