@kitn.ai/ui 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/dist/{Icon-DObhsRtm.js → Icon-DJDuE_-x.js} +1 -1
- package/dist/action-icons-xkBBVKkF.js +1 -0
- package/dist/agent-tooling/construct/schema-url.d.ts +9 -0
- package/dist/agent-tooling/construct/schema.d.ts +76 -1
- package/dist/agent-tooling/construct/templates.d.ts +45 -0
- package/dist/arrow-down-DU6wOl-h.js +1 -0
- package/dist/arrow-left-DD46kN17.js +1 -0
- package/dist/artifact-DoUAr1Fd.js +1 -0
- package/dist/assets/dev-DwzHsqYD.js +553 -0
- package/dist/{attachments-Qr4kg-2b.js → attachments-BLWPDiLH.js} +1 -1
- package/dist/{audio-visualizer-DToYdfSc.js → audio-visualizer-DhHicYvV.js} +1 -1
- package/dist/{badge-CdEu_jn2.js → badge-BGm7pcwi.js} +1 -1
- package/dist/{bell-DQAAOpDz.js → bell-CMEdSo7A.js} +1 -1
- package/dist/builder-app/App.d.ts +1 -0
- package/dist/builder-app/edit-guard.d.ts +8 -0
- package/dist/builder-app/main.d.ts +0 -0
- package/dist/builder-page/assets/index-C10OF-ur.js +72 -0
- package/dist/builder-page/assets/index-CFyKLBlu.css +1 -0
- package/dist/builder-page/index.html +20 -0
- package/dist/button-BM0YTTDQ.js +1 -0
- package/dist/{card-renderer-Jx70m6_l.js → card-renderer-Ds-wXc6_.js} +1 -1
- package/dist/check-BG1VfYjm.js +1 -0
- package/dist/{checkbox-group-B2_rpoPM.js → checkbox-group-CqXj8ehz.js} +1 -1
- package/dist/{checkbox-D7iRB3nL.js → checkbox-np_5mKxM.js} +1 -1
- package/dist/chevron-down-CZ4W0hy6.js +1 -0
- package/dist/chevron-right-5gdxkCTF.js +1 -0
- package/dist/{choice-card-m0BYqqOK.js → choice-card-BYBmPAT5.js} +1 -1
- package/dist/circle-C0lwjD2A.js +1 -0
- package/dist/circle-check-BfT1cxQU.js +1 -0
- package/dist/{circle-x-BPG0iPst.js → circle-x-DzUfvRqr.js} +1 -1
- package/dist/{code-block-ClMLKb7C.js → code-block-H6E5Eurc.js} +1 -1
- package/dist/{collapsible-B4DlWicL.js → collapsible-CBq1Tj6y.js} +1 -1
- package/dist/components/builder-composer-triggers.d.ts +62 -0
- package/dist/components/builder-layout.d.ts +54 -0
- package/dist/components/builder-message-actions.d.ts +63 -0
- package/dist/components/builder-panel-derived.d.ts +18 -0
- package/dist/components/builder-panel.d.ts +240 -0
- package/dist/components/builder-preview.d.ts +11 -0
- package/dist/components/builder-shell-controls.d.ts +69 -0
- package/dist/components/builder-skeleton.d.ts +58 -0
- package/dist/components/builder-start.d.ts +79 -0
- package/dist/components/builder-workspace-variants.d.ts +23 -0
- package/dist/components/captions.d.ts +66 -0
- package/dist/components/chat-thread.d.ts +27 -1
- package/dist/components/construct-form-paths.d.ts +54 -0
- package/dist/components/message.d.ts +5 -0
- package/dist/{composer-sQ0HcvyG.js → composer-B5IvgxAF.js} +1 -1
- package/dist/{confirm-card-rQWDQsnC.js → confirm-card-B63pZIfG.js} +1 -1
- package/dist/construct-cli.es.js +469 -125
- package/dist/construct-templates.d.ts +5 -0
- package/dist/construct-templates.js +266 -0
- package/dist/construct.js +269 -86
- package/dist/{context-0kg4ZX1J.js → context-Byqor78j.js} +1 -1
- package/dist/controllable-C4BSt73s.js +1 -0
- package/dist/{conversation-item-BxwQLaFJ.js → conversation-item-piXZFRWa.js} +1 -1
- package/dist/{conversation-list-BCfGN1JU.js → conversation-list-B5yu9cPg.js} +1 -1
- package/dist/{copy-n9u9rqpi.js → copy-DfI2NYEX.js} +1 -1
- package/dist/{create-tween-uPkArjIG.js → create-tween-C4j96_Aj.js} +1 -1
- package/dist/{create-tween-C-q-JCM-.js → create-tween-CHleHI8m.js} +1 -1
- package/dist/{create-tween-CRrJXxBd.js → create-tween-DQ6lOhdl.js} +1 -1
- package/dist/{create-tween-DZ3RRDHD.js → create-tween-DyjTgkJi.js} +1 -1
- package/dist/custom-elements.json +40 -5
- package/dist/{default-input-CaMrJkgH.js → default-input-Bwmt8Lai.js} +1 -1
- package/dist/define-B3OpsIDE.js +1 -0
- package/dist/define.js +41 -41
- package/dist/define.server.js +46 -46
- package/dist/{disclosure-WUImBbDv.js → disclosure-ZSAlD4wg.js} +1 -1
- package/dist/{dropdown-wZVeJOMd.js → dropdown-CSRhiMN2.js} +1 -1
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-actions.d.ts +12 -0
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-types.d.ts +7 -2
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkbox-group.js +1 -1
- package/dist/elements/checkbox.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-item.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.js +1 -1
- package/dist/elements/dropdown.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-grid.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/radio-group.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/select.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slider.js +1 -1
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +22 -10
- package/dist/{ellipsis-CbOpJnU3.js → ellipsis-CJ5QIjWo.js} +1 -1
- package/dist/{embed-CrfjkbUG.js → embed-6Vji0PfS.js} +1 -1
- package/dist/{external-link-ByXAwBBQ.js → external-link-BPTtTdet.js} +1 -1
- package/dist/{file-text-DMib2qwt.js → file-text-eVjcFDrK.js} +1 -1
- package/dist/{file-tree-CJqM2aP7.js → file-tree-a9tfWYmg.js} +1 -1
- package/dist/{folder-_BAb929C.js → folder-DN6Ilq6d.js} +1 -1
- package/dist/{form-DSZgZQZk.js → form-U1cwhcQx.js} +1 -1
- package/dist/{hover-card-BQbul9eq.js → hover-card-C5ptzZOZ.js} +1 -1
- package/dist/{icon-CdcfRhYI.js → icon-BOMrFp0D.js} +1 -1
- package/dist/index.js +2297 -2279
- package/dist/index.server.js +2102 -2084
- package/dist/{info-DsFjGuI_.js → info-DxzKsIHn.js} +1 -1
- package/dist/{input-DKbmkMos.js → input-aW72PACH.js} +1 -1
- package/dist/kai.es.js +1 -1
- package/dist/{kbd-PvdGhX4s.js → kbd-BP-6MwUs.js} +1 -1
- package/dist/{link-preview-DbM1866N.js → link-preview-CZaDilMG.js} +1 -1
- package/dist/{link-zF8PzWfF.js → link-rlN11N0B.js} +1 -1
- package/dist/loader-BdFK70fF.js +1 -0
- package/dist/{markdown-LMvmAyBk.js → markdown-DjEaoNz-.js} +1 -1
- package/dist/mcp.es.js +587 -111
- package/dist/message--Nlbemtp.js +1 -0
- package/dist/message-C88yTu2A.js +1 -0
- package/dist/message-circle-CZeVDN1f.js +1 -0
- package/dist/message-square-BXNBKC6i.js +1 -0
- package/dist/{minimize-2-BQRoBV1V.js → minimize-2-BxLOOSEf.js} +1 -1
- package/dist/{model-switcher-CMpCOYcB.js → model-switcher-BxRvub2A.js} +1 -1
- package/dist/{overlay-Cb-MkyDa.js → overlay-CDwKJtHN.js} +1 -1
- package/dist/{panel-right-i8warFxT.js → panel-right-BovedCwW.js} +1 -1
- package/dist/{paperclip-ByY3UVO6.js → paperclip-D0Xyjja2.js} +1 -1
- package/dist/play-DGJ4MgB2.js +1 -0
- package/dist/primitives/speech.d.ts +35 -0
- package/dist/primitives/use-auto-resize.d.ts +11 -0
- package/dist/{progress-bar-jVAHvMUf.js → progress-bar-D-aQfy5h.js} +1 -1
- package/dist/{prompt-suggestion-CD-qtNgC.js → prompt-suggestion-C6qLjMEQ.js} +1 -1
- package/dist/{radio-BsoBMhkF.js → radio-D1CkXISc.js} +1 -1
- package/dist/react/index.d.ts +22 -6
- package/dist/react.js +13 -13
- package/dist/{reasoning-4kBPtlTx.js → reasoning-DARmbgmE.js} +1 -1
- package/dist/register-impl-WcrdIihK.js +424 -0
- package/dist/{resizable-uPmY9VY-.js → resizable-Da6oSO2z.js} +1 -1
- package/dist/{rotate-ccw-B2xn93py.js → rotate-ccw-31ed1xK_.js} +1 -1
- package/dist/{rotate-cw-CMVLoCyH.js → rotate-cw-qkl39dw3.js} +1 -1
- package/dist/{scroll-area-BqLUo8ZZ.js → scroll-area-RrKnLYlA.js} +1 -1
- package/dist/{scroll-button-DslXiJyL.js → scroll-button--QeNjobK.js} +1 -1
- package/dist/{select-2cLRtPGT.js → select-CS_0mWW3.js} +1 -1
- package/dist/{separator-BWQtNJcM.js → separator-fT-zBypW.js} +1 -1
- package/dist/{settings-BBR63jLg.js → settings-dfkV_WDm.js} +1 -1
- package/dist/{settings-group-B2ciy-Iw.js → settings-group-CX694npG.js} +1 -1
- package/dist/{skeleton-CkHI-HgC.js → skeleton-BsqKD9TV.js} +1 -1
- package/dist/{slider-DDR9U-i0.js → slider-_EWfTH9H.js} +1 -1
- package/dist/{solid-DruVOXnf.js → solid-BITWG_VE.js} +4214 -3996
- package/dist/{solid-BzCFdL25.js → solid-CRFYWlBw.js} +5647 -5373
- package/dist/solid.d.ts +6 -0
- package/dist/solid.js +259 -254
- package/dist/solid.server.js +259 -254
- package/dist/{source-GEnFF528.js → source-Da1TFiuj.js} +1 -1
- package/dist/speech-CjbWuqG2.js +1 -0
- package/dist/{star-CilHq8GP.js → star-ZvSbbXS6.js} +1 -1
- package/dist/{store-Ciyk6eZG.js → store-DE5lcr9q.js} +1 -1
- package/dist/{switch-KkeQIbuH.js → switch-P_fsHqv3.js} +1 -1
- package/dist/{tasks-card-BrN4bggJ.js → tasks-card-B86qqyZm.js} +1 -1
- package/dist/text-shimmer-BAkw_65G.js +1 -0
- package/dist/textarea-Bkinatpi.js +1 -0
- package/dist/theme.tokens.css +22 -21
- package/dist/{thumbs-up-CsT9Wzb9.js → thumbs-up-CDGW7a4o.js} +1 -1
- package/dist/{toast-store-tgK6MuMv.js → toast-store-B94I50AO.js} +1 -1
- package/dist/{tool-DzUjIBT8.js → tool-CUiCeyi1.js} +1 -1
- package/dist/{tooltip-Bkkg_4t0.js → tooltip-B3wlQSPh.js} +1 -1
- package/dist/{trash-2-jBKxjGGQ.js → trash-2-Cu8yZmFQ.js} +1 -1
- package/dist/{triangle-alert-DlQ68ppf.js → triangle-alert-CCtDUUmn.js} +1 -1
- package/dist/ui/button-variant-names.d.ts +12 -0
- package/dist/ui/button.d.ts +6 -0
- package/dist/ui/color-field.d.ts +48 -0
- package/dist/ui/textarea.d.ts +4 -0
- package/dist/ui/toggle-chip.d.ts +40 -0
- package/dist/{upload-TcAHXv9W.js → upload-CxotJlLv.js} +1 -1
- package/dist/{use-card-resolution-DaZqAdWf.js → use-card-resolution-DILUvNyj.js} +1 -1
- package/dist/{variant-aurora-DbW_OWL2.js → variant-aurora-BGaXBBGf.js} +1 -1
- package/dist/{variant-aurora-BhLuYxJR.js → variant-aurora-BLcQmvkd.js} +1 -1
- package/dist/{variant-aurora-CNmfHhcx.js → variant-aurora-DZIfAtx1.js} +2 -2
- package/dist/{variant-aurora-D6GsdTPA.js → variant-aurora-DpoIW4ZX.js} +2 -2
- package/dist/{variant-custom-BRvaNHSi.js → variant-custom-BhtWdAxE.js} +1 -1
- package/dist/{variant-custom-DMTrEO5w.js → variant-custom-BtApuSyr.js} +2 -2
- package/dist/{variant-custom-DV2x254s.js → variant-custom-C-zS4ZtE.js} +2 -2
- package/dist/{variant-custom-BvYHR62b.js → variant-custom-swi9PXxU.js} +1 -1
- package/dist/{variant-wave-sp-qwsdI.js → variant-wave-7JCIwhpb.js} +1 -1
- package/dist/{variant-wave-BpDd8t3Z.js → variant-wave-BG4nGr6x.js} +1 -1
- package/dist/{variant-wave-BNMGrDfj.js → variant-wave-C32lM9G2.js} +2 -2
- package/dist/{variant-wave-CyBueXj9.js → variant-wave-Dl6jOiK3.js} +2 -2
- package/dist/x-C4niw05N.js +1 -0
- package/frameworks/react/index.tsx +12 -6
- package/kit-base.css +311 -0
- package/llms-full.txt +9 -6
- package/llms.txt +1 -1
- package/package.json +23 -4
- package/solid.css +48 -0
- package/src/elements/element-meta.json +39 -14
- package/theme.css +39 -23
- package/dist/action-icons-CcgNN2oT.js +0 -1
- package/dist/arrow-down-CjRU3JgV.js +0 -1
- package/dist/arrow-left-Bnpb1UEW.js +0 -1
- package/dist/artifact-Bwt7OgcR.js +0 -1
- package/dist/assets/dev-5XzXYR21.js +0 -91
- package/dist/button-DkuOnGLy.js +0 -1
- package/dist/check-njkVdZu2.js +0 -1
- package/dist/chevron-down-B72s6PXR.js +0 -1
- package/dist/chevron-right-BatuHzUm.js +0 -1
- package/dist/circle-check-B62pODB3.js +0 -1
- package/dist/circle-zx0ByzY9.js +0 -1
- package/dist/controllable-CnGpMF6r.js +0 -1
- package/dist/define-By-uL588.js +0 -1
- package/dist/loader-BM4CEz4G.js +0 -1
- package/dist/message-BSsgvHvf.js +0 -1
- package/dist/message-BYZECyek.js +0 -1
- package/dist/message-circle-DruwObfX.js +0 -1
- package/dist/message-square-BG6q6mlm.js +0 -1
- package/dist/play-BvKKq3fX.js +0 -1
- package/dist/register-impl-B1hLkrI8.js +0 -424
- package/dist/text-shimmer-Dcj1MHuq.js +0 -1
- package/dist/textarea-Dpvbngbg.js +0 -1
- package/dist/x-Z5UntkIJ.js +0 -1
|
@@ -361,10 +361,16 @@ export interface ChatProps extends WebComponentProps {
|
|
|
361
361
|
kindIcons?: Record<string, string>;
|
|
362
362
|
/** Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). */
|
|
363
363
|
actionsReveal?: "always" | "hover";
|
|
364
|
+
/** Role-scoped DEFAULT action bars (B-7b): a user message with no `actions` of its own gets `userActions`; an assistant message, `assistantActions`. A per-message `m.actions` OVERRIDES the role default (replace, not merge), so a message that sets `actions: []` renders NO action bar even when a role default is set. Set as JS properties. */
|
|
365
|
+
userActions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[];
|
|
366
|
+
/** See `userActions`, the assistant-role default. */
|
|
367
|
+
assistantActions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[];
|
|
368
|
+
/** Hide the citations row consecutive `source` parts collapse into (`part="citations"`, message.tsx). Named as a HIDE, not `sources: boolean`, so absence-means-default stays unambiguous: absent/false is today's rendering, byte-for-byte (B-8). */
|
|
369
|
+
hideSources?: boolean;
|
|
364
370
|
/** Which attachment media types the user may stage, in HTML `accept` syntax: `<kai-chat accept="image/*,application/pdf">`. A plain string, so unlike `messages` it DOES work as an attribute. Omitted means no filter. MEDIA TYPES ONLY -- exact (`image/png`) or subtype wildcard (`text/*`). HTML allows a file extension here and this does not: `accept=".py"` THROWS with the entry named, rather than silently resolving to a picker that accepts nothing. It can only NARROW what the kit can already encode: `accept="image/*"` resolves to the four image formats both APIs take, not to every image type the OS offers. Pass the SAME string to `toOpenAIMessages(msgs, { accept })` and the picker and the wire cannot disagree -- both resolve it through `resolveMediaPolicy` against one declaration. That declaration is readable as `encodableMediaTypes()` from `@kitn.ai/ui/wire`, if you would rather build your own picker than use this prop. */
|
|
365
371
|
accept?: string;
|
|
366
372
|
/** The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). Omit for an empty thread. Re-declared here (rather than inherited from `ChatThreadProps`) because the ELEMENT registers a `[]` default and renders the empty state without it, while the SolidJS `<ChatThread>` component still requires it. The facade hands it a validated array either way. Matches `<kai-thread>`. */
|
|
367
|
-
messages?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "
|
|
373
|
+
messages?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[];
|
|
368
374
|
/** Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. */
|
|
369
375
|
cardTypes?: Record<string, string>;
|
|
370
376
|
/** JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `cardTypes`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. */
|
|
@@ -372,7 +378,7 @@ export interface ChatProps extends WebComponentProps {
|
|
|
372
378
|
/** Turns on the prior-conversations list (a list-toggle button in the header, plus a second list view sharing the panel, C-1). Attribute- settable like every other boolean flag on this element: `<kai-chat conversations>`. Requires `store`. A row select, "new conversation," or the visitor's mount-time auto-restore all deliver their messages the same way: listen for `kai-conversation-load` and set `el.messages` from `event.detail.messages` (a fresh array): this element does not update `messages` for you. Set with no `store`, the underlying `ChatThread` decides loudly (one console.error) and stays visually off; this facade always supplies its own internal load handler (the `kai-conversation-load` dispatch below), so the second ChatThread guard, missing `onConversationLoad`, never trips here, even for a consumer who never listens for the event. Default false. */
|
|
373
379
|
conversations?: boolean;
|
|
374
380
|
/** The adapter this thread persists conversations through: an object of three functions (`list`/`load`/`save`; `ConversationStore`, exported from `@kitn.ai/ui`'s `primitives/conversation-store`). A JS PROPERTY ONLY: `el.store = myAdapter`. It can never be an attribute, since a function-bearing object has no HTML string form, the same reasoning that keeps `messages`/`cardSchemas` property-only (the kai- contract: array/object props are JS properties, never attributes). Two built-ins ship: `localStorageStore(name, userId?)` and `fetchStore(url, userId?)`. */
|
|
375
|
-
store?: { list: () => Promise<{ id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[]>; load: (id: string) => Promise<{ id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "
|
|
381
|
+
store?: { list: () => Promise<{ id: string; title: string; groupId?: string; scope?: { type: "document" | "collection"; documentId?: string; filters?: { tags?: string[]; authors?: string[]; contentType?: "transcript" | "markdown"; dateRange?: { from: string; to: string } } }; messageCount: number; lastMessageAt?: string; updatedAt: string; trailing?: string; lastReadAt?: string }[]>; load: (id: string) => Promise<{ id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]>; save: (id: string, messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[]) => Promise<void>; markRead?: ((id: string) => Promise<void>) };
|
|
376
382
|
/** Turns on the widget home screen (Intercom-pattern): the panel boots into a `home` view, with a greeting, most-recent-conversation card, a "new conversation" CTA, and host-defined links, plus a Home/Messages tab bar for switching back to the thread. An OBJECT, so it is a JS property only: `el.home = { greeting: { title: 'Hey' }, links: [...] }`, never an attribute (the kai- contract: array/object props are JS properties). A `links` entry with no `href` fires `kai-home-link` with that entry when tapped, rather than navigating; one WITH `href` opens it directly (only when the URL passes the kit's own scheme allowlist). Omit for the no-home widget (chat view only, unchanged). */
|
|
377
383
|
home?: { greeting?: { title?: string; subtitle?: string }; recentConversation?: boolean; newConversation?: { label?: string }; links?: { label: string; href?: string; description?: string; icon?: string }[] };
|
|
378
384
|
/** The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time. */
|
|
@@ -380,7 +386,7 @@ export interface ChatProps extends WebComponentProps {
|
|
|
380
386
|
/** One or more picked files were refused because `accept` excluded them. The element renders NO message of its own: it reports the facts (name, media type, whether the kit could have sent it) and what the user should see is the application's call. Only ever fires when `accept` is set. */
|
|
381
387
|
onAttachmentsRejected?: (event: CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" | "unsupported" }[] }>) => void;
|
|
382
388
|
/** A conversation's history loaded: a row tap in the list, "new conversation," or the visitor's own mount-time auto-restore of their most recent thread (only fires when `conversations` is on and a `store` is set). `detail.id` is that conversation's id, `undefined` for the "new conversation" case (no id exists until the first message mints one, C-6). Set `el.messages = event.detail.messages` (already a fresh array) to actually render it, since this element does not do that for you; `messages` stays your own state like everywhere else on this element. */
|
|
383
|
-
onConversationLoad?: (event: CustomEvent<{ id: string | undefined; messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: undefined | { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: undefined | string; index?: undefined | number; streamId?: undefined | string; signature?: undefined | string; raw?: undefined | { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: undefined | "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; rawInput?: undefined | string; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string; raw?: undefined | { source: string; payload: unknown } }; raw?: undefined | { source: string; payload: unknown } } | { type: "card"; envelope: { 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 } }; raw?: undefined | { source: string; payload: unknown } } | { type: "source"; source: { id?: undefined | string; url?: undefined | string; title?: undefined | string; snippet?: undefined | string; index?: undefined | number }; raw?: undefined | { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }; raw?: undefined | { source: string; payload: unknown } })[]; actions?: undefined | ("copy" | "
|
|
389
|
+
onConversationLoad?: (event: CustomEvent<{ id: string | undefined; messages: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: undefined | { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: undefined | string; index?: undefined | number; streamId?: undefined | string; signature?: undefined | string; raw?: undefined | { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: undefined | "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; rawInput?: undefined | string; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string; raw?: undefined | { source: string; payload: unknown } }; raw?: undefined | { source: string; payload: unknown } } | { type: "card"; envelope: { 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 } }; raw?: undefined | { source: string; payload: unknown } } | { type: "source"; source: { id?: undefined | string; url?: undefined | string; title?: undefined | string; snippet?: undefined | string; index?: undefined | number }; raw?: undefined | { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }; raw?: undefined | { source: string; payload: unknown } })[]; actions?: undefined | ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { 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" }[] }>) => void;
|
|
384
390
|
/** A `home.links` entry with no `href` was activated (tapped/clicked/Enter). Meaningful only when `home` is set. */
|
|
385
391
|
onHomeLink?: (event: CustomEvent<{ entry: { label: string; href?: undefined | string; description?: undefined | string; icon?: undefined | string } }>) => void;
|
|
386
392
|
/** An action button on a message was clicked. `action` is the built-in name or custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. */
|
|
@@ -401,7 +407,7 @@ export interface ChatProps extends WebComponentProps {
|
|
|
401
407
|
|
|
402
408
|
export const Chat = /*#__PURE__*/ createWebComponent<ChatProps>(
|
|
403
409
|
'kai-chat',
|
|
404
|
-
["theme","value","placeholder","loading","suggestions","suggestionMode","persistSuggestions","proseSize","codeTheme","codeHighlight","reasoning","reasoningOpen","chatTitle","models","currentModel","context","scrollButton","headerStart","headerEnd","headerFull","sidebar","empty","composer","composerActions","footer","attach","webSearch","voice","triggers","kindIcons","actionsReveal","accept","messages","cardTypes","cardSchemas","conversations","store","home"],
|
|
410
|
+
["theme","value","placeholder","loading","suggestions","suggestionMode","persistSuggestions","proseSize","codeTheme","codeHighlight","reasoning","reasoningOpen","chatTitle","models","currentModel","context","scrollButton","headerStart","headerEnd","headerFull","sidebar","empty","composer","composerActions","footer","attach","webSearch","voice","triggers","kindIcons","actionsReveal","userActions","assistantActions","hideSources","accept","messages","cardTypes","cardSchemas","conversations","store","home"],
|
|
405
411
|
{ onAttachmentsChange: 'kai-attachments-change', onAttachmentsRejected: 'kai-attachments-rejected', onConversationLoad: 'kai-conversation-load', onHomeLink: 'kai-home-link', onMessageAction: 'kai-message-action', onModelChange: 'kai-model-change', onSubmit: 'kai-submit', onSuggestionClick: 'kai-suggestion-click', onValueChange: 'kai-value-change', onVoice: 'kai-voice', onWebSearch: 'kai-web-search' },
|
|
406
412
|
() => import('@kitn.ai/ui/elements/chat'),
|
|
407
413
|
);
|
|
@@ -1170,7 +1176,7 @@ export const Menu = /*#__PURE__*/ createWebComponent<MenuProps>(
|
|
|
1170
1176
|
|
|
1171
1177
|
export interface MessageProps extends WebComponentProps {
|
|
1172
1178
|
/** The full message object. Set as a JS property. */
|
|
1173
|
-
message?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "
|
|
1179
|
+
message?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" };
|
|
1174
1180
|
/** Who is speaking: `'user'` or `'assistant'`. Convenience for simple cases when not passing a `message` object. This is the SEMANTIC role of the message, not an ARIA role. The name collides with the global ARIA `role` attribute, which is why the facade lifts it off the host (see `liftRoleOffHost`). Neither speaker is a valid ARIA role, so a `role="user"` left on `<kai-message>` is a CRITICAL axe `aria-roles` violation. The accessible role lives on the row inside the shadow root instead: `role="article"` plus an `aria-label` naming the speaker, matching the SolidJS `<Message>` component. */
|
|
1175
1181
|
role?: "user" | "assistant";
|
|
1176
1182
|
/** Force markdown on/off. Defaults to on for assistant, off for user. */
|
|
@@ -2037,7 +2043,7 @@ export const ThinkingBar = /*#__PURE__*/ createWebComponent<ThinkingBarProps>(
|
|
|
2037
2043
|
|
|
2038
2044
|
export interface ThreadProps extends WebComponentProps {
|
|
2039
2045
|
/** The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). */
|
|
2040
|
-
messages?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "
|
|
2046
|
+
messages?: { id: string; role: "user" | "assistant"; parts: ({ type: "text"; text: string; raw?: { source: string; payload: unknown } } | { type: "reasoning"; text: string; label?: string; index?: number; streamId?: string; signature?: string; raw?: { source: string; payload: unknown } } | { type: "tool"; tool: { type: string; kind?: "command" | "file-change" | "search" | "fetch" | "mcp" | "image" | "generic"; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; rawInput?: string; output?: Record<string, unknown>; toolCallId?: string; errorText?: string; raw?: { source: string; payload: unknown } }; raw?: { source: string; payload: unknown } } | { type: "card"; envelope: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }; raw?: { source: string; payload: unknown } } | { type: "source"; source: { id?: string; url?: string; title?: string; snippet?: string; index?: number }; raw?: { source: string; payload: unknown } } | { type: "file"; attachment: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }; raw?: { source: string; payload: unknown } })[]; actions?: ("copy" | "dislike" | "edit" | "like" | "regenerate" | "speak" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" }[];
|
|
2041
2047
|
/** Show a typing indicator on the pending assistant turn. Set it while awaiting the assistant's reply. */
|
|
2042
2048
|
loading?: boolean;
|
|
2043
2049
|
/** Body/prose font scale for rendered markdown (`'xs' | 'sm' | 'base' | 'lg'`). Defaults to `'sm'`. */
|
package/kit-base.css
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/* Shared kit base + component rules. ONE source, TWO consumers, and the
|
|
2
|
+
consumers are different style paths, which is the whole reason this file
|
|
3
|
+
exists:
|
|
4
|
+
|
|
5
|
+
src/elements/styles.css compiled to compiled.css and adopted into every
|
|
6
|
+
kai-* shadow root (the ./elements and ./define
|
|
7
|
+
entries). It also sets the :host rules.
|
|
8
|
+
solid.css Tailwind SOURCE that a light-DOM SolidJS consumer
|
|
9
|
+
@imports into their own Tailwind build (the . and
|
|
10
|
+
./solid entries ship class names and no CSS).
|
|
11
|
+
|
|
12
|
+
Before this file, everything below lived only in the elements sheet, so the
|
|
13
|
+
Solid path silently shipped without it: .kai-radio / .kai-checkbox / .kai-range
|
|
14
|
+
rendered as raw OS chrome, no inset focus ring, no token-driven focus outline,
|
|
15
|
+
no scrollbars, no ::selection. Nothing in CI rendered that path, so nothing
|
|
16
|
+
noticed. The same defect had already happened once before, one layer up, when
|
|
17
|
+
the Storybook sheet hand-copied a few of these rules and omitted the rest.
|
|
18
|
+
|
|
19
|
+
NEVER copy a rule from here into either consumer, and never add a rule to one
|
|
20
|
+
consumer that both need. Add it here. The one thing that cannot live here is
|
|
21
|
+
:host, which selects nothing in a light DOM; those rules stay in
|
|
22
|
+
src/elements/styles.css, and .storybook/styles.css re-states them on body.
|
|
23
|
+
|
|
24
|
+
This file relies on the --color-* tokens from theme.css and on the classes
|
|
25
|
+
tw-animate-css emits; both consumers import those alongside it. */
|
|
26
|
+
|
|
27
|
+
@layer base {
|
|
28
|
+
/* Code/monospace surfaces use --kai-font-code (named for its ROLE — code — not
|
|
29
|
+
"mono", so any family works). Covers inline code, <pre>, and code blocks. */
|
|
30
|
+
code, kbd, samp, pre {
|
|
31
|
+
font-family: var(--kai-font-code, ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace);
|
|
32
|
+
}
|
|
33
|
+
* { border-color: var(--color-border); }
|
|
34
|
+
|
|
35
|
+
/* Native controls (date/time pickers, <select>, number spinners, the
|
|
36
|
+
checkbox/radio fallbacks, scrollbars) must follow the kit's light/dark
|
|
37
|
+
mode — NOT the OS — so e.g. the native calendar icon never renders black
|
|
38
|
+
on a dark surface. `color-scheme` drives that native rendering; we set it
|
|
39
|
+
from the resolved mode (the `.dark` scope the element factory toggles). */
|
|
40
|
+
.dark { color-scheme: dark; }
|
|
41
|
+
|
|
42
|
+
/* One token-driven focus ring for EVERY focusable element in the shadow root.
|
|
43
|
+
Blue by default (via --color-ring), consistent in light + dark, and
|
|
44
|
+
overridable through --kai-color-ring. Uses `outline` (not a box-shadow
|
|
45
|
+
ring) because it follows the element's radius and — unlike a `ring-*`
|
|
46
|
+
utility — is written in literal values, so it does not depend on any
|
|
47
|
+
`--tw-*` custom property. That matters more than it looks: `@property`
|
|
48
|
+
registration is document-global and is ignored inside a shadow root, so
|
|
49
|
+
every `--tw-*` here is unset and every `ring-*` utility computes to
|
|
50
|
+
nothing. This literal rule is the only reason most of the kit had a focus
|
|
51
|
+
indicator at all. `:focus-visible` so it only appears for keyboard
|
|
52
|
+
navigation, never on a mouse click. Components that render their own ring
|
|
53
|
+
set `focus-visible:outline-none` (a utility in a later cascade layer) and
|
|
54
|
+
win over this — so there's no double ring, just a guaranteed blue fallback
|
|
55
|
+
for the many controls that have no explicit focus style.
|
|
56
|
+
|
|
57
|
+
CAVEAT, learned the hard way: an outline is NOT immune to clipping. It is
|
|
58
|
+
unaffected by the element's OWN overflow, but `outline-offset: 2px` draws
|
|
59
|
+
it entirely outside the border box, where an ANCESTOR with
|
|
60
|
+
`overflow: clip`/`hidden` erases it completely. Controls that sit flush
|
|
61
|
+
inside such a container need `.kai-focus-inset` below. */
|
|
62
|
+
:focus-visible {
|
|
63
|
+
outline: 2px solid var(--color-ring);
|
|
64
|
+
outline-offset: 2px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Consistent cross-platform scrollbars for EVERY scroll region inside the
|
|
68
|
+
shadow root (message list, conversation list, menus, hover cards, code
|
|
69
|
+
blocks, the prompt textarea …). Thin + rounded + subtle, themed via tokens
|
|
70
|
+
for light/dark, thumb stronger on hover. Track stays transparent and
|
|
71
|
+
overflow is untouched, so no scrollbar is forced where none is needed.
|
|
72
|
+
Scoped to the shadow root, so it never restyles the host page. */
|
|
73
|
+
* {
|
|
74
|
+
scrollbar-width: thin;
|
|
75
|
+
scrollbar-color: var(--color-scrollbar-thumb) transparent;
|
|
76
|
+
}
|
|
77
|
+
*::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
78
|
+
*::-webkit-scrollbar-track { background: transparent; }
|
|
79
|
+
*::-webkit-scrollbar-thumb {
|
|
80
|
+
background-color: var(--color-scrollbar-thumb);
|
|
81
|
+
border-radius: 9999px;
|
|
82
|
+
border: 2px solid transparent;
|
|
83
|
+
background-clip: padding-box;
|
|
84
|
+
}
|
|
85
|
+
*::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
|
|
86
|
+
*::-webkit-scrollbar-corner { background: transparent; }
|
|
87
|
+
|
|
88
|
+
/* SELECTED text, kit-owned. Scoped to the shadow root — deliberately, and this
|
|
89
|
+
is the whole design decision, so read it before "simplifying" it into
|
|
90
|
+
theme.css.
|
|
91
|
+
|
|
92
|
+
WHY THE KIT OWNS IT AT ALL: left to the UA, the browser's blue selection was
|
|
93
|
+
visually stronger than the kit's OWN deliberate emphasis (the composer's
|
|
94
|
+
--color-highlight mark), so an incidental drag-select outranked a keyword the
|
|
95
|
+
app had flagged on purpose. How selected text reads is a fact about the
|
|
96
|
+
medium, which is the kit's call.
|
|
97
|
+
|
|
98
|
+
WHY ONLY HERE, AND NOT IN theme.css: theme.css is imported into a CONSUMER's
|
|
99
|
+
Tailwind build, so a bare ::selection there would restyle every paragraph on
|
|
100
|
+
their page — the kit deciding for the app's own content, which is not ours to
|
|
101
|
+
decide. The shadow root IS the kit's DOM, so the same rule is unambiguous
|
|
102
|
+
here. Identical reasoning to the scrollbar and :focus-visible rules above,
|
|
103
|
+
which are scoped the same way and for the same reason. theme.css therefore
|
|
104
|
+
carries the TOKENS only. A Solid-layer consumer rendering our components
|
|
105
|
+
into their own light DOM opts in by importing solid.css, which brings this
|
|
106
|
+
whole file and says so in its header; a consumer who wants the tokens alone
|
|
107
|
+
imports theme.css and writes their own scoped rule:
|
|
108
|
+
`.my-chat ::selection { background-color: var(--color-selection); … }`.
|
|
109
|
+
|
|
110
|
+
WHY THE RULE MUST BE IN THIS SHEET AND NOT INHERITED: ::selection does NOT
|
|
111
|
+
cross the shadow boundary the way ordinary inherited properties do — a
|
|
112
|
+
document-level ::selection leaves shadow content on the UA default. The
|
|
113
|
+
--kai-color-selection TOKEN does pierce (custom properties inherit normally),
|
|
114
|
+
which is what keeps a consumer override working.
|
|
115
|
+
|
|
116
|
+
Both background AND color are set; see the theme.css note for why the
|
|
117
|
+
foreground is load-bearing rather than cosmetic (coloured text falls under
|
|
118
|
+
WCAG AA on this fill if it keeps its own colour). */
|
|
119
|
+
::selection {
|
|
120
|
+
background-color: var(--color-selection);
|
|
121
|
+
color: var(--color-selection-foreground);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Respect a user's reduced-motion preference: disable the tw-animate-css
|
|
125
|
+
enter/exit keyframe animations (toasts, overlays, cards) so they appear and
|
|
126
|
+
disappear instantly instead of sliding/fading. Targets the utility classes
|
|
127
|
+
tw-animate-css emits (.animate-in / .animate-out) plus any arbitrary
|
|
128
|
+
`animate-[…]` keyframe utility. */
|
|
129
|
+
@media (prefers-reduced-motion: reduce) {
|
|
130
|
+
.animate-in,
|
|
131
|
+
.animate-out,
|
|
132
|
+
[class*="animate-["] {
|
|
133
|
+
animation: none !important;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@layer components {
|
|
140
|
+
/* Focus ring drawn INSIDE the element's border box, for focusable regions
|
|
141
|
+
that sit flush against a clipping ancestor. The scrollable code-block body
|
|
142
|
+
and the message log are both `overflow-*` regions filling a rounded
|
|
143
|
+
`overflow: clip` container, so the default `outline-offset: 2px` put the
|
|
144
|
+
whole ring outside the clip and it never painted — the outline computed
|
|
145
|
+
correctly, matched `:focus-visible`, and was erased before it reached the
|
|
146
|
+
screen. A negative offset keeps it clear of the boundary. Same token and
|
|
147
|
+
width as the base ring, so it reads identically. */
|
|
148
|
+
.kai-focus-inset:focus-visible {
|
|
149
|
+
outline: 2px solid var(--color-ring);
|
|
150
|
+
outline-offset: -2px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Designed radio + checkbox (appearance-none) so the form controls read as
|
|
154
|
+
intentional, theme correctly in light/dark, and have comfortable targets —
|
|
155
|
+
replacing the weak native defaults. Used via `.kai-radio` / `.kai-checkbox`. */
|
|
156
|
+
.kai-radio,
|
|
157
|
+
.kai-checkbox {
|
|
158
|
+
appearance: none;
|
|
159
|
+
-webkit-appearance: none;
|
|
160
|
+
inline-size: 1.125rem;
|
|
161
|
+
block-size: 1.125rem;
|
|
162
|
+
margin: 0;
|
|
163
|
+
flex-shrink: 0;
|
|
164
|
+
cursor: pointer;
|
|
165
|
+
border: 1.5px solid var(--color-input);
|
|
166
|
+
background-color: var(--color-background);
|
|
167
|
+
display: inline-grid;
|
|
168
|
+
place-content: center;
|
|
169
|
+
box-shadow: 0 1px 2px rgb(0 0 0 / 0.04);
|
|
170
|
+
transition: border-color 0.18s ease, background-color 0.18s ease, box-shadow 0.18s ease,
|
|
171
|
+
transform 0.1s ease;
|
|
172
|
+
}
|
|
173
|
+
.kai-radio { border-radius: 9999px; }
|
|
174
|
+
.kai-checkbox { border-radius: 0.3rem; }
|
|
175
|
+
.kai-radio::after {
|
|
176
|
+
content: "";
|
|
177
|
+
inline-size: 0.5rem;
|
|
178
|
+
block-size: 0.5rem;
|
|
179
|
+
border-radius: 9999px;
|
|
180
|
+
background-color: var(--color-primary);
|
|
181
|
+
transform: scale(0);
|
|
182
|
+
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
183
|
+
}
|
|
184
|
+
.kai-radio:checked { border-color: var(--color-primary); }
|
|
185
|
+
.kai-radio:checked::after { transform: scale(1); }
|
|
186
|
+
.kai-checkbox:checked {
|
|
187
|
+
background-color: var(--color-primary);
|
|
188
|
+
border-color: var(--color-primary);
|
|
189
|
+
}
|
|
190
|
+
.kai-checkbox::after {
|
|
191
|
+
content: "";
|
|
192
|
+
inline-size: 0.8rem;
|
|
193
|
+
block-size: 0.8rem;
|
|
194
|
+
background-color: var(--color-primary-foreground);
|
|
195
|
+
transform: scale(0);
|
|
196
|
+
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
197
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
198
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
199
|
+
}
|
|
200
|
+
.kai-checkbox:checked::after { transform: scale(1); }
|
|
201
|
+
.kai-radio:hover:not(:disabled),
|
|
202
|
+
.kai-checkbox:hover:not(:disabled) {
|
|
203
|
+
border-color: var(--color-primary);
|
|
204
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-primary) 9%, transparent);
|
|
205
|
+
}
|
|
206
|
+
.kai-radio:active:not(:disabled),
|
|
207
|
+
.kai-checkbox:active:not(:disabled) { transform: scale(0.92); }
|
|
208
|
+
.kai-checkbox:checked {
|
|
209
|
+
box-shadow: 0 1px 4px color-mix(in srgb, var(--color-primary) 26%, transparent);
|
|
210
|
+
}
|
|
211
|
+
/* The mixed state ("some of the children below are ticked"). `indeterminate` is a
|
|
212
|
+
DOM property with no HTML attribute, so a rule is the ONLY way it can be seen —
|
|
213
|
+
without one a mixed box paints pixel-identically to an unchecked one and lies
|
|
214
|
+
about its state. That was live: `<kai-tasks>` in `select` mode puts an
|
|
215
|
+
indeterminate select-all above a partial selection, and it read as "none
|
|
216
|
+
selected".
|
|
217
|
+
|
|
218
|
+
Same fill and the same masked `::after` as `:checked`, with the tick mask
|
|
219
|
+
swapped for a bar, so the two states are siblings of one technique rather than
|
|
220
|
+
two: the bar is `--color-primary-foreground` on `--color-primary`, exactly the
|
|
221
|
+
pair the tick already uses, so it themes and contrasts identically in both
|
|
222
|
+
themes and stays consumer-themable through the same tokens.
|
|
223
|
+
|
|
224
|
+
Ordered AFTER the `:checked` rules on purpose. `indeterminate` is independent of
|
|
225
|
+
`checked`, so a box can be both; specificity is equal, so source order decides,
|
|
226
|
+
and mixed-wins is what the browser's own native rendering does.
|
|
227
|
+
|
|
228
|
+
Scoped to `.kai-checkbox` on purpose: `:indeterminate` also matches every radio
|
|
229
|
+
in a group with nothing selected, and an empty radio group must go on reading as
|
|
230
|
+
empty rather than sprouting a third state. */
|
|
231
|
+
.kai-checkbox:indeterminate {
|
|
232
|
+
background-color: var(--color-primary);
|
|
233
|
+
border-color: var(--color-primary);
|
|
234
|
+
box-shadow: 0 1px 4px color-mix(in srgb, var(--color-primary) 26%, transparent);
|
|
235
|
+
}
|
|
236
|
+
.kai-checkbox:indeterminate::after {
|
|
237
|
+
transform: scale(1);
|
|
238
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
239
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
240
|
+
}
|
|
241
|
+
.kai-radio:disabled,
|
|
242
|
+
.kai-checkbox:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
243
|
+
|
|
244
|
+
/* Custom range slider: a filled track (--kai-range-fill set by the widget) and a
|
|
245
|
+
refined thumb that scales on hover/press with a focus halo — no native chrome. */
|
|
246
|
+
.kai-range {
|
|
247
|
+
appearance: none;
|
|
248
|
+
-webkit-appearance: none;
|
|
249
|
+
inline-size: 100%;
|
|
250
|
+
block-size: 1.25rem;
|
|
251
|
+
background: transparent;
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
margin: 0;
|
|
254
|
+
}
|
|
255
|
+
.kai-range::-webkit-slider-runnable-track {
|
|
256
|
+
block-size: 0.375rem;
|
|
257
|
+
border-radius: 9999px;
|
|
258
|
+
border: none;
|
|
259
|
+
box-shadow: none;
|
|
260
|
+
background: linear-gradient(
|
|
261
|
+
to right,
|
|
262
|
+
var(--color-primary) var(--kai-range-fill, 0%),
|
|
263
|
+
var(--color-muted) var(--kai-range-fill, 0%)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
.kai-range::-moz-range-track {
|
|
267
|
+
block-size: 0.375rem;
|
|
268
|
+
border-radius: 9999px;
|
|
269
|
+
border: none;
|
|
270
|
+
box-shadow: none;
|
|
271
|
+
background: var(--color-muted);
|
|
272
|
+
}
|
|
273
|
+
.kai-range::-moz-range-progress {
|
|
274
|
+
block-size: 0.375rem;
|
|
275
|
+
border-radius: 9999px;
|
|
276
|
+
background: var(--color-primary);
|
|
277
|
+
}
|
|
278
|
+
.kai-range::-webkit-slider-thumb {
|
|
279
|
+
appearance: none;
|
|
280
|
+
-webkit-appearance: none;
|
|
281
|
+
inline-size: 1.125rem;
|
|
282
|
+
block-size: 1.125rem;
|
|
283
|
+
border-radius: 9999px;
|
|
284
|
+
background: var(--color-background);
|
|
285
|
+
border: 2px solid var(--color-primary);
|
|
286
|
+
box-shadow: 0 1px 3px rgb(0 0 0 / 0.18);
|
|
287
|
+
margin-block-start: -0.375rem;
|
|
288
|
+
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
|
289
|
+
}
|
|
290
|
+
.kai-range::-moz-range-thumb {
|
|
291
|
+
inline-size: 1.125rem;
|
|
292
|
+
block-size: 1.125rem;
|
|
293
|
+
border-radius: 9999px;
|
|
294
|
+
background: var(--color-background);
|
|
295
|
+
border: 2px solid var(--color-primary);
|
|
296
|
+
box-shadow: 0 1px 3px rgb(0 0 0 / 0.18);
|
|
297
|
+
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
|
298
|
+
}
|
|
299
|
+
.kai-range:hover::-webkit-slider-thumb { transform: scale(1.12); }
|
|
300
|
+
.kai-range:hover::-moz-range-thumb { transform: scale(1.12); }
|
|
301
|
+
.kai-range:focus-visible { outline: none; }
|
|
302
|
+
.kai-range:focus-visible::-webkit-slider-thumb,
|
|
303
|
+
.kai-range:active::-webkit-slider-thumb {
|
|
304
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
305
|
+
}
|
|
306
|
+
.kai-range:focus-visible::-moz-range-thumb,
|
|
307
|
+
.kai-range:active::-moz-range-thumb {
|
|
308
|
+
box-shadow: 0 0 0 6px color-mix(in srgb, var(--color-primary) 14%, transparent);
|
|
309
|
+
}
|
|
310
|
+
.kai-range:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
311
|
+
}
|
package/llms-full.txt
CHANGED
|
@@ -58,7 +58,7 @@ interface ChatMessage {
|
|
|
58
58
|
/** The ONLY content channel. Ordered. */
|
|
59
59
|
parts: MessagePart[];
|
|
60
60
|
/** Action buttons under the message. Chrome, not content. */
|
|
61
|
-
actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit')[];
|
|
61
|
+
actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit' | 'speak')[];
|
|
62
62
|
avatar?: { src?: string; fallback?: string; alt?: string };
|
|
63
63
|
feedback?: 'like' | 'dislike';
|
|
64
64
|
}
|
|
@@ -1610,12 +1610,15 @@ _No events._
|
|
|
1610
1610
|
| `triggers` | — | `undefined \| { char: string; kind: string; items?: undefined \| { id: string; label: string; icon?: undefined \| string; description?: undefined \| string; group?: undefined \| string; kind?: undefined \| string; promptText?: undefined \| string; data?: undefined \| Record<string, unknown> }[] }[]` | Rich entity triggers. Each `{ char, kind, items }` opens a caret-anchored menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a JS property; forwarded to the input. |
|
|
1611
1611
|
| `kindIcons` | — | `undefined \| Record<string, string>` | Default icon per entity kind (kind → image src) for pills/menu items. |
|
|
1612
1612
|
| `actionsReveal` | `actions-reveal` | `undefined \| "always" \| "hover"` | Whether each message's action bar is always visible (`'always'`, default) or only revealed on hover of that message row (`'hover'`). |
|
|
1613
|
+
| `userActions` | — | `undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]` | Role-scoped DEFAULT action bars (B-7b): a user message with no `actions` of its own gets `userActions`; an assistant message, `assistantActions`. A per-message `m.actions` OVERRIDES the role default (replace, not merge), so a message that sets `actions: []` renders NO action bar even when a role default is set. Set as JS properties. |
|
|
1614
|
+
| `assistantActions` | — | `undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { id: string; label: string; icon?: undefined \| string; tooltip?: undefined \| string })[]` | See `userActions`, the assistant-role default. |
|
|
1615
|
+
| `hideSources` | `hide-sources` | `undefined \| false \| true` | Hide the citations row consecutive `source` parts collapse into (`part="citations"`, message.tsx). Named as a HIDE, not `sources: boolean`, so absence-means-default stays unambiguous: absent/false is today's rendering, byte-for-byte (B-8). |
|
|
1613
1616
|
| `accept` | `accept` | `undefined \| string` | Which attachment media types the user may stage, in HTML `accept` syntax: `<kai-chat accept="image/*,application/pdf">`. A plain string, so unlike `messages` it DOES work as an attribute. Omitted means no filter. MEDIA TYPES ONLY -- exact (`image/png`) or subtype wildcard (`text/*`). HTML allows a file extension here and this does not: `accept=".py"` THROWS with the entry named, rather than silently resolving to a picker that accepts nothing. It can only NARROW what the kit can already encode: `accept="image/*"` resolves to the four image formats both APIs take, not to every image type the OS offers. Pass the SAME string to `toOpenAIMessages(msgs, { accept })` and the picker and the wire cannot disagree -- both resolve it through `resolveMediaPolicy` against one declaration. That declaration is readable as `encodableMediaTypes()` from `@kitn.ai/ui/wire`, if you would rather build your own picker than use this prop. |
|
|
1614
|
-
| `messages` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "
|
|
1617
|
+
| `messages` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }[]` | The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). Omit for an empty thread. Re-declared here (rather than inherited from `ChatThreadProps`) because the ELEMENT registers a `[]` default and renders the empty state without it, while the SolidJS `<ChatThread>` component still requires it. The facade hands it a validated array either way. Matches `<kai-thread>`. |
|
|
1615
1618
|
| `cardTypes` | — | `undefined \| Record<string, string>` | Optional card type -> custom-element tag overrides/additions for `card` parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. |
|
|
1616
1619
|
| `cardSchemas` | — | `undefined \| Record<string, object>` | JSON Schemas for the card types this app renders, keyed by envelope type. The companion of `cardTypes`, which says what DRAWS a card while this says what a VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas = { 'pricing-table': pricingSchema }`, never an attribute. `createCardRegistry(...).validationSchemas` is exactly this shape. Without it the kit validates its own seven built-ins and leaves your own card type, the one your app actually cares about, as the only unchecked thing on screen. A schema here WINS over a built-in of the same name. Typed `Record<string, object>` rather than `Record<string, JsonSchema>` deliberately: an imported `.json` schema widens `"type"` to `string`, and an authored one carries `$schema`/`title`/`description`/`additionalProperties`, so the tighter type would reject both of the normal ways to supply one. |
|
|
1617
1620
|
| `conversations` | `conversations` | `undefined \| false \| true` | Turns on the prior-conversations list (a list-toggle button in the header, plus a second list view sharing the panel, C-1). Attribute- settable like every other boolean flag on this element: `<kai-chat conversations>`. Requires `store`. A row select, "new conversation," or the visitor's mount-time auto-restore all deliver their messages the same way: listen for `kai-conversation-load` and set `el.messages` from `event.detail.messages` (a fresh array): this element does not update `messages` for you. Set with no `store`, the underlying `ChatThread` decides loudly (one console.error) and stays visually off; this facade always supplies its own internal load handler (the `kai-conversation-load` dispatch below), so the second ChatThread guard, missing `onConversationLoad`, never trips here, even for a consumer who never listens for the event. Default false. |
|
|
1618
|
-
| `store` | — | `undefined \| { list: () => Promise<{ id: string; title: string; groupId?: undefined \| string; scope?: undefined \| { 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?: undefined \| string; updatedAt: string; trailing?: undefined \| string; lastReadAt?: undefined \| string }[]>; load: (id: string) => Promise<{ id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "
|
|
1621
|
+
| `store` | — | `undefined \| { list: () => Promise<{ id: string; title: string; groupId?: undefined \| string; scope?: undefined \| { 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?: undefined \| string; updatedAt: string; trailing?: undefined \| string; lastReadAt?: undefined \| string }[]>; load: (id: string) => Promise<{ id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }[]>; save: (id: string, messages: { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }[]) => Promise<void>; markRead?: undefined \| ((id: string) => Promise<void>) }` | The adapter this thread persists conversations through: an object of three functions (`list`/`load`/`save`; `ConversationStore`, exported from `@kitn.ai/ui`'s `primitives/conversation-store`). A JS PROPERTY ONLY: `el.store = myAdapter`. It can never be an attribute, since a function-bearing object has no HTML string form, the same reasoning that keeps `messages`/`cardSchemas` property-only (the kai- contract: array/object props are JS properties, never attributes). Two built-ins ship: `localStorageStore(name, userId?)` and `fetchStore(url, userId?)`. |
|
|
1619
1622
|
| `home` | — | `undefined \| { greeting?: undefined \| { title?: undefined \| string; subtitle?: undefined \| string }; recentConversation?: undefined \| false \| true; newConversation?: undefined \| { label?: undefined \| string }; links?: undefined \| { label: string; href?: undefined \| string; description?: undefined \| string; icon?: undefined \| string }[] }` | Turns on the widget home screen (Intercom-pattern): the panel boots into a `home` view, with a greeting, most-recent-conversation card, a "new conversation" CTA, and host-defined links, plus a Home/Messages tab bar for switching back to the thread. An OBJECT, so it is a JS property only: `el.home = { greeting: { title: 'Hey' }, links: [...] }`, never an attribute (the kai- contract: array/object props are JS properties). A `links` entry with no `href` fires `kai-home-link` with that entry when tapped, rather than navigating; one WITH `href` opens it directly (only when the URL passes the kit's own scheme allowlist). Omit for the no-home widget (chat view only, unchanged). |
|
|
1620
1623
|
|
|
1621
1624
|
**Events** (non-bubbling `CustomEvent`s — listen directly on the element):
|
|
@@ -1624,7 +1627,7 @@ _No events._
|
|
|
1624
1627
|
|---|---|---|
|
|
1625
1628
|
| `kai-attachments-change` | `CustomEvent<{ attachments: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }[] }>` | The staged attachments changed (file added or removed). Carries the full current list so a consumer can react in real time. |
|
|
1626
1629
|
| `kai-attachments-rejected` | `CustomEvent<{ rejected: { filename: string; mediaType: string; reason: "filtered" \| "unsupported" }[] }>` | One or more picked files were refused because `accept` excluded them. The element renders NO message of its own: it reports the facts (name, media type, whether the kit could have sent it) and what the user should see is the application's call. Only ever fires when `accept` is set. |
|
|
1627
|
-
| `kai-conversation-load` | `CustomEvent<{ id: string \| undefined; messages: { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "
|
|
1630
|
+
| `kai-conversation-load` | `CustomEvent<{ id: string \| undefined; messages: { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }[] }>` | A conversation's history loaded: a row tap in the list, "new conversation," or the visitor's own mount-time auto-restore of their most recent thread (only fires when `conversations` is on and a `store` is set). `detail.id` is that conversation's id, `undefined` for the "new conversation" case (no id exists until the first message mints one, C-6). Set `el.messages = event.detail.messages` (already a fresh array) to actually render it, since this element does not do that for you; `messages` stays your own state like everywhere else on this element. |
|
|
1628
1631
|
| `kai-home-link` | `CustomEvent<{ entry: { label: string; href?: undefined \| string; description?: undefined \| string; icon?: undefined \| string } }>` | A `home.links` entry with no `href` was activated (tapped/clicked/Enter). Meaningful only when `home` is set. |
|
|
1629
1632
|
| `kai-message-action` | `CustomEvent<{ messageId: string; action: string; state?: undefined \| "on" \| "off" }>` | An action button on a message was clicked. `action` is the built-in name or custom id. `state` is present only for the toggleable feedback votes: `'on'` when a like/dislike is set, `'off'` when re-tapped to clear. |
|
|
1630
1633
|
| `kai-model-change` | `CustomEvent<{ modelId: string }>` | The header model switcher changed. |
|
|
@@ -2696,7 +2699,7 @@ _No events._
|
|
|
2696
2699
|
| Property | Attribute | Type | Description |
|
|
2697
2700
|
|---|---|---|---|
|
|
2698
2701
|
| `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
|
|
2699
|
-
| `message` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "
|
|
2702
|
+
| `message` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }` | The full message object. Set as a JS property. |
|
|
2700
2703
|
| `role` | `role` | `undefined \| "user" \| "assistant"` | Who is speaking: `'user'` or `'assistant'`. Convenience for simple cases when not passing a `message` object. This is the SEMANTIC role of the message, not an ARIA role. The name collides with the global ARIA `role` attribute, which is why the facade lifts it off the host (see `liftRoleOffHost`). Neither speaker is a valid ARIA role, so a `role="user"` left on `<kai-message>` is a CRITICAL axe `aria-roles` violation. The accessible role lives on the row inside the shadow root instead: `role="article"` plus an `aria-label` naming the speaker, matching the SolidJS `<Message>` component. |
|
|
2701
2704
|
| `markdown` | `markdown` | `undefined \| false \| true` | Force markdown on/off. Defaults to on for assistant, off for user. |
|
|
2702
2705
|
| `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Text/markdown sizing for the message body. |
|
|
@@ -3867,7 +3870,7 @@ _No events._
|
|
|
3867
3870
|
| Property | Attribute | Type | Description |
|
|
3868
3871
|
|---|---|---|---|
|
|
3869
3872
|
| `theme` | `theme` | `"light" \| "dark" \| "auto"` | Color mode (`auto` follows prefers-color-scheme). |
|
|
3870
|
-
| `messages` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "
|
|
3873
|
+
| `messages` | — | `undefined \| { id: string; role: "user" \| "assistant"; parts: ({ type: "text"; text: string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "reasoning"; text: string; label?: undefined \| string; index?: undefined \| number; streamId?: undefined \| string; signature?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } } \| { type: "tool"; tool: { type: string; kind?: undefined \| "command" \| "file-change" \| "search" \| "fetch" \| "mcp" \| "image" \| "generic"; state: "input-streaming" \| "input-available" \| "output-available" \| "output-error"; input?: undefined \| Record<string, unknown>; rawInput?: undefined \| string; output?: undefined \| Record<string, unknown>; toolCallId?: undefined \| string; errorText?: undefined \| string; raw?: undefined \| { source: string; payload: unknown } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "card"; envelope: { 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 } }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "source"; source: { id?: undefined \| string; url?: undefined \| string; title?: undefined \| string; snippet?: undefined \| string; index?: undefined \| number }; raw?: undefined \| { source: string; payload: unknown } } \| { type: "file"; attachment: { id: string; type: "file" \| "source-document"; filename?: undefined \| string; mediaType?: undefined \| string; url?: undefined \| string; title?: undefined \| string }; raw?: undefined \| { source: string; payload: unknown } })[]; actions?: undefined \| ("copy" \| "dislike" \| "edit" \| "like" \| "regenerate" \| "speak" \| { 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" }[]` | The full message thread to render, newest last. Each entry carries its role, ordered `parts`, and optional actions/avatar/feedback. Set as a JS property (`el.messages = [...]`); a NEW array reference per streaming chunk re-renders (mutating in place does not). |
|
|
3871
3874
|
| `loading` | `loading` | `undefined \| false \| true` | Show a typing indicator on the pending assistant turn. Set it while awaiting the assistant's reply. |
|
|
3872
3875
|
| `proseSize` | `prose-size` | `undefined \| "xs" \| "sm" \| "base" \| "lg"` | Body/prose font scale for rendered markdown (`'xs' \| 'sm' \| 'base' \| 'lg'`). Defaults to `'sm'`. |
|
|
3873
3876
|
| `codeTheme` | `code-theme` | `undefined \| string` | Shiki theme name for syntax-highlighted code blocks (e.g. `'github-dark-dimmed'`). |
|
package/llms.txt
CHANGED
|
@@ -56,7 +56,7 @@ interface ChatMessage {
|
|
|
56
56
|
/** The ONLY content channel. Ordered. */
|
|
57
57
|
parts: MessagePart[];
|
|
58
58
|
/** Action buttons under the message. Chrome, not content. */
|
|
59
|
-
actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit')[];
|
|
59
|
+
actions?: ('copy' | 'like' | 'dislike' | 'regenerate' | 'edit' | 'speak')[];
|
|
60
60
|
avatar?: { src?: string; fallback?: string; alt?: string };
|
|
61
61
|
feedback?: 'like' | 'dislike';
|
|
62
62
|
}
|