// AUTO-GENERATED by scripts/gen-element-api.mjs — do not edit by hand.
// Typed React wrappers for every kitn custom element. Usage:
//   import { Message } from '@kitn.ai/chat/react';
//   <Message message={msg} onMessageAction={(e) => …} />
import { createWebComponent, type WebComponentProps } from './runtime';


export interface ArtifactProps extends WebComponentProps {
  /** URL the preview iframe frames. Consumer-controlled. */
  src?: string;
  /** Files for the Code tab tree + each file's preview `url`. Set as a JS property (array). */
  files: { path: string; url?: undefined | string; code?: undefined | string; language?: undefined | string; type?: undefined | "html" | "pdf" | "image" | "other" }[];
  /** Active tab: `preview` (default) or `code`. */
  tab?: "preview" | "code";
  /** Selected file path — syncs the tree highlight, Code source, and preview. */
  activeFile?: string;
  /** iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`). */
  sandbox?: string;
  /** Accessible title for the preview iframe. */
  iframeTitle?: string;
  /** Reflects the artifact's own maximized view-state (usually driven by the protocol). */
  maximized?: boolean;
  /** Show the expand-to-fill button (OPT-IN). */
  expandable?: boolean;
  /** Show the open-in-new-tab button (OPT-IN). */
  openInTab?: boolean;
  /** Hide back/forward. */
  noNav?: boolean;
  /** Hide reload. */
  noReload?: boolean;
  /** Hide home. */
  noHome?: boolean;
  /** Hide the address field. */
  noPathField?: boolean;
  /** Hide the Preview|Code toggle. */
  noTabs?: boolean;
  /** Standalone chrome: rounded corners + border (else square, borderless in-panel). */
  standalone?: boolean;
  /** Show the address but make it read-only (visible, nav-tracking, non-editable). */
  readonlyPath?: boolean;
  /** Fired when a file is selected. `detail.path`. */
  onFileSelect?: (event: CustomEvent<{ path: string }>) => void;
  /** Artifact's own maximize button toggled (consumer-observable; non-bubbling). */
  onMaximizeChange?: (event: CustomEvent<{ maximized: boolean }>) => void;
  /** Fired when the preview navigates. `detail.url` = the new location. */
  onNavigate?: (event: CustomEvent<{ url: string }>) => void;
  /** Fired when the Preview|Code tab changes. `detail.tab`. */
  onTabChange?: (event: CustomEvent<{ tab: "preview" | "code" }>) => void;
}

export const Artifact = createWebComponent<ArtifactProps>(
  'kc-artifact',
  ["theme","src","files","tab","activeFile","sandbox","iframeTitle","maximized","expandable","openInTab","noNav","noReload","noHome","noPathField","noTabs","standalone","readonlyPath"],
  { onFileSelect: 'kc-file-select', onMaximizeChange: 'kc-maximize-change', onNavigate: 'kc-navigate', onTabChange: 'kc-tab-change' },
);

export interface AttachmentsProps extends WebComponentProps {
  /** The attachments to render. Set as a JS property (array). */
  items: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[];
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
  variant?: "grid" | "inline" | "list";
  /** Wrap each item in a hover card that previews its details. */
  hoverCard?: boolean;
  /** Show a remove button per item; clicking it fires a `kc-remove` event. */
  removable?: boolean;
  /** Also show the media type beneath the filename (non-grid variants). */
  showMediaType?: boolean;
  /** Text shown when `items` is empty. */
  emptyText?: string;
  /** A remove button was clicked. */
  onRemove?: (event: CustomEvent<{ id: string }>) => void;
}

export const Attachments = createWebComponent<AttachmentsProps>(
  'kc-attachments',
  ["theme","items","variant","hoverCard","removable","showMediaType","emptyText"],
  { onRemove: 'kc-remove' },
);

export interface CardProps extends WebComponentProps {
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
  heading?: string;
  /** Supporting text under the heading. Attribute: `description`. */
  description?: string;
  /** When set, the card renders its inline error state instead of the body. Attribute: `error-message`. */
  errorMessage?: string;
  /** Compact spacing for dense lists. Attribute: `dense`. */
  dense?: boolean;
}

export const Card = createWebComponent<CardProps>(
  'kc-card',
  ["theme","heading","description","errorMessage","dense"],
  {  },
);

export interface CardsProps extends WebComponentProps {
  /** The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. */
  cards?: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: 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. */
  types?: Record<string, string>;
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
  policy?: { onSubmit?: (cardId: string, data: unknown) => void; onAction?: (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: (text: string, opts: { mode: "compose" | "send"; context?: unknown; }) => void; onOpen?: (url: string, target: "tab" | "artifact") => void; onState?: (cardId: string, patch: unknown) => void; onDismiss?: (cardId: string) => void; onError?: (cardId: string, message: string) => void; maxSendPromptMode?: "compose" | "send" };
}

export const Cards = createWebComponent<CardsProps>(
  'kc-cards',
  ["theme","cards","types","policy"],
  {  },
);

export interface ChainOfThoughtProps extends WebComponentProps {
  /** The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). */
  steps: { label: string; content?: undefined | string }[];
}

export const ChainOfThought = createWebComponent<ChainOfThoughtProps>(
  'kc-chain-of-thought',
  ["theme","steps"],
  {  },
);

export interface ChatProps extends WebComponentProps {
  /** 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: { 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 } }[];
  /** 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. */
  value?: string;
  /** 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?: "xs" | "sm" | "base" | "lg";
  /** 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 `kc-model-change` event fires on selection. */
  models?: { id: string; name: string; provider?: string; description?: string; group?: string }[];
  /** 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?: { usedTokens: number; maxTokens: number; inputTokens?: number; outputTokens?: number; estimatedCost?: number };
  /** 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 `<kc-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;
  /** 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;
  /** Slash commands — when set, typing `/` in the input opens the command palette and fires `kc-slash-select`. Set as a JS property. */
  slashCommands?: { id: string; label: string; description?: string; category?: string }[];
  /** Command ids to highlight as active in the palette. */
  slashActiveIds?: string[];
  /** Single-line palette rows. */
  slashCompact?: boolean;
  /** Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). */
  actionsReveal?: "always" | "hover";
  /** An action button on a message was clicked. `action` is the built-in name or custom id. */
  onMessageAction?: (event: CustomEvent<{ messageId: string; action: string }>) => void;
  /** The header model switcher changed. */
  onModelChange?: (event: CustomEvent<{ modelId: string }>) => void;
  /** The Search button was clicked. */
  onSearch?: (event: CustomEvent<Record<string, never>>) => void;
  /** A slash command was chosen from the palette. */
  onSlashSelect?: (event: CustomEvent<{ command: { id: string; label: string; description?: undefined | string; category?: undefined | string } }>) => void;
  /** User submitted a message. */
  onSubmit?: (event: CustomEvent<{ value: string; attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
  /** A suggestion chip was clicked (only in `suggestion-mode="fill"`). */
  onSuggestionClick?: (event: CustomEvent<{ value: string }>) => void;
  /** Fired on every input change. */
  onValueChange?: (event: CustomEvent<{ value: string }>) => void;
  /** The Mic / voice button was clicked. */
  onVoice?: (event: CustomEvent<Record<string, never>>) => void;
}

export const Chat = createWebComponent<ChatProps>(
  'kc-chat',
  ["theme","messages","value","placeholder","loading","suggestions","suggestionMode","persistSuggestions","proseSize","codeTheme","codeHighlight","chatTitle","models","currentModel","context","scrollButton","headerStart","headerEnd","search","voice","slashCommands","slashActiveIds","slashCompact","actionsReveal"],
  { onMessageAction: 'kc-message-action', onModelChange: 'kc-model-change', onSearch: 'kc-search', onSlashSelect: 'kc-slash-select', onSubmit: 'kc-submit', onSuggestionClick: 'kc-suggestion-click', onValueChange: 'kc-value-change', onVoice: 'kc-voice' },
);

export interface CheckpointProps extends WebComponentProps {
  /** Optional text beside the icon. */
  label?: string;
  /** Tooltip on hover. */
  tooltip?: string;
  /** Visual button style. */
  variant?: "ghost" | "default" | "outline";
  /** Button size (use an `icon*` size for an icon-only checkpoint). */
  size?: "sm" | "lg" | "md" | "icon" | "icon-sm";
  /** The checkpoint was clicked. */
  onSelect?: (event: CustomEvent) => void;
}

export const Checkpoint = createWebComponent<CheckpointProps>(
  'kc-checkpoint',
  ["theme","label","tooltip","variant","size"],
  { onSelect: 'kc-select' },
);

export interface ChoiceProps extends WebComponentProps {
  /** 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. */
  data?: Record<string, unknown>;
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
  cardId?: string;
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
  heading?: string;
  /** Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. */
  resolution?: Record<string, unknown>;
}

export const Choice = createWebComponent<ChoiceProps>(
  'kc-choice',
  ["theme","data","cardId","heading","resolution"],
  {  },
);

export interface CodeBlockProps extends WebComponentProps {
  /** The source code to render. */
  code: string;
  /** Language grammar (e.g. `js`, `python`). Defaults to `tsx`. */
  language?: string;
  /** Shiki theme name. */
  codeTheme?: string;
  /** Disable syntax highlighting (renders plain text, no Shiki). */
  codeHighlight?: boolean;
  /** Code text sizing. */
  proseSize?: "xs" | "sm" | "base" | "lg";
}

export const CodeBlock = createWebComponent<CodeBlockProps>(
  'kc-code-block',
  ["theme","code","language","codeTheme","codeHighlight","proseSize"],
  {  },
);

export interface ConfirmProps extends WebComponentProps {
  /** 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. */
  data?: Record<string, unknown>;
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
  cardId?: string;
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
  heading?: string;
  /** Focus the default action on mount (off by default — no focus-stealing). Attribute: `autofocus`. */
  autofocus?: boolean;
  /** Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'action', action:'…' }`. */
  resolution?: Record<string, unknown>;
}

export const Confirm = createWebComponent<ConfirmProps>(
  'kc-confirm',
  ["theme","data","cardId","heading","autofocus","resolution"],
  {  },
);

export interface ContextProps extends WebComponentProps {
  /** Token-usage data. Set as a JS property. */
  context?: { usedTokens: number; maxTokens: number; inputTokens?: number; outputTokens?: number; reasoningTokens?: number; cacheTokens?: number; estimatedCost?: number };
  /** Fraction (0–1) above which the meter turns yellow. Defaults to `0.7` (70%). */
  warnThreshold?: number;
  /** Fraction (0–1) above which the meter turns red. Defaults to `0.9` (90%). */
  dangerThreshold?: number;
  /** Fires when the computed severity level changes (ok → warn → danger or back). `detail.level` is `'ok'`, `'warn'`, or `'danger'`. */
  onThresholdChange?: (event: CustomEvent<{ level: "ok" | "warn" | "danger" }>) => void;
}

export const Context = createWebComponent<ContextProps>(
  'kc-context',
  ["theme","context","warnThreshold","dangerThreshold"],
  { onThresholdChange: 'kc-threshold-change' },
);

export interface ConversationsProps extends WebComponentProps {
  /** 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. */
  groups: { id: string; userId?: undefined | string; teamId?: undefined | string; name: string; sortOrder: number; createdAt: 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. */
  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 }[];
  /** The id of the currently-open conversation, highlighted in the list. */
  activeId?: string;
  /** A conversation was selected. */
  onConversationSelect?: (event: CustomEvent<{ id: string }>) => void;
  /** The "New chat" button was clicked. */
  onNewChat?: (event: CustomEvent<Record<string, never>>) => void;
  /** The sidebar toggle was clicked. */
  onToggleSidebar?: (event: CustomEvent<Record<string, never>>) => void;
}

export const Conversations = createWebComponent<ConversationsProps>(
  'kc-conversations',
  ["theme","groups","conversations","activeId"],
  { onConversationSelect: 'kc-conversation-select', onNewChat: 'kc-new-chat', onToggleSidebar: 'kc-toggle-sidebar' },
);

export interface EmbedProps extends WebComponentProps {
  /** Stable card id correlating every emitted event. Set as an attribute or property. */
  cardId?: string;
  /** The embed payload (provider + id/url + options). Set as a JS **property** (object). */
  data?: { provider: "youtube" | "vimeo" | "generic"; id?: string; url?: string; title?: string; poster?: string; start?: number; aspectRatio?: "16:9" | "4:3" | "1:1" | "9:16" };
}

export const Embed = createWebComponent<EmbedProps>(
  'kc-embed',
  ["theme","cardId","data"],
  {  },
);

export interface EmptyProps extends WebComponentProps {
  /** Title text. Attribute: `empty-title` (`title` is a global HTML attribute). */
  emptyTitle?: string;
  /** Description text. */
  description?: string;
}

export const Empty = createWebComponent<EmptyProps>(
  'kc-empty',
  ["theme","emptyTitle","description"],
  {  },
);

export interface FeedbackBarProps extends WebComponentProps {
  /** The banner label (e.g. "Was this helpful?"). Attribute: `bar-title` (`title` is avoided — it's a global HTML attribute). */
  barTitle?: string;
  /** When set, a not-helpful vote opens an optional detail form before the thank-you confirmation. Attribute: `collect-detail`. */
  collectDetail?: boolean;
  /** Optional category chips for the detail form. Set as a JS property (array). */
  categories?: string[];
  /** Heading for the detail form. Attribute: `detail-title`. */
  detailTitle?: string;
  /** Placeholder for the detail comment box. Attribute: `detail-placeholder`. */
  detailPlaceholder?: string;
  /** Submit button label in the detail form. Attribute: `submit-label`. */
  submitLabel?: string;
  /** Confirmation copy shown after a vote/submit. Attribute: `thanks-message`. */
  thanksMessage?: string;
  /** The user dismissed the banner. */
  onClose?: (event: CustomEvent) => void;
  /** The user rated the response. `value` is `'helpful'` or `'not-helpful'`. */
  onFeedback?: (event: CustomEvent<{ value: "helpful" | "not-helpful" }>) => void;
  /** The user submitted the optional detail form (`collect-detail`). */
  onFeedbackDetail?: (event: CustomEvent<{ value: "helpful" | "not-helpful"; category?: undefined | string; comment?: undefined | string }>) => void;
}

export const FeedbackBar = createWebComponent<FeedbackBarProps>(
  'kc-feedback-bar',
  ["theme","barTitle","collectDetail","categories","detailTitle","detailPlaceholder","submitLabel","thanksMessage"],
  { onClose: 'kc-close', onFeedback: 'kc-feedback', onFeedbackDetail: 'kc-feedback-detail' },
);

export interface FileTreeProps extends WebComponentProps {
  /** The files to render. Set as a JS property (array of `{ path, url?, code?, language?, type? }`). */
  files: { path: string; url?: undefined | string; code?: undefined | string; language?: undefined | string; type?: undefined | "html" | "pdf" | "image" | "other" }[];
  /** Selected file path — highlighted in the tree. */
  activeFile?: string;
  /** Folder paths expanded initially. Omit to start with all folders open. */
  defaultExpanded?: string[];
  /** Fired when a file is selected. `detail.path` = the file's path. */
  onSelect?: (event: CustomEvent<{ path: string }>) => void;
}

export const FileTree = createWebComponent<FileTreeProps>(
  'kc-file-tree',
  ["theme","files","activeFile","defaultExpanded"],
  { onSelect: 'kc-select' },
);

export interface FileUploadProps extends WebComponentProps {
  /** Allow selecting multiple files (default true). */
  multiple?: boolean;
  /** `accept` attribute for the file picker (e.g. `image/*`). */
  accept?: string;
  /** Disable the dropzone — no clicking, no drag-and-drop. */
  disabled?: boolean;
  /** Default dropzone label (overridable via the default slot). */
  label?: string;
  /** Files were picked or dropped. */
  onFilesAdded?: (event: CustomEvent<{ files: File[] }>) => void;
}

export const FileUpload = createWebComponent<FileUploadProps>(
  'kc-file-upload',
  ["theme","multiple","accept","disabled","label"],
  { onFilesAdded: 'kc-files-added' },
);

export interface FormProps extends WebComponentProps {
  /** 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). */
  data?: Record<string, unknown>;
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
  cardId?: string;
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
  heading?: string;
  /** Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{…} }`. */
  resolution?: Record<string, unknown>;
}

export const Form = createWebComponent<FormProps>(
  'kc-form',
  ["theme","data","cardId","heading","resolution"],
  {  },
);

export interface ImageProps extends WebComponentProps {
  /** Base64-encoded image data (pair with `media-type`). */
  base64?: string;
  /** Raw image bytes (set as a JS property). */
  bytes?: Uint8Array;
  /** Alt text. */
  alt?: string;
  /** MIME type (default `image/png`). */
  mediaType?: string;
}

export const Image = createWebComponent<ImageProps>(
  'kc-image',
  ["theme","base64","bytes","alt","mediaType"],
  {  },
);

export interface LinkPreviewProps extends WebComponentProps {
  /** Stable card id correlating every emitted event. Set as an attribute or property. */
  cardId?: string;
  /** The link payload (OG metadata). Set as a JS **property** (object). */
  data?: { url: string; title?: string; description?: string; image?: string; imageAlt?: string; favicon?: string; domain?: string; siteName?: string };
}

export const LinkPreview = createWebComponent<LinkPreviewProps>(
  'kc-link-preview',
  ["theme","cardId","data"],
  {  },
);

export interface LoaderProps extends WebComponentProps {
  /** The animation style: `'circular' | 'classic' | 'pulse' | 'pulse-dot' | 'dots' | 'typing' | 'wave' | 'bars' | 'terminal' | 'text-blink' | 'text-shimmer' | 'loading-dots'`. Defaults to `'circular'`. */
  variant?: "circular" | "classic" | "pulse" | "pulse-dot" | "dots" | "typing" | "wave" | "bars" | "terminal" | "text-blink" | "text-shimmer" | "loading-dots";
  /** Loader size: `'sm' | 'md' | 'lg'`. Defaults to `'md'`. */
  size?: "sm" | "lg" | "md";
  /** Label for the text-based variants. */
  text?: string;
}

export const Loader = createWebComponent<LoaderProps>(
  'kc-loader',
  ["theme","variant","size","text"],
  {  },
);

export interface MarkdownProps extends WebComponentProps {
  /** The markdown source to render. */
  content: string;
  /** Text/markdown sizing. */
  proseSize?: "xs" | "sm" | "base" | "lg";
  /** Shiki theme for fenced code blocks. */
  codeTheme?: string;
  /** Disable syntax highlighting (no Shiki loads). */
  codeHighlight?: boolean;
}

export const Markdown = createWebComponent<MarkdownProps>(
  'kc-markdown',
  ["theme","content","proseSize","codeTheme","codeHighlight"],
  {  },
);

export interface MessageProps extends WebComponentProps {
  /** The full message object. Set as a JS property. */
  message?: { id: string; role: "user" | "assistant"; content: string; reasoning?: { text: string; label?: string }; tools?: { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; output?: Record<string, unknown>; toolCallId?: string; errorText?: string }[]; attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string } };
  /** Convenience for simple cases when not passing a `message` object. */
  role?: "user" | "assistant";
  /** Convenience content (used when `message` is not set). */
  content?: string;
  /** Force markdown on/off. Defaults to on for assistant, off for user. */
  markdown?: boolean;
  /** Text/markdown sizing for the message body. */
  proseSize?: "xs" | "sm" | "base" | "lg";
  /** Shiki theme name used for fenced code blocks in the content. */
  codeTheme?: string;
  /** Disable syntax highlighting for code blocks (no Shiki loads). */
  codeHighlight?: boolean;
  /** Whether the action bar is always visible (`'always'`, default) or only revealed on hover of the message row (`'hover'`). */
  actionsReveal?: "always" | "hover";
  /** Convenience avatar image URL (used when `message.avatar` is not set). */
  avatarSrc?: string;
  /** Convenience avatar fallback text (used when `message.avatar` is not set). */
  avatarFallback?: string;
  /** An action button was clicked. `action` is the built-in name or custom id. */
  onMessageAction?: (event: CustomEvent<{ messageId: string; action: string }>) => void;
}

export const Message = createWebComponent<MessageProps>(
  'kc-message',
  ["theme","message","role","content","markdown","proseSize","codeTheme","codeHighlight","actionsReveal","avatarSrc","avatarFallback"],
  { onMessageAction: 'kc-message-action' },
);

export interface ModelSwitcherProps extends WebComponentProps {
  /** The selectable models. Set as a JS property (array). */
  models: { id: string; name: string; provider?: undefined | string; description?: undefined | string; group?: undefined | string }[];
  /** The currently-selected model id. Defaults to the first model. */
  currentModel?: string;
  /** A model was selected. */
  onModelChange?: (event: CustomEvent<{ modelId: string }>) => void;
}

export const ModelSwitcher = createWebComponent<ModelSwitcherProps>(
  'kc-model-switcher',
  ["theme","models","currentModel"],
  { onModelChange: 'kc-model-change' },
);

export interface PopoverProps extends WebComponentProps {
  /** Floating placement relative to the trigger (floating-ui placement). */
  placement?: "top" | "right" | "bottom" | "left" | "top-start" | "top-end" | "right-start" | "right-end" | "bottom-start" | "bottom-end" | "left-start" | "left-end";
  /** Gap in px between the trigger and the panel. */
  gutter?: number;
  /** 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. */
  open?: boolean;
  /** The popover wants to open or close (click, Escape, or outside-click). */
  onOpenChange?: (event: CustomEvent<{ open: boolean }>) => void;
}

export const Popover = createWebComponent<PopoverProps>(
  'kc-popover',
  ["theme","placement","gutter","open"],
  { onOpenChange: 'kc-open-change' },
);

export interface PromptInputProps extends WebComponentProps {
  /** 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. */
  value?: string;
  /** Placeholder text shown in the empty input. */
  placeholder?: string;
  /** Disable the input and submit button entirely (non-interactive). */
  disabled?: boolean;
  /** Show the loading/streaming state and block submit (use while awaiting a reply). */
  loading?: boolean;
  /** Starter prompts shown above the input. 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";
  /** Slash commands — when set, typing `/` opens the command palette. Set as a JS property. */
  slashCommands?: { id: string; label: string; description?: string; category?: string }[];
  /** Command ids to highlight as active. */
  slashActiveIds?: string[];
  /** Single-line palette rows. */
  slashCompact?: boolean;
  /** Show a Search (Globe) button in the left toolbar; clicking it fires a `search` event. */
  search?: boolean;
  /** Show a Voice (Mic) button in the left toolbar; clicking it fires a `voice` event. */
  voice?: boolean;
  /** 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`. */
  stoppable?: boolean;
  /** 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). */
  attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[];
  /** The Search (Globe) toolbar button was clicked. */
  onSearch?: (event: CustomEvent<Record<string, never>>) => void;
  /** A slash command was chosen from the palette. */
  onSlashSelect?: (event: CustomEvent<{ command: { id: string; label: string; description?: undefined | string; category?: undefined | string } }>) => void;
  /** The Stop button was clicked while `stoppable` and `loading` are both true. */
  onStop?: (event: CustomEvent<Record<string, never>>) => void;
  /** The user submitted the prompt (Enter or send button) with its attachments. */
  onSubmit?: (event: CustomEvent<{ value: string; attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
  /** A suggestion was clicked while `suggestion-mode="fill"`. */
  onSuggestionClick?: (event: CustomEvent<{ value: string }>) => void;
  /** A custom `<kc-action>` toolbar button was clicked. `action` is the `id` of the `<kc-action>` element that was clicked. */
  onToolbarAction?: (event: CustomEvent<{ action: string }>) => void;
  /** The input text changed (fires on every keystroke). */
  onValueChange?: (event: CustomEvent<{ value: string }>) => void;
  /** The Voice (Mic) toolbar button was clicked. */
  onVoice?: (event: CustomEvent<Record<string, never>>) => void;
}

export const PromptInput = createWebComponent<PromptInputProps>(
  'kc-prompt-input',
  ["theme","value","placeholder","disabled","loading","suggestions","suggestionMode","slashCommands","slashActiveIds","slashCompact","search","voice","stoppable","attachments"],
  { onSearch: 'kc-search', onSlashSelect: 'kc-slash-select', onStop: 'kc-stop', onSubmit: 'kc-submit', onSuggestionClick: 'kc-suggestion-click', onToolbarAction: 'kc-toolbar-action', onValueChange: 'kc-value-change', onVoice: 'kc-voice' },
);

export interface ReasoningProps extends WebComponentProps {
  /** The reasoning text to display. */
  text: string;
  /** Trigger label. */
  label?: string;
  /** Controlled open state — set as a property (`el.open = true`). Omit for uncontrolled (the trigger toggles it). */
  open?: boolean;
  /** While true, auto-expands (and re-collapses when it flips false). */
  streaming?: boolean;
  /** Render `text` as markdown. */
  markdown?: boolean;
  /** Open state changed (via the trigger or streaming auto-open). */
  onOpenChange?: (event: CustomEvent<{ open: boolean }>) => void;
}

export const Reasoning = createWebComponent<ReasoningProps>(
  'kc-reasoning',
  ["theme","text","label","open","streaming","markdown"],
  { onOpenChange: 'kc-open-change' },
);

export interface RemoteProps extends WebComponentProps {
  /** The remote card URL. Attribute: `src`. */
  src?: string;
  /** Exact provider origin (https: or http://localhost for dev). Attribute: `provider-origin`. */
  providerOrigin?: string;
  /** The card envelope to render. JS property only. */
  envelope?: Record<string, unknown>;
  /** Optional routing policy. JS property only. */
  policy?: Record<string, unknown>;
}

export const Remote = createWebComponent<RemoteProps>(
  'kc-remote',
  ["theme","src","providerOrigin","envelope","policy"],
  {  },
);

export interface ResizableProps extends WebComponentProps {
  /** Layout axis: `horizontal` (row, default) or `vertical` (column). */
  orientation?: "horizontal" | "vertical";
  /** Which item index is maximized (null = none). Declarative source of truth. */
  maximizedIndex?: null | number;
  /** Fired on drag-end / keyboard resize / visibility change. `detail.sizes` = panel sizes in percent. */
  onChange?: (event: CustomEvent<{ sizes: number[] }>) => void;
  /** Observe layout maximize state. */
  onMaximizeChange?: (event: CustomEvent<{ maximized: boolean; index: null | number }>) => void;
}

export const Resizable = createWebComponent<ResizableProps>(
  'kc-resizable',
  ["theme","orientation","maximizedIndex"],
  { onChange: 'kc-change', onMaximizeChange: 'kc-maximize-change' },
);

export interface ResizableItemProps extends WebComponentProps {
  /** Initial main-axis size: `"280px"` (fixed) or `"25%"`/`25` (percent). Omitted → flexible. */
  size?: string;
  /** Minimum size during resize (px or %). */
  min?: string;
  /** Maximum size during resize (px or %). */
  max?: string;
  /** Fix this panel's size; adjacent dividers become non-draggable. */
  locked?: boolean;
  /** Hide this panel; its divider is dropped and the rest reflow. */
  hidden?: boolean;
  onChange?: (event: CustomEvent<unknown>) => void;
  onMaximizeChange?: (event: CustomEvent<unknown>) => void;
}

export const ResizableItem = createWebComponent<ResizableItemProps>(
  'kc-resizable-item',
  ["theme","size","min","max","locked","hidden"],
  { onChange: 'kc-change', onMaximizeChange: 'kc-maximize-change' },
);

export interface ResponseStreamProps extends WebComponentProps {
  /** Text to stream. A string, or an `AsyncIterable<string>` (set as a JS property — async iterables can't be HTML attributes). */
  text?: string | AsyncIterable<string>;
  /** Reveal animation. */
  mode?: "typewriter" | "fade";
  /** Characters/segments per tick. */
  speed?: number;
  /** Element tag to render as. */
  as?: string;
  /** Streaming finished. */
  onComplete?: (event: CustomEvent) => void;
}

export const ResponseStream = createWebComponent<ResponseStreamProps>(
  'kc-response-stream',
  ["theme","text","mode","speed","as"],
  { onComplete: 'kc-complete' },
);

export interface ScopePickerProps extends WebComponentProps {
  /** Authors to offer as scope filters. Set as a JS property. */
  availableAuthors: string[];
  /** Tags to offer as scope filters. Set as a JS property. */
  availableTags: string[];
  /** The label shown on the trigger for the active scope. */
  currentLabel?: string;
  /** A scope was chosen (`undefined` filters = "All Content"). */
  onScopeChange?: (event: CustomEvent<{ filters: undefined | { tags?: undefined | string[]; authors?: undefined | string[]; contentType?: undefined | "transcript" | "markdown"; dateRange?: undefined | { from: string; to: string } } }>) => void;
}

export const ScopePicker = createWebComponent<ScopePickerProps>(
  'kc-scope-picker',
  ["theme","availableAuthors","availableTags","currentLabel"],
  { onScopeChange: 'kc-scope-change' },
);

export interface ScrollButtonProps extends WebComponentProps {
  /** 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="...">`. */
  for?: string;
  /** Button visual variant: `'outline' | 'ghost' | 'default'`. Defaults to `'outline'`. */
  variant?: "ghost" | "default" | "outline";
  /** Button size token. Defaults to `'icon'` (square). */
  size?: "sm" | "lg" | "md" | "icon" | "icon-sm";
  /** Emitted when the user clicks the button and `scrollToBottom()` is called. Carries no detail — consumers use it to know a manual scroll occurred. */
  onScroll?: (event: CustomEvent) => void;
}

export const ScrollButton = createWebComponent<ScrollButtonProps>(
  'kc-scroll-button',
  ["theme","for","variant","size"],
  { onScroll: 'kc-scroll' },
);

export interface SkillsProps extends WebComponentProps {
  /** The active skills to badge. Set as a JS property. */
  skills: { id: string; name: string }[];
}

export const Skills = createWebComponent<SkillsProps>(
  'kc-skills',
  ["theme","skills"],
  {  },
);

export interface SourceProps extends WebComponentProps {
  /** The URL this citation links to (the domain also seeds the default label/favicon). */
  href?: string;
  /** Trigger label (defaults to the domain). */
  label?: string;
  /** Hover-card headline. Attribute: `headline` (`title` is avoided — it's a global HTML attribute that reflects in a CE constructor and breaks it). */
  headline?: string;
  /** Hover-card body text describing the source. */
  description?: string;
  /** Show the source's favicon next to the trigger label. */
  showFavicon?: boolean;
}

export const Source = createWebComponent<SourceProps>(
  'kc-source',
  ["theme","href","label","headline","description","showFavicon"],
  {  },
);

export interface SourcesProps extends WebComponentProps {
  /** The sources to render. Set as a JS property. */
  sources: { href: string; title?: undefined | string; description?: undefined | string; label?: undefined | string; showFavicon?: undefined | boolean }[];
  /** Show favicons on all items (per-item `showFavicon` overrides). */
  showFavicon?: boolean;
  /** When true, each citation chip is labelled with its 1-based index in the merged (prop + declarative-children) list (`[1]`, `[2]`, …) instead of the per-item `label` or domain fallback. HTML attribute: `numbered` (boolean — bare attribute or `numbered="true"`). JS property: `el.numbered = true`. */
  numbered?: boolean;
}

export const Sources = createWebComponent<SourcesProps>(
  'kc-sources',
  ["theme","sources","showFavicon","numbered"],
  {  },
);

export interface SuggestionsProps extends WebComponentProps {
  /** The suggestions. Strings, or `{ label, value }` when the displayed text and the emitted value differ. Set as a JS property. */
  suggestions: (string | { label: string; value?: undefined | string })[];
  /** Chip style: `'outline'` (default), `'ghost'`, or `'default'` (filled). */
  variant?: "ghost" | "default" | "outline";
  /** Size preset for each chip. Defaults to the pill default (`'lg'`); pass `'sm'` for smaller pills (or `'md'`). */
  size?: "sm" | "lg" | "md" | "icon" | "icon-sm";
  /** Full-width left-aligned rows instead of pills. */
  block?: boolean;
  /** Substring to highlight within each suggestion. */
  highlight?: string;
  /** A suggestion was clicked. */
  onSelect?: (event: CustomEvent<{ value: string }>) => void;
}

export const Suggestions = createWebComponent<SuggestionsProps>(
  'kc-suggestions',
  ["theme","suggestions","variant","size","block","highlight"],
  { onSelect: 'kc-select' },
);

export interface SwitchProps extends WebComponentProps {
  /** Initial checked state. Bare attribute (`<kc-switch checked>`) turns it on. */
  checked?: boolean;
  /** Disable interaction. */
  disabled?: boolean;
  /** Accessible label. */
  label?: string;
  /** The toggle changed. */
  onChange?: (event: CustomEvent<{ checked: boolean }>) => void;
}

export const Switch = createWebComponent<SwitchProps>(
  'kc-switch',
  ["theme","checked","disabled","label"],
  { onChange: 'kc-change' },
);

export interface TasksProps extends WebComponentProps {
  /** The tasks definition (the CardEnvelope.data). Set as a JS PROPERTY: `el.data = { tasks:[…], selectAll, confirmLabel, … }`. Import `TasksCardData` from `@kitn.ai/chat` for the full shape. */
  data?: Record<string, unknown>;
  /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
  cardId?: string;
  /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
  heading?: string;
  /** Set when the user resolved this card; renders the read-only view. Property: `el.resolution = { kind:'submit', data:{ selected:[…] } }`. */
  resolution?: Record<string, unknown>;
}

export const Tasks = createWebComponent<TasksProps>(
  'kc-tasks',
  ["theme","data","cardId","heading","resolution"],
  {  },
);

export interface TextShimmerProps extends WebComponentProps {
  /** The text to shimmer. */
  text?: string;
  /** Element tag to render as (default `span`). */
  as?: string;
  /** Animation duration in seconds. */
  duration?: number;
  /** Gradient spread (5–45). */
  spread?: number;
}

export const TextShimmer = createWebComponent<TextShimmerProps>(
  'kc-text-shimmer',
  ["theme","text","as","duration","spread"],
  {  },
);

export interface ThinkingBarProps extends WebComponentProps {
  /** The shimmering label, e.g. "Thinking…". */
  text?: string;
  /** When true, show a "stop" affordance that fires a `stop` event. */
  stoppable?: boolean;
  /** Label for the stop affordance. */
  stopLabel?: string;
  /** The "stop / answer now" affordance was clicked. */
  onStop?: (event: CustomEvent) => void;
}

export const ThinkingBar = createWebComponent<ThinkingBarProps>(
  'kc-thinking-bar',
  ["theme","text","stoppable","stopLabel"],
  { onStop: 'kc-stop' },
);

export interface ToolProps extends WebComponentProps {
  /** The tool-call to display. Set as a JS property. */
  tool?: { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; output?: Record<string, unknown>; toolCallId?: string; errorText?: string };
  /** Start expanded. */
  open?: boolean;
}

export const Tool = createWebComponent<ToolProps>(
  'kc-tool',
  ["theme","tool","open"],
  {  },
);

export interface VoiceInputProps extends WebComponentProps {
  /** Transcriber the host supplies — records audio, returns the text. This is a **function-valued property** (`el.transcribe = async blob => '...'`) because a value-returning callback can't be modelled as a fire-and-forget event. */
  transcribe?: (audio: Blob) => Promise<string>;
  /** Disable the mic button (non-interactive). */
  disabled?: boolean;
  /** Raw audio captured (before transcription) — for hosts that prefer to handle transcription themselves instead of via the `transcribe` property. */
  onAudioCaptured?: (event: CustomEvent<{ blob: Blob }>) => void;
  /** Transcription completed (the `transcribe` property resolved). */
  onTranscription?: (event: CustomEvent<{ text: string }>) => void;
}

export const VoiceInput = createWebComponent<VoiceInputProps>(
  'kc-voice-input',
  ["theme","transcribe","disabled"],
  { onAudioCaptured: 'kc-audio-captured', onTranscription: 'kc-transcription' },
);

export interface WorkspaceProps extends WebComponentProps {
  /** Pre-bucketed conversation groups for the sidebar. Set as a JS property. */
  groups: { id: string; userId?: undefined | string; teamId?: undefined | string; name: string; sortOrder: number; createdAt: string }[];
  /** Flat conversation list (auto-bucketed if `groups` is empty). 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 }[];
  /** Id of the open conversation, highlighted in the sidebar. */
  activeId?: string;
  /** The active conversation's message thread, newest last. Set as a JS property. */
  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 } }[];
  value?: string;
  placeholder?: string;
  loading?: boolean;
  suggestions?: string[];
  suggestionMode?: "submit" | "fill";
  proseSize?: "xs" | "sm" | "base" | "lg";
  codeTheme?: string;
  codeHighlight?: boolean;
  chatTitle?: string;
  models?: { id: string; name: string; provider?: string; description?: string; group?: string }[];
  currentModel?: string;
  context?: { usedTokens: number; maxTokens: number; inputTokens?: number; outputTokens?: number; estimatedCost?: number };
  scrollButton?: boolean;
  search?: boolean;
  voice?: boolean;
  slashCommands?: { id: string; label: string; description?: string; category?: string }[];
  slashActiveIds?: string[];
  slashCompact?: boolean;
  /** Sidebar default width as a percent of the workspace (default 22). */
  sidebarWidth?: number;
  /** Sidebar min width in px (default 200). */
  sidebarMinWidth?: number;
  /** Sidebar max width in px (default 420). */
  sidebarMaxWidth?: number;
  /** Controlled collapsed state. Set this as a JS property (`el.sidebarCollapsed = true`) to drive the sidebar from your app, updating it in response to the `kc-sidebar-toggle` event. Omit for uncontrolled (the element manages it). */
  sidebarCollapsed?: boolean;
  /** Initial collapsed state when uncontrolled (default false). Use the `default-sidebar-collapsed` attribute to start collapsed in plain HTML. */
  defaultSidebarCollapsed?: boolean;
  /** A conversation was selected in the sidebar. */
  onConversationSelect?: (event: CustomEvent<{ id: string }>) => void;
  /** An action button on a message was clicked. */
  onMessageAction?: (event: CustomEvent<{ messageId: string; action: string }>) => void;
  /** The header model switcher changed. */
  onModelChange?: (event: CustomEvent<{ modelId: string }>) => void;
  /** The "New chat" button was clicked. */
  onNewChat?: (event: CustomEvent<Record<string, never>>) => void;
  /** The Search button was clicked. */
  onSearch?: (event: CustomEvent<Record<string, never>>) => void;
  /** The sidebar was collapsed or expanded. */
  onSidebarToggle?: (event: CustomEvent<{ collapsed: boolean }>) => void;
  /** A slash command was chosen from the palette. */
  onSlashSelect?: (event: CustomEvent<{ command: { id: string; label: string; description?: undefined | string; category?: undefined | string } }>) => void;
  /** User submitted a message. */
  onSubmit?: (event: CustomEvent<{ value: string; attachments: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[] }>) => void;
  /** A suggestion chip was clicked (only in `suggestion-mode="fill"`). */
  onSuggestionClick?: (event: CustomEvent<{ value: string }>) => void;
  /** Fired on every input change. */
  onValueChange?: (event: CustomEvent<{ value: string }>) => void;
  /** The Mic / voice button was clicked. */
  onVoice?: (event: CustomEvent<Record<string, never>>) => void;
}

export const Workspace = createWebComponent<WorkspaceProps>(
  'kc-workspace',
  ["theme","groups","conversations","activeId","messages","value","placeholder","loading","suggestions","suggestionMode","proseSize","codeTheme","codeHighlight","chatTitle","models","currentModel","context","scrollButton","search","voice","slashCommands","slashActiveIds","slashCompact","sidebarWidth","sidebarMinWidth","sidebarMaxWidth","sidebarCollapsed","defaultSidebarCollapsed"],
  { onConversationSelect: 'kc-conversation-select', onMessageAction: 'kc-message-action', onModelChange: 'kc-model-change', onNewChat: 'kc-new-chat', onSearch: 'kc-search', onSidebarToggle: 'kc-sidebar-toggle', onSlashSelect: 'kc-slash-select', onSubmit: 'kc-submit', onSuggestionClick: 'kc-suggestion-click', onValueChange: 'kc-value-change', onVoice: 'kc-voice' },
);
