@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.
1,160 lines • 294 kB
JSON
[
{
"tag": "kai-agent-card",
"className": "KaiAgentCardElement",
"displayName": "AgentCard",
"props": [
{
"name": "name",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "The agent's name — the primary label. Attribute: `name`.",
"displayType": "undefined | string"
},
{
"name": "active",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Selected / focused state: highlighted border + surface. Attribute: `active`.",
"displayType": "undefined | false | true"
},
{
"name": "needsAttention",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Raise a prominent \"Needs you\" pill plus a glowing amber edge — the attention-routing signal that pulls focus to this agent. Attribute: `needs-attention`.",
"displayType": "undefined | false | true"
},
{
"name": "status",
"type": "undefined | { tone: \"working\" | \"idle\" | \"done\" | \"error\" | \"blocked\"; label?: undefined | string; pulse?: undefined | false | true }",
"optional": true,
"scalar": false,
"description": "Run status — a JS PROPERTY (object), not an attribute. Shape: `{ tone, label?, pulse? }`, where `tone` is one of `working` | `idle` | `done` | `error` | `blocked` (maps to the kit's tool hues), `label` is an optional short string beside the dot, and `pulse` animates the dot. Set it with `el.status = { tone: 'working', label: 'Working', pulse: true }`.",
"displayType": "undefined | { tone: \"working\" | \"idle\" | \"done\" | \"error\" | \"blocked\"; label?: undefined | string; pulse?: undefined | false | true }"
}
],
"events": [
{
"name": "kai-activate",
"detail": null,
"description": "The card was activated — clicked, or Enter / Space while focused. Promote this agent back to focus.",
"displayDetail": null
},
{
"name": "kai-menu",
"detail": null,
"description": "The trailing \"...\" kebab was clicked. The consumer opens its own menu; the card only surfaces the affordance (the click does not also activate the card).",
"displayDetail": null
}
],
"methods": [],
"composedFrom": [
{
"name": "AgentCard",
"group": "UI",
"storyId": "solid-advanced-primitives-agentcard--docs"
}
],
"tokens": [],
"parts": [
{
"name": "status",
"doc": "The leading tone-colored status dot.",
"recipe": "kai-agent-card::part(status) { width: 0.625rem; height: 0.625rem }"
},
{
"name": "menu",
"doc": "The trailing overflow (\"...\") menu button.",
"recipe": "kai-agent-card::part(menu) { opacity: 1 }"
}
]
},
{
"tag": "kai-artifact",
"className": "KaiArtifactElement",
"displayName": "Artifact",
"props": [
{
"name": "src",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "URL the preview iframe frames. Consumer-controlled.",
"displayType": "undefined | string"
},
{
"name": "files",
"type": "{ path: string; url?: undefined | string; code?: undefined | string; language?: undefined | string; type?: undefined | \"html\" | \"pdf\" | \"image\" | \"other\"; additions?: undefined | number; deletions?: undefined | number; status?: undefined | \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\" }[]",
"optional": false,
"scalar": false,
"description": "Files for the Code tab tree + each file's preview `url`. Set as a JS property (array).",
"typeName": "FileTreeFile[]",
"typeShape": "{ path: string; url?: undefined | string; code?: undefined | string; language?: undefined | string; type?: undefined | \"html\" | \"pdf\" | \"image\" | \"other\"; additions?: undefined | number; deletions?: undefined | number; status?: undefined | \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\" }[]",
"default": "[]",
"typeImport": "FileTreeFile",
"displayType": "{ path: string; url?: undefined | string; code?: undefined | string; language?: undefined | string; type?: undefined | \"html\" | \"pdf\" | \"image\" | \"other\"; additions?: undefined | number; deletions?: undefined | number; status?: undefined | \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\" }[]"
},
{
"name": "tab",
"type": "undefined | \"preview\" | \"code\"",
"optional": true,
"scalar": true,
"description": "Controlled active tab: `preview` or `code`. When set, the artifact follows it (re-asserted on change). Leave unset for an uncontrolled tab (see `defaultTab`).",
"displayType": "undefined | \"preview\" | \"code\""
},
{
"name": "defaultTab",
"type": "undefined | \"preview\" | \"code\"",
"optional": true,
"scalar": true,
"description": "Uncontrolled INITIAL tab (used only when `tab` is unset). Default `preview`. Seeds the starting tab; the user can then switch freely without the consumer re-asserting a controlled `tab`.",
"displayType": "undefined | \"preview\" | \"code\""
},
{
"name": "activeFile",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Selected file path — syncs the tree highlight, Code source, and preview.",
"displayType": "undefined | string"
},
{
"name": "sandbox",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "iframe `sandbox` override. Secure default `allow-scripts allow-forms` (NOT `allow-same-origin`).",
"default": "'allow-scripts allow-forms'",
"displayType": "undefined | string"
},
{
"name": "iframeTitle",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Accessible title for the preview iframe.",
"displayType": "undefined | string"
},
{
"name": "maximized",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Reflects the artifact's own maximized view-state (usually driven by the protocol).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "expandable",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show the expand-to-fill button (OPT-IN).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "openInTab",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show the open-in-new-tab button (OPT-IN).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "noNav",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Hide back/forward.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "noReload",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Hide reload.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "noHome",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Hide home.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "noPathField",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Hide the address field.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "noTabs",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Hide the Preview|Code toggle.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "standalone",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Standalone chrome: rounded corners + border (else square, borderless in-panel).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "readonlyPath",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show the address but make it read-only (visible, nav-tracking, non-editable).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "displayUrl",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Friendly address shown in the path field instead of the real current url (read-only, non-navigable). Use when the framed url is not consumer-facing (e.g. a `data:` blob) so a clean address shows instead of leaking it. Scalar string: set as the `display-url` attribute or the `displayUrl` property.",
"displayType": "undefined | string"
}
],
"events": [
{
"name": "kai-file-select",
"detail": "{ path: string }",
"description": "Fired when a file is selected. `detail.path`.",
"displayDetail": "{ path: string }"
},
{
"name": "kai-maximize-change",
"detail": "{ maximized: false | true }",
"description": "Artifact's own maximize button toggled (consumer-observable; non-bubbling).",
"displayDetail": "{ maximized: false | true }"
},
{
"name": "kai-navigate",
"detail": "{ url: string }",
"description": "Fired when the preview navigates. `detail.url` = the new location.",
"displayDetail": "{ url: string }"
},
{
"name": "kai-tab-change",
"detail": "{ tab: \"preview\" | \"code\" }",
"description": "Fired when the Preview|Code tab changes. `detail.tab`.",
"displayDetail": "{ tab: \"preview\" | \"code\" }"
}
],
"methods": [
{
"name": "back",
"params": "",
"returns": "void",
"description": "Go back in the artifact's own history stack (no-op when there's no prior entry)."
},
{
"name": "forward",
"params": "",
"returns": "void",
"description": "Go forward in the history stack (no-op when there's no forward entry)."
},
{
"name": "reload",
"params": "",
"returns": "void",
"description": "Force-reload the current preview url (also re-renders an inline PDF)."
},
{
"name": "home",
"params": "",
"returns": "void",
"description": "Navigate to the `src` home url (no-op when there's no `src`)."
},
{
"name": "navigate",
"params": "url: string",
"returns": "void",
"description": "Push + load a url in the preview — the path-field submit path (fires kai-navigate)."
},
{
"name": "selectFile",
"params": "path: string",
"returns": "void",
"description": "Select a file by path: highlights the tree, shows its source, navigates the preview (fires kai-file-select + kai-navigate). Named selectFile to avoid the `activeFile` prop."
},
{
"name": "openExternal",
"params": "",
"returns": "void",
"description": "Open the current url in a new browser tab (no-op when there's no concrete url). Named openExternal, NOT openInTab — that's a prop (toolbar button visibility)."
},
{
"name": "maximize",
"params": "",
"returns": "void",
"description": "Enter the maximized view-state (fires kai-maximize-change{maximized:true}). Named maximize, NOT maximized — that's a prop."
},
{
"name": "restore",
"params": "",
"returns": "void",
"description": "Exit the maximized view-state (fires kai-maximize-change{maximized:false})."
}
],
"composedFrom": [
{
"name": "Artifact",
"group": "Components",
"storyId": "solid-advanced-elements-artifact--docs"
}
],
"tokens": []
},
{
"tag": "kai-attachments",
"className": "KaiAttachmentsElement",
"displayName": "Attachments",
"props": [
{
"name": "items",
"type": "{ id: string; type: \"file\" | \"source-document\"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]",
"optional": false,
"scalar": false,
"description": "The attachments to render. Set as a JS property (array).",
"typeName": "AttachmentData[]",
"typeShape": "{ id: string; type: \"file\" | \"source-document\"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]",
"default": "[]",
"typeImport": "AttachmentData",
"displayType": "AttachmentData[]"
},
{
"name": "variant",
"type": "undefined | \"grid\" | \"inline\" | \"list\"",
"optional": true,
"scalar": true,
"description": "Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows.",
"default": "'grid'",
"displayType": "undefined | \"grid\" | \"inline\" | \"list\""
},
{
"name": "hoverCard",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Wrap each item in a hover card that previews its details.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "removable",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show a remove button per item; clicking it fires a `kai-remove` event.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "showMediaType",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Also show the media type beneath the filename (non-grid variants).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "emptyText",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Text shown when `items` is empty.",
"displayType": "undefined | string"
}
],
"events": [
{
"name": "kai-remove",
"detail": "{ id: string }",
"description": "A remove button was clicked.",
"displayDetail": "{ id: string }"
}
],
"methods": [],
"composedFrom": [
{
"name": "Attachments",
"group": "Components",
"storyId": "solid-advanced-elements-attachments--docs"
},
{
"name": "Attachment",
"group": "Components",
"storyId": "solid-advanced-elements-attachment--docs"
},
{
"name": "AttachmentPreview",
"group": "Components",
"storyId": "solid-advanced-elements-attachmentpreview--docs"
},
{
"name": "AttachmentInfo",
"group": "Components",
"storyId": "solid-advanced-elements-attachmentinfo--docs"
},
{
"name": "AttachmentRemove",
"group": "Components",
"storyId": "solid-advanced-elements-attachmentremove--docs"
},
{
"name": "AttachmentHoverCard",
"group": "Components",
"storyId": "solid-advanced-elements-attachmenthovercard--docs"
},
{
"name": "AttachmentHoverCardTrigger",
"group": "Components",
"storyId": "solid-advanced-elements-attachmenthovercardtrigger--docs"
},
{
"name": "AttachmentHoverCardContent",
"group": "Components",
"storyId": "solid-advanced-elements-attachmenthovercardcontent--docs"
},
{
"name": "AttachmentEmpty",
"group": "Components",
"storyId": "solid-advanced-elements-attachmentempty--docs"
}
],
"tokens": [],
"parts": [
{
"name": "preview",
"doc": "The image shown in an attachment’s hover-card preview. Bounded by default (max ~320×256, aspect preserved) so a large image never blows up the card — raise or lower the cap from outside.",
"recipe": "kai-attachments::part(preview) { max-width: 32rem; max-height: 24rem }"
}
]
},
{
"tag": "kai-avatar",
"className": "KaiAvatarElement",
"displayName": "Avatar",
"props": [
{
"name": "src",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Image URL/data-URI. When absent, the `fallback` initials show instead.",
"displayType": "undefined | string"
},
{
"name": "alt",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Alt text for the image. Defaults to `fallback`.",
"displayType": "undefined | string"
},
{
"name": "fallback",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Short text shown when there's no image — usually initials (e.g. \"JD\", \"AI\").",
"default": "''",
"displayType": "undefined | string"
},
{
"name": "size",
"type": "undefined | \"sm\" | \"md\" | \"lg\"",
"optional": true,
"scalar": true,
"description": "Size token: `sm` | `md` (default) | `lg`.",
"default": "'md'",
"displayType": "undefined | \"sm\" | \"md\" | \"lg\""
}
],
"events": [],
"methods": [],
"composedFrom": [
{
"name": "Avatar",
"group": "UI",
"storyId": "solid-advanced-primitives-avatar--docs"
}
],
"tokens": []
},
{
"tag": "kai-badge",
"className": "KaiBadgeElement",
"displayName": "Badge",
"props": [
{
"name": "variant",
"type": "undefined | \"default\" | \"count\" | \"citation\"",
"optional": true,
"scalar": true,
"description": "`default` (muted pill) · `count` (compact number badge) · `citation` (filled primary, for inline citation markers). Defaults to `default`.",
"default": "'default'",
"displayType": "undefined | \"default\" | \"count\" | \"citation\""
}
],
"events": [],
"methods": [],
"composedFrom": [
{
"name": "Badge",
"group": "UI",
"storyId": "solid-advanced-primitives-badge--docs"
}
],
"tokens": [],
"parts": [
{
"name": "badge",
"doc": "The badge pill. Restyle its background, color, or shape; the `variant` prop (default/count/citation) sets the defaults.",
"recipe": "kai-badge::part(badge) { background: var(--color-primary); color: var(--color-primary-foreground) }"
}
]
},
{
"tag": "kai-button",
"className": "KaiButtonElement",
"displayName": "Button",
"props": [
{
"name": "variant",
"type": "undefined | \"default\" | \"subtle\" | \"ghost\" | \"outline\" | \"destructive\"",
"optional": true,
"scalar": true,
"description": "Visual style. `default` (filled), `subtle` (muted text, hover tint — the toolbar icon look), `ghost` (transparent, hover fill), `outline`, or `destructive`. Defaults to `default`.",
"default": "'default'",
"displayType": "undefined | \"default\" | \"subtle\" | \"ghost\" | \"outline\" | \"destructive\""
},
{
"name": "size",
"type": "undefined | \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\"",
"optional": true,
"scalar": true,
"description": "Size token. `icon` / `icon-sm` are square (for icon-only buttons); `sm` / `md` / `lg` size text buttons. Defaults to `md`.",
"default": "'md'",
"displayType": "undefined | \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\""
},
{
"name": "icon",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Leading icon: a named icon (e.g. `\"mic\"`, `\"plus\"`), an image URL/data-URI, or plain text. Renders before any slotted label.",
"displayType": "undefined | string"
},
{
"name": "iconTrailing",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Trailing icon, after the label (e.g. `\"chevron-down\"` for a menu affordance).",
"displayType": "undefined | string"
},
{
"name": "label",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Accessible name. REQUIRED for icon-only buttons (no visible text); ignored when you slot visible text, which already names the button.",
"displayType": "undefined | string"
},
{
"name": "disabled",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Disable the button (non-interactive, dimmed).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "full",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Stretch the button to the full width of its container (a block button) — e.g. a card CTA or a stacked action. Attribute: `full`.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "align",
"type": "undefined | \"start\" | \"center\" | \"end\"",
"optional": true,
"scalar": true,
"description": "Justify the button's content: `start`, `center` (default), or `end`. Combine with `full` for a full-width, left-aligned button.",
"default": "'center'",
"displayType": "undefined | \"start\" | \"center\" | \"end\""
},
{
"name": "type",
"type": "undefined | \"button\" | \"submit\" | \"reset\"",
"optional": true,
"scalar": true,
"description": "Native button `type`. Defaults to `button` (so it never submits a form).",
"default": "'button'",
"displayType": "undefined | \"button\" | \"submit\" | \"reset\""
}
],
"events": [
{
"name": "kai-click",
"detail": null,
"description": "The button was activated (pointer or keyboard). Carries no detail. The native `click` also bubbles (composed) for consumers who prefer it.",
"displayDetail": null
}
],
"methods": [
{
"name": "focus",
"params": "options?: FocusOptions",
"returns": "void",
"description": "Focus the inner `<button>` (host.focus() would focus the wrapper)."
},
{
"name": "blur",
"params": "",
"returns": "void",
"description": "Blur the inner `<button>`."
},
{
"name": "click",
"params": "",
"returns": "void",
"description": "Programmatically activate the button — runs the same path as a user click and fires kai-click. Forwarding to the inner button means `disabled` is respected automatically."
}
],
"composedFrom": [
{
"name": "Button",
"group": "UI",
"storyId": "solid-advanced-primitives-button--docs"
}
],
"tokens": [],
"slots": [
{
"name": "icon",
"mode": "replace",
"doc": "A custom leading icon (any inline SVG, inherits `currentColor`). Wins over the `icon` prop."
}
],
"parts": [
{
"name": "button",
"doc": "The button element. Restyle radius, padding, colors, or weight from outside; the `variant`/`size` props set the defaults.",
"recipe": "kai-button::part(button) { border-radius: 9999px; font-weight: 600 }"
}
]
},
{
"tag": "kai-card",
"className": "KaiCardElement",
"displayName": "Card",
"props": [
{
"name": "appearance",
"type": "undefined | \"outlined\" | \"filled\" | \"plain\" | \"accent\"",
"optional": true,
"scalar": true,
"description": "Surface treatment: `outlined` (default) | `filled` | `plain` | `accent`. Attribute: `appearance`.",
"default": "'outlined'",
"displayType": "undefined | \"outlined\" | \"filled\" | \"plain\" | \"accent\""
},
{
"name": "orientation",
"type": "undefined | \"vertical\" | \"horizontal\" | \"responsive\"",
"optional": true,
"scalar": true,
"description": "`vertical` (default, media on top) | `horizontal` (media at the start) | `responsive` (horizontal when the card's container is wide enough, else vertical — a container query on the card's own width). Attribute: `orientation`.",
"default": "'vertical'",
"displayType": "undefined | \"vertical\" | \"horizontal\" | \"responsive\""
},
{
"name": "collapse",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "The card width below which a `responsive` card collapses to vertical and the footer actions stack. A CSS length; default `28rem`. Attribute: `collapse`.",
"default": "'28rem'",
"displayType": "undefined | string"
},
{
"name": "dense",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Tighter spacing for dense lists. Attribute: `dense`.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "dismissible",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show a close (×) that hides the card and emits `kai-dismiss`. Attribute: `dismissible`. Off by default.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "href",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Render the whole card as a link. Attribute: `href`. Wins over `clickable`.",
"displayType": "undefined | string"
},
{
"name": "target",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "`target` for the `href` anchor. Attribute: `target`.",
"displayType": "undefined | string"
},
{
"name": "rel",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "`rel` for the `href` anchor. Attribute: `rel`.",
"displayType": "undefined | string"
},
{
"name": "clickable",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Make the whole card a button (`role=\"button\"`, Enter/Space, hover affordance) that emits `kai-card-click`. Attribute: `clickable`. Ignored when `href` is set.",
"default": "false",
"displayType": "undefined | false | true"
}
],
"events": [
{
"name": "kai-card-click",
"detail": null,
"description": "A `clickable`/`href` card was activated (click, or Enter/Space).",
"displayDetail": null
},
{
"name": "kai-dismiss",
"detail": null,
"description": "The card was dismissed via its × (it also hides itself).",
"displayDetail": null
}
],
"methods": [],
"composedFrom": [
{
"name": "Card",
"group": "UI",
"storyId": "solid-advanced-primitives-card--docs"
}
],
"tokens": [],
"slots": [
{
"name": "media",
"mode": "inject",
"doc": "Full-bleed media (image/video/illustration) at the top (vertical) or start (horizontal). Clipped to the card corners."
},
{
"name": "header",
"mode": "inject",
"doc": "Header content, e.g. a title. Rendered above the body."
},
{
"name": "header-actions",
"mode": "inject",
"doc": "An actions cluster pinned to the end of the header row."
},
{
"name": "footer",
"mode": "inject",
"doc": "Footer content rendered below the body."
},
{
"name": "footer-actions",
"mode": "inject",
"doc": "Action buttons pinned to the end of the footer. Do NOT combine with a clickable/href card (nested interactive)."
}
],
"parts": [
{
"name": "card",
"doc": "The card root (a div, or an a when href is set). Restyle its radius, border, or background; set --kai-card-spacing for padding/gaps (the dense prop sets the compact default).",
"recipe": "kai-card::part(card) { border-radius: 1rem; --kai-card-spacing: 1.5rem }"
},
{
"name": "media",
"doc": "The full-bleed media region. Cap or crop it from outside (e.g. a fixed height with object-fit).",
"recipe": "kai-card::part(media) { max-height: 12rem }"
},
{
"name": "header",
"doc": "The header row (header content + header-actions). Add a divider or adjust its alignment.",
"recipe": "kai-card::part(header) { border-bottom: 1px solid var(--color-border) }"
},
{
"name": "body",
"doc": "The default-slot body region.",
"recipe": "kai-card::part(body) { font-size: 0.9375rem }"
},
{
"name": "footer",
"doc": "The footer row (footer content + footer-actions).",
"recipe": "kai-card::part(footer) { border-top: 1px solid var(--color-border) }"
},
{
"name": "dismiss",
"doc": "The dismiss (×) button shown when dismissible. Recolor or reposition it from outside.",
"recipe": "kai-card::part(dismiss) { color: var(--color-muted-foreground) }"
}
]
},
{
"tag": "kai-cards",
"className": "KaiCardsElement",
"displayName": "Cards",
"props": [
{
"name": "cards",
"type": "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 } | { kind: \"dismissed\"; at?: undefined | string } | { kind: \"expired\"; reason?: undefined | string; at?: undefined | string } }[]",
"optional": true,
"scalar": false,
"description": "The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`.",
"displayType": "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 } | { kind: \"dismissed\"; at?: undefined | string } | { kind: \"expired\"; reason?: undefined | string; at?: undefined | string } }[]"
},
{
"name": "types",
"type": "undefined | Record<string, string>",
"optional": true,
"scalar": false,
"description": "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.",
"displayType": "undefined | Record<string, string>"
},
{
"name": "policy",
"type": "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; onReopen?: undefined | (cardId: string) => void; onError?: undefined | (cardId: string, message: string) => void; maxSendPromptMode?: undefined | \"compose\" | \"send\" }",
"optional": true,
"scalar": false,
"description": "Optional CardPolicy handling child events. Property: `el.policy`.",
"displayType": "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; onReopen?: undefined | (cardId: string) => void; onError?: undefined | (cardId: string, message: string) => void; maxSendPromptMode?: undefined | \"compose\" | \"send\" }"
}
],
"events": [
{
"name": "kai-card-resolved",
"detail": "{ cardId: string; resolution: { kind: \"action\"; action: string; payload?: unknown; at?: undefined | string } | { kind: \"submit\"; data: unknown; at?: undefined | string } | { kind: \"dismissed\"; at?: undefined | string } | { kind: \"expired\"; reason?: undefined | string; at?: undefined | string } }",
"description": "A child card transitioned to a resolved/deferred state (an action was chosen, a form/tasks submission landed, or it was dismissed) — re-emitted off the host as a non-bubbling convenience event so a consumer can observe resolution centrally without diffing the cards array. `detail` = `{ cardId, resolution }`. (A `reopen` un-resolves a card and has no `CardResolution`, so it does NOT fire this — observe reopen via the underlying bubbling `kai-card` event.)",
"displayDetail": "{ cardId: string; resolution: { kind: \"action\"; action: string; payload?: unknown; at?: undefined | string } | { kind: \"submit\"; data: unknown; at?: undefined | string } | { kind: \"dismissed\"; at?: undefined | string } | { kind: \"expired\"; reason?: undefined | string; at?: undefined | string } }"
}
],
"methods": [
{
"name": "resolve",
"params": "cardId: string, resolution: CardResolution",
"returns": "void",
"description": "Programmatically resolve a child card by id: set that envelope's `resolution` so the child re-renders into its read-only/resolved view — the imperative twin of the consumer mutating the cards array. No-op for an unknown id."
},
{
"name": "dismiss",
"params": "cardId: string",
"returns": "void",
"description": "Collapse a card to its re-openable stub from the host side — convenience for `resolve(cardId, { kind: 'dismissed' })`."
},
{
"name": "getCard",
"params": "cardId: string",
"returns": "HTMLElement | null",
"description": "Return the live child element node for a card id (or null) so consumers can call that card's own methods (focus/expand/…) without a shadow-DOM query."
}
],
"composedFrom": [
{
"name": "CardFallback",
"group": "Components",
"storyId": "solid-advanced-elements-cardfallback--docs"
}
],
"tokens": []
},
{
"tag": "kai-chain-of-thought",
"className": "KaiChainOfThoughtElement",
"displayName": "ChainOfThought",
"props": [
{
"name": "steps",
"type": "{ label: string; content?: undefined | string; id?: undefined | string }[]",
"optional": false,
"scalar": false,
"description": "The reasoning steps. Set as a JS property. Compound sub-parts collapse to this one data model (Route 1). Each `{ label, content?, id? }`.",
"typeName": "ChainOfThoughtStepData[]",
"typeShape": "{ label: string; content?: undefined | string; id?: undefined | string }[]",
"default": "[]",
"displayType": "{ label: string; content?: undefined | string; id?: undefined | string }[]"
},
{
"name": "type",
"type": "undefined | \"single\" | \"multiple\"",
"optional": true,
"scalar": true,
"description": "Open mode: `'multiple'` (default — any number of steps open at once) or `'single'` (at most one open; opening a step closes the others).",
"displayType": "undefined | \"single\" | \"multiple\""
},
{
"name": "value",
"type": "undefined | string | string[]",
"optional": true,
"scalar": false,
"description": "Controlled open step key(s). When set, it WINS over user interaction (the consumer owns the open set). String in `single` mode, string[] in `multiple` mode. Set as a JS property.",
"displayType": "undefined | string | string[]"
},
{
"name": "defaultValue",
"type": "undefined | string | string[]",
"optional": true,
"scalar": false,
"description": "Uncontrolled INITIAL open step key(s) — seeds which steps render expanded. Ignored once `value` is provided. Set as a JS property.",
"displayType": "undefined | string | string[]"
}
],
"events": [
{
"name": "kai-value-change",
"detail": "{ value: string | string[] }",
"description": "The open set changed — by user click OR an expand()/collapse()/toggle() call. `value` is a string in `single` mode, a string[] in `multiple` mode. (Maps Radix Accordion's onValueChange.)",
"displayDetail": "{ value: string | string[] }"
}
],
"methods": [
{
"name": "expand",
"params": "index?: number",
"returns": "void",
"description": "Open one step's detail by index, or — with no arg — ALL steps. In `single` mode opening one step closes the others (expand-all keeps the last)."
},
{
"name": "collapse",
"params": "index?: number",
"returns": "void",
"description": "Close one step's detail by index, or — with no arg — ALL steps."
},
{
"name": "toggle",
"params": "index?: number",
"returns": "void",
"description": "Flip one step's open state by index."
}
],
"composedFrom": [
{
"name": "ChainOfThoughtAccordion",
"group": "Components",
"storyId": "solid-advanced-elements-chainofthoughtaccordion--docs"
}
],
"tokens": []
},
{
"tag": "kai-chat",
"className": "KaiChatElement",
"displayName": "Chat",
"props": [
{
"name": "messages",
"type": "{ 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 }; feedback?: undefined | \"like\" | \"dislike\" }[]",
"optional": false,
"scalar": false,
"description": "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 = [...]`).",
"typeName": "ChatMessage[]",
"typeShape": "{ 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 }; feedback?: undefined | \"like\" | \"dislike\" }[]",
"default": "[]",
"typeImport": "ChatMessage",
"displayType": "{ 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 }; feedback?: undefined | \"like\" | \"dislike\" }[]"
},
{
"name": "value",
"type": "undefined | string | ({ type: \"text\"; text: string } | { type: \"entity\"; entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } })[]",
"optional": true,
"scalar": false,
"description": "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.",
"displayType": "undefined | string | ({ type: \"text\"; text: string } | { type: \"entity\"; entity: { kind: string; id: string; label: string; icon?: undefined | string; promptText?: undefined | string; data?: undefined | Record<string, unknown> } })[]"
},
{
"name": "placeholder",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Placeholder text shown in the empty input.",
"default": "'Send a message...'",
"displayType": "undefined | string"
},
{
"name": "loading",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "When true, shows the loading/streaming state and disables submit (use while awaiting the assistant's reply).",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "suggestions",
"type": "undefined | string[]",
"optional": true,
"scalar": false,
"description": "Starter prompts shown above the input when the thread is empty. Clicking one follows `suggestionMode`. Set as a JS property.",
"displayType": "undefined | string[]"
},
{
"name": "suggestionMode",
"type": "undefined | \"submit\" | \"fill\"",
"optional": true,
"scalar": true,
"description": "What clicking a suggestion does: `'submit'` (default) sends it immediately as if typed and submitted; `'fill'` just places it in the input.",
"default": "'submit'",
"displayType": "undefined | \"submit\" | \"fill\""
},
{
"name": "persistSuggestions",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "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.",
"default": "false",
"displayType": "undefined | false | true"
},
{
"name": "proseSize",
"type": "undefined | \"sm\" | \"lg\" | \"xs\" | \"base\"",
"optional": true,
"scalar": true,
"description": "Body/prose font scale for rendered markdown (`'xs' | 'sm' | 'base' | 'lg'`). Defaults to `'sm'`.",
"default": "'sm'",
"displayType": "undefined | \"sm\" | \"lg\" | \"xs\" | \"base\""
},
{
"name": "codeTheme",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`).",
"default": "'github-dark-dimmed'",
"displayType": "undefined | string"
},
{
"name": "codeHighlight",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Enable Shiki syntax highlighting in code blocks. Turn off to render plain `<pre>` blocks (lighter, no highlighter load). Default true.",
"default": "true",
"displayType": "undefined | false | true"
},
{
"name": "chatTitle",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "Optional header title shown on the left of the header.",
"displayType": "undefined | string"
},
{
"name": "models",
"type": "undefined | { id: string; name: string; provider?: undefined | string; description?: undefined | string; group?: undefined | string }[]",
"optional": true,
"scalar": false,
"description": "Optional model list. When set (>1 model) a ModelSwitcher is shown in the header and a `kai-model-change` event fires on selection.",
"displayType": "undefined | { id: string; name: string; provider?: undefined | string; description?: undefined | string; group?: undefined | string }[]"
},
{
"name": "currentModel",
"type": "undefined | string",
"optional": true,
"scalar": true,
"description": "The currently selected model id (pairs with `models`).",
"displayType": "undefined | string"
},
{
"name": "context",
"type": "undefined | { usedTokens: number; maxTokens: number; inputTokens?: undefined | number; outputTokens?: undefined | number; estimatedCost?: undefined | number }",
"optional": true,
"scalar": false,
"description": "Optional context-window token usage. When set, a Context token meter is shown in the header.",
"displayType": "ContextData | undefined"
},
{
"name": "scrollButton",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Show the scroll-to-bottom button inside the scroll area. Default true.",
"default": "true",
"displayType": "undefined | false | true"
},
{
"name": "headerStart",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "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.",
"displayType": "undefined | false | true"
},
{
"name": "headerEnd",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "Whether the host has `slot=\"header-end\"` content (right of the controls).",
"displayType": "undefined | false | true"
},
{
"name": "headerFull",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "REPLACE — full custom header in place of the built-in title/model/context bar.",
"displayType": "undefined | false | true"
},
{
"name": "sidebar",
"type": "undefined | false | true",
"optional": true,
"scalar": true,
"description": "INJECT — left sidebar column (e.g. a conversation list / your own nav).",
"displayType": "undefined | false | true"
},
{
"name