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 * `
` 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 `` 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;
/** 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 `` 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;