@kitn.ai/ui 0.17.0 → 0.18.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 +28 -5
- package/dist/components/artifact.d.ts +39 -1
- package/dist/components/attachment-types.d.ts +12 -0
- package/dist/components/attachments.d.ts +2 -10
- package/dist/components/card.d.ts +27 -0
- package/dist/components/chain-of-thought.d.ts +78 -2
- package/dist/components/chat-scope-picker.d.ts +7 -0
- package/dist/components/chat-thread.d.ts +37 -12
- package/dist/components/choice-card.d.ts +26 -0
- package/dist/components/coachmark.d.ts +49 -0
- package/dist/components/composer-dom.d.ts +35 -0
- package/dist/components/composer-highlight.d.ts +48 -0
- package/dist/components/composer-history.d.ts +34 -0
- package/dist/components/composer.d.ts +86 -0
- package/dist/components/confirm-card.d.ts +17 -0
- package/dist/components/conversation-item.d.ts +9 -0
- package/dist/components/conversation-list.d.ts +36 -1
- package/dist/components/file-tree.d.ts +15 -0
- package/dist/components/form.d.ts +34 -0
- package/dist/components/markdown.d.ts +3 -0
- package/dist/components/message.d.ts +12 -0
- package/dist/components/model-switcher.d.ts +7 -0
- package/dist/components/prompt-input.d.ts +13 -4
- package/dist/components/prompt-suggestion.d.ts +9 -1
- package/dist/components/reasoning.d.ts +14 -1
- package/dist/components/response-compare.d.ts +14 -0
- package/dist/components/screen.d.ts +46 -0
- package/dist/components/tasks-card.d.ts +49 -1
- package/dist/components/toast.d.ts +12 -2
- package/dist/components/tool-types.d.ts +10 -0
- package/dist/components/tool.d.ts +8 -8
- package/dist/components/voice-input.d.ts +27 -0
- package/dist/components/voice-output.d.ts +33 -0
- package/dist/core-C8fzo39E.js +12 -0
- package/dist/custom-elements.json +8392 -1860
- package/dist/elements/agent-card.d.ts +1 -0
- package/dist/elements/agent-card.js +1 -0
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/autoloader.js +1 -1
- package/dist/elements/avatar.d.ts +1 -0
- package/dist/elements/avatar.js +1 -0
- package/dist/elements/badge.d.ts +1 -0
- package/dist/elements/badge.js +1 -0
- package/dist/elements/button.d.ts +1 -0
- package/dist/elements/button.js +1 -0
- 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-scope-picker.js +1 -1
- package/dist/elements/chat-types.d.ts +47 -0
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
- package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
- package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
- package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
- package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
- package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
- package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
- package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
- package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
- package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
- package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
- package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
- package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
- package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
- package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
- package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
- package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
- package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
- package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
- package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
- package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
- package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
- package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
- package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
- package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
- package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
- package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
- package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
- package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
- package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
- package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
- package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
- package/dist/elements/chunks/{index-CTBEJeo6.js → index-BCRhi84a.js} +1 -1
- package/dist/elements/chunks/info-B_toFS0l.js +1 -0
- package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
- package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
- package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
- package/dist/elements/chunks/loader-q6MamGus.js +1 -0
- package/dist/elements/chunks/{markdown-CMTGx9r8.js → markdown-DBrcqq4S.js} +9 -9
- package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
- package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
- package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
- package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
- package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
- package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
- package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
- package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
- package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
- package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
- package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
- package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
- package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
- package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
- package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
- package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
- package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
- package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
- package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
- package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
- package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
- package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
- package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
- package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
- package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
- package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
- package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
- package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
- package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
- package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
- package/dist/elements/chunks/x-C1qtndxn.js +1 -0
- package/dist/elements/coachmark.d.ts +1 -0
- package/dist/elements/coachmark.js +1 -0
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.d.ts +1 -0
- package/dist/elements/command.js +1 -0
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.d.ts +1 -0
- package/dist/elements/composer.js +1 -0
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/css.d.ts +1 -0
- package/dist/elements/default-input.d.ts +51 -0
- package/dist/elements/define.d.ts +53 -0
- package/dist/elements/dialog.d.ts +1 -0
- package/dist/elements/dialog.js +1 -0
- package/dist/elements/disclosure.d.ts +37 -0
- package/dist/elements/editable-label.d.ts +1 -0
- package/dist/elements/editable-label.js +1 -0
- 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.d.ts +1 -0
- package/dist/elements/hover-card.js +1 -0
- package/dist/elements/icon.d.ts +1 -0
- package/dist/elements/icon.js +1 -0
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.d.ts +1 -0
- package/dist/elements/input.js +1 -0
- package/dist/elements/kbd.d.ts +1 -0
- package/dist/elements/kbd.js +1 -0
- 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.d.ts +1 -0
- package/dist/elements/menu.js +1 -0
- 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.d.ts +1 -0
- package/dist/elements/nav.js +1 -0
- package/dist/elements/notice.d.ts +1 -0
- package/dist/elements/notice.js +1 -0
- package/dist/elements/pane-group.d.ts +1 -0
- package/dist/elements/pane-group.js +1 -0
- package/dist/elements/pane.d.ts +1 -0
- package/dist/elements/pane.js +1 -0
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.d.ts +1 -0
- package/dist/elements/progress-bar.js +1 -0
- package/dist/elements/prompt-dock.d.ts +1 -0
- package/dist/elements/prompt-dock.js +1 -0
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/register-impl.d.ts +0 -0
- package/dist/elements/register.d.ts +13 -0
- package/dist/elements/remote.d.ts +1 -0
- package/dist/elements/remote.js +1 -0
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +2 -2
- package/dist/elements/screen.d.ts +1 -0
- package/dist/elements/screen.js +1 -0
- package/dist/elements/scroll-area.d.ts +1 -0
- package/dist/elements/scroll-area.js +1 -0
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.d.ts +1 -0
- package/dist/elements/search.js +1 -0
- package/dist/elements/segmented.d.ts +1 -0
- package/dist/elements/segmented.js +1 -0
- package/dist/elements/separator.d.ts +1 -0
- package/dist/elements/separator.js +1 -0
- package/dist/elements/setting-item.d.ts +1 -0
- package/dist/elements/setting-item.js +1 -0
- package/dist/elements/settings-group.d.ts +1 -0
- package/dist/elements/settings-group.js +1 -0
- package/dist/elements/skeleton.d.ts +1 -0
- package/dist/elements/skeleton.js +1 -0
- package/dist/elements/slots.d.ts +176 -0
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.d.ts +1 -0
- package/dist/elements/status.js +1 -0
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.d.ts +1 -0
- package/dist/elements/tabs.js +1 -0
- 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/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.d.ts +1 -0
- package/dist/elements/tooltip.js +1 -0
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.d.ts +1 -0
- package/dist/elements/voice-output.js +1 -0
- package/dist/elements.d.ts +733 -58
- package/dist/engine-javascript-C1x7zo1_.js +141 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +8256 -5703
- package/dist/kai.es.js +1 -0
- package/dist/llms/llms-full.txt +1146 -64
- package/dist/llms/llms.txt +4 -4
- package/dist/mcp.es.js +62 -7
- package/dist/primitives/composer-model.d.ts +22 -0
- package/dist/primitives/composer-triggers.d.ts +19 -0
- package/dist/primitives/create-kai-chat.d.ts +38 -0
- package/dist/primitives/toast-store.d.ts +32 -2
- package/dist/primitives/use-speech-recognition.d.ts +26 -0
- package/dist/react/index.d.ts +1085 -97
- package/dist/react/runtime.d.ts +9 -2
- package/dist/react/use-kai-chat.d.ts +41 -0
- package/dist/react.js +572 -193
- package/dist/register-impl-CYWGkgbI.js +141 -0
- package/dist/schemas/tasks.schema.json +2 -2
- package/dist/state/index.d.ts +4 -0
- package/dist/state/messages.d.ts +11 -0
- package/dist/state/stream.d.ts +22 -0
- package/dist/state/suggestions.d.ts +4 -0
- package/dist/state.js +117 -0
- package/dist/theme.tokens.css +87 -0
- package/dist/types.d.ts +4 -0
- package/dist/ui/agent-card.d.ts +61 -0
- package/dist/ui/button.d.ts +5 -2
- package/dist/ui/card.d.ts +61 -0
- package/dist/ui/collapsible.d.ts +15 -1
- package/dist/ui/command.d.ts +33 -0
- package/dist/ui/dialog.d.ts +42 -0
- package/dist/ui/dropdown.d.ts +95 -1
- package/dist/ui/editable-label.d.ts +26 -0
- package/dist/ui/hover-card.d.ts +13 -1
- package/dist/ui/icon.d.ts +18 -0
- package/dist/ui/input.d.ts +33 -0
- package/dist/ui/kbd.d.ts +21 -0
- package/dist/ui/nav.d.ts +62 -0
- package/dist/ui/notice.d.ts +31 -0
- package/dist/ui/overlay.d.ts +12 -0
- package/dist/ui/pane-grid.d.ts +58 -0
- package/dist/ui/pane-group.d.ts +75 -0
- package/dist/ui/pane.d.ts +78 -0
- package/dist/ui/popover.d.ts +11 -1
- package/dist/ui/progress-bar.d.ts +23 -0
- package/dist/ui/prompt-dock.d.ts +96 -0
- package/dist/ui/resizable.d.ts +12 -0
- package/dist/ui/scroll-area.d.ts +4 -0
- package/dist/ui/segmented.d.ts +28 -0
- package/dist/ui/settings-group.d.ts +32 -0
- package/dist/ui/skeleton.d.ts +12 -0
- package/dist/ui/stat.d.ts +16 -0
- package/dist/ui/status.d.ts +13 -0
- package/dist/ui/switch.d.ts +8 -1
- package/dist/ui/tabs.d.ts +35 -0
- package/dist/ui/tooltip.d.ts +19 -1
- package/frameworks/react/index.tsx +1042 -157
- package/frameworks/react/runtime.tsx +30 -1
- package/frameworks/react/use-kai-chat.tsx +87 -0
- package/llms-full.txt +1146 -64
- package/llms.txt +4 -4
- package/package.json +23 -8
- package/src/agent-tooling/mcp/manifest.ts +14 -0
- package/src/agent-tooling/mcp/tools/debug.ts +10 -6
- package/src/agent-tooling/mcp/tools/reference.ts +32 -0
- package/src/agent-tooling/mcp/tools/scaffold.ts +30 -1
- package/src/components/artifact.tsx +98 -13
- package/src/components/attachment-types.ts +13 -0
- package/src/components/attachments.tsx +4 -14
- package/src/components/card.tsx +109 -15
- package/src/components/chain-of-thought.tsx +210 -1
- package/src/components/chat-scope-picker.tsx +13 -3
- package/src/components/chat-thread.tsx +225 -116
- package/src/components/choice-card.tsx +103 -11
- package/src/components/coachmark.tsx +245 -0
- package/src/components/component-meta.json +691 -102
- package/src/components/composer-dom.ts +159 -0
- package/src/components/composer-highlight.ts +242 -0
- package/src/components/composer-history.ts +82 -0
- package/src/components/composer.tsx +979 -0
- package/src/components/confirm-card.tsx +54 -1
- package/src/components/conversation-item.tsx +69 -7
- package/src/components/conversation-list.tsx +134 -37
- package/src/components/file-tree.tsx +197 -47
- package/src/components/form-widgets.tsx +9 -7
- package/src/components/form.tsx +140 -17
- package/src/components/image.tsx +3 -5
- package/src/components/link-preview.tsx +5 -4
- package/src/components/markdown.tsx +5 -2
- package/src/components/message.tsx +23 -4
- package/src/components/model-switcher.tsx +19 -4
- package/src/components/prompt-input.tsx +46 -75
- package/src/components/prompt-suggestion.tsx +48 -16
- package/src/components/reasoning.tsx +29 -6
- package/src/components/response-compare.tsx +31 -2
- package/src/components/screen.tsx +176 -0
- package/src/components/tasks-card.tsx +342 -21
- package/src/components/toast.tsx +119 -31
- package/src/components/tool-types.ts +10 -0
- package/src/components/tool.tsx +22 -16
- package/src/components/voice-input.tsx +102 -19
- package/src/components/voice-output.tsx +202 -0
- package/src/elements/agent-card.tsx +67 -0
- package/src/elements/artifact.tsx +53 -4
- package/src/elements/attachments.tsx +1 -0
- package/src/elements/avatar.tsx +33 -0
- package/src/elements/badge.tsx +32 -0
- package/src/elements/button.tsx +126 -0
- package/src/elements/card-media.jpg +0 -0
- package/src/elements/card.tsx +124 -31
- package/src/elements/cards.tsx +81 -7
- package/src/elements/chain-of-thought.tsx +77 -32
- package/src/elements/chat-scope-picker.tsx +35 -8
- package/src/elements/chat-types.ts +2 -2
- package/src/elements/chat-workspace.tsx +153 -41
- package/src/elements/chat.tsx +45 -23
- package/src/elements/choice.tsx +71 -19
- package/src/elements/coachmark.tsx +90 -0
- package/src/elements/command.tsx +209 -0
- package/src/elements/compare.tsx +24 -2
- package/src/elements/compiled.css +1 -1
- package/src/elements/composer.tsx +151 -0
- package/src/elements/confirm-card.tsx +36 -11
- package/src/elements/conversation-list.tsx +80 -11
- package/src/elements/default-input.tsx +75 -53
- package/src/elements/define.tsx +47 -6
- package/src/elements/dialog.tsx +98 -0
- package/src/elements/disclosure.ts +73 -0
- package/src/elements/editable-label.tsx +125 -0
- package/src/elements/element-manifest.json +129 -1
- package/src/elements/element-meta.json +5562 -1336
- package/src/elements/element-types.d.ts +732 -57
- package/src/elements/file-tree.tsx +12 -2
- package/src/elements/file-upload.tsx +1 -1
- package/src/elements/form.tsx +54 -11
- package/src/elements/hover-card.tsx +80 -0
- package/src/elements/icon-names.json +48 -0
- package/src/elements/icon.tsx +41 -0
- package/src/elements/input.tsx +203 -0
- package/src/elements/kbd.tsx +45 -0
- package/src/elements/menu.tsx +217 -0
- package/src/elements/message.tsx +58 -11
- package/src/elements/model-switcher.tsx +27 -1
- package/src/elements/nav.tsx +87 -0
- package/src/elements/notice.tsx +53 -0
- package/src/elements/pane-group.tsx +119 -0
- package/src/elements/pane.tsx +129 -0
- package/src/elements/popover.tsx +39 -17
- package/src/elements/progress-bar.tsx +39 -0
- package/src/elements/prompt-dock.tsx +81 -0
- package/src/elements/prompt-input.tsx +112 -63
- package/src/elements/prompt-suggestions.tsx +25 -9
- package/src/elements/reasoning.tsx +30 -9
- package/src/elements/register-impl.ts +38 -3
- package/src/elements/register.ts +11 -3
- package/src/elements/resizable.tsx +48 -11
- package/src/elements/screen.tsx +90 -0
- package/src/elements/scroll-area.tsx +33 -0
- package/src/elements/search.tsx +180 -0
- package/src/elements/segmented.tsx +135 -0
- package/src/elements/separator.tsx +36 -0
- package/src/elements/setting-item.tsx +50 -0
- package/src/elements/settings-group.tsx +37 -0
- package/src/elements/skeleton.tsx +45 -0
- package/src/elements/slots.ts +607 -0
- package/src/elements/source.tsx +13 -6
- package/src/elements/status.tsx +42 -0
- package/src/elements/switch.tsx +89 -14
- package/src/elements/tabs.tsx +95 -0
- package/src/elements/tasks.tsx +70 -14
- package/src/elements/toast.tsx +17 -2
- package/src/elements/tool.tsx +43 -9
- package/src/elements/tooltip.tsx +85 -0
- package/src/elements/video-poster.jpg +0 -0
- package/src/elements/voice-input.tsx +66 -16
- package/src/elements/voice-output.tsx +69 -0
- package/src/index.ts +5 -2
- package/src/primitives/card-schemas/tasks.schema.json +2 -2
- package/src/primitives/composer-model.ts +49 -0
- package/src/primitives/composer-triggers.ts +35 -0
- package/src/primitives/create-kai-chat.ts +66 -0
- package/src/primitives/toast-store.ts +43 -2
- package/src/primitives/use-speech-recognition.ts +135 -0
- package/src/state/index.ts +6 -0
- package/src/state/messages.ts +36 -0
- package/src/state/stream.ts +92 -0
- package/src/state/suggestions.ts +9 -0
- package/src/types.ts +4 -0
- package/src/ui/agent-card.tsx +212 -0
- package/src/ui/button.tsx +16 -5
- package/src/ui/card.tsx +245 -0
- package/src/ui/collapsible.tsx +30 -4
- package/src/ui/command.tsx +99 -0
- package/src/ui/dialog.tsx +217 -0
- package/src/ui/dropdown.tsx +375 -13
- package/src/ui/editable-label.tsx +112 -0
- package/src/ui/hover-card.tsx +28 -5
- package/src/ui/icon.tsx +110 -0
- package/src/ui/input.tsx +132 -0
- package/src/ui/kbd.tsx +118 -0
- package/src/ui/nav.tsx +282 -0
- package/src/ui/notice.tsx +84 -0
- package/src/ui/overlay.tsx +108 -2
- package/src/ui/pane-grid.tsx +116 -0
- package/src/ui/pane-group.tsx +280 -0
- package/src/ui/pane.tsx +193 -0
- package/src/ui/popover.tsx +16 -2
- package/src/ui/progress-bar.tsx +83 -0
- package/src/ui/prompt-dock.tsx +145 -0
- package/src/ui/resizable.tsx +69 -1
- package/src/ui/scroll-area.tsx +14 -3
- package/src/ui/segmented.tsx +109 -0
- package/src/ui/settings-group.tsx +67 -0
- package/src/ui/skeleton.tsx +62 -5
- package/src/ui/stat.tsx +41 -0
- package/src/ui/status.tsx +48 -0
- package/src/ui/switch.tsx +49 -23
- package/src/ui/tabs.tsx +168 -0
- package/src/ui/tooltip.tsx +52 -8
- package/theme.css +113 -0
- package/dist/components/slash-command.d.ts +0 -15
- package/dist/elements/chunks/Icon-D_Zmwmwh.js +0 -1
- package/dist/elements/chunks/action-icons-PVfIvcuG.js +0 -1
- package/dist/elements/chunks/attachments-Dp6vqtrc.js +0 -1
- package/dist/elements/chunks/button-CKyYD9G4.js +0 -1
- package/dist/elements/chunks/card-Cjho-PYk.js +0 -1
- package/dist/elements/chunks/chat-thread-RT808iPf.js +0 -1
- package/dist/elements/chunks/check-CDYaMYrM.js +0 -1
- package/dist/elements/chunks/chevron-down-3SBiq8HF.js +0 -1
- package/dist/elements/chunks/chevron-right-k2UFlIVK.js +0 -1
- package/dist/elements/chunks/cn-Dogu6PbV.js +0 -1
- package/dist/elements/chunks/code-block-BYUmfFSd.js +0 -1
- package/dist/elements/chunks/collapsible-BiNwdOn6.js +0 -1
- package/dist/elements/chunks/context-18g6oi4J.js +0 -1
- package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +0 -1
- package/dist/elements/chunks/default-input-CSAoYYMd.js +0 -1
- package/dist/elements/chunks/download-D8K0Je8r.js +0 -1
- package/dist/elements/chunks/dropdown-bUs2iTia.js +0 -1
- package/dist/elements/chunks/external-link-6mA0wxab.js +0 -1
- package/dist/elements/chunks/file-text-DnaYk3Dm.js +0 -1
- package/dist/elements/chunks/file-tree-CgtRLlxK.js +0 -1
- package/dist/elements/chunks/hover-card-CIGw62gP.js +0 -1
- package/dist/elements/chunks/link-BlGSeoPy.js +0 -1
- package/dist/elements/chunks/loader-DvhR4qa0.js +0 -1
- package/dist/elements/chunks/message-CDZwXjhL.js +0 -1
- package/dist/elements/chunks/message-feedback-CYx81QiA.js +0 -1
- package/dist/elements/chunks/model-switcher-O_tUhoOT.js +0 -1
- package/dist/elements/chunks/overlay-DydXYMQW.js +0 -1
- package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +0 -1
- package/dist/elements/chunks/reasoning-2PUnHlmr.js +0 -1
- package/dist/elements/chunks/resizable-B5WNbSeb.js +0 -1
- package/dist/elements/chunks/star-t9tI-P0q.js +0 -1
- package/dist/elements/chunks/store-C6rIooZz.js +0 -1
- package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +0 -1
- package/dist/elements/chunks/textarea-3nm6Ia9U.js +0 -1
- package/dist/elements/chunks/thumbs-up-BBtguGld.js +0 -1
- package/dist/elements/chunks/toast-store-D8cL3fWl.js +0 -1
- package/dist/elements/chunks/tool-GJ3vwp1U.js +0 -1
- package/dist/elements/chunks/tooltip-DwKAz4XR.js +0 -1
- package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +0 -1
- package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +0 -1
- package/dist/elements/chunks/video-mVYiM99-.js +0 -1
- package/dist/elements/chunks/x-CzxL27Ch.js +0 -1
- package/dist/kitn-chat.es.js +0 -1
- package/dist/register-JyTglR6m.js +0 -1
- package/dist/register-impl-C6wlSsts.js +0 -77
- package/src/components/slash-command.tsx +0 -232
- package/src/elements/framework-usage.json +0 -588
- /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
- /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createSignal, For, Show } from 'solid-js';
|
|
1
|
+
import { createSignal, createEffect, For, Show, onMount } from 'solid-js';
|
|
2
2
|
import { ChatConfig, useChatConfig } from '../primitives/chat-config';
|
|
3
|
+
import { type ComposerDoc, normalizeValue, serializeToText } from '../primitives/composer-model';
|
|
3
4
|
import { ChatContainer, ChatContainerContent, ChatContainerScrollAnchor } from './chat-container';
|
|
4
5
|
import { Message, MessageAvatar, MessageBody } from './message';
|
|
5
6
|
import { type AttachmentData } from './attachments';
|
|
@@ -11,7 +12,7 @@ import {
|
|
|
11
12
|
ContextContentBody, ContextContentFooter, ContextInputUsage, ContextOutputUsage,
|
|
12
13
|
} from './context';
|
|
13
14
|
import { DefaultPromptInput } from '../elements/default-input';
|
|
14
|
-
import type {
|
|
15
|
+
import type { TriggerDef } from './composer';
|
|
15
16
|
import type { ChatMessage } from '../elements/chat-types';
|
|
16
17
|
import type { ProseSize } from '../primitives/chat-config';
|
|
17
18
|
import type { ModelOption } from '../types';
|
|
@@ -31,9 +32,10 @@ export interface ChatThreadProps {
|
|
|
31
32
|
* content, and optional reasoning/tools/attachments/actions. Set as a JS
|
|
32
33
|
* property (`el.messages = [...]`). */
|
|
33
34
|
messages: ChatMessage[];
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
35
|
+
/** Value of the input. A **string** is controlled (the host owns the text and
|
|
36
|
+
* updates it on `kai-value-change`). A **ComposerDoc** is a one-time seed that
|
|
37
|
+
* pre-populates pills; the user then edits freely. Leave unset for uncontrolled. */
|
|
38
|
+
value?: string | ComposerDoc;
|
|
37
39
|
/** Placeholder text shown in the empty input. */
|
|
38
40
|
placeholder?: string;
|
|
39
41
|
/** When true, shows the loading/streaming state and disables submit (use while
|
|
@@ -75,29 +77,63 @@ export interface ChatThreadProps {
|
|
|
75
77
|
headerStart?: boolean;
|
|
76
78
|
/** Whether the host has `slot="header-end"` content (right of the controls). */
|
|
77
79
|
headerEnd?: boolean;
|
|
80
|
+
// ── Composition slots ─────────────────────────────────────────────────────
|
|
81
|
+
// Each flag below is set by the `<kai-chat>` facade when matching light-DOM
|
|
82
|
+
// `slot="…"` content is projected, and gates one composition slot. Two kinds:
|
|
83
|
+
// • INJECT — additive: project YOUR markup into a region (sidebar, footer,
|
|
84
|
+
// composer-actions, header-start/-end).
|
|
85
|
+
// • REPLACE — substitutive: your markup stands in for a whole region
|
|
86
|
+
// (header, empty, composer). A replaced region's projected
|
|
87
|
+
// content owns its own data/events — a slotted (light-DOM) node
|
|
88
|
+
// can't read this component's reactive state. That boundary is
|
|
89
|
+
// the whole reason `messages` stays a data prop, not a slot.
|
|
90
|
+
/** REPLACE — full custom header in place of the built-in title/model/context bar. */
|
|
91
|
+
headerFull?: boolean;
|
|
92
|
+
/** INJECT — left sidebar column (e.g. a conversation list / your own nav). */
|
|
93
|
+
sidebar?: boolean;
|
|
94
|
+
/** REPLACE — custom zero-state rendered in the message area while the thread is empty (replaces the empty message list only; the composer and its suggestions still render). */
|
|
95
|
+
empty?: boolean;
|
|
96
|
+
/** REPLACE — full custom composer in place of the built-in prompt input. The
|
|
97
|
+
* projected content wires its own submit (the data-flow boundary). */
|
|
98
|
+
composer?: boolean;
|
|
99
|
+
/** INJECT — accessory row just above the composer (e.g. extra actions). */
|
|
100
|
+
composerActions?: boolean;
|
|
101
|
+
/** INJECT — footer row below the composer (disclaimers, token meter, …). */
|
|
102
|
+
footer?: boolean;
|
|
78
103
|
/** Show a Search (Globe) button in the input toolbar; fires a `search` event. */
|
|
79
104
|
search?: boolean;
|
|
80
105
|
/** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
|
|
81
106
|
voice?: boolean;
|
|
82
|
-
/**
|
|
83
|
-
*
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
slashCompact?: boolean;
|
|
107
|
+
/** Rich entity triggers — each `{ char, kind, items }` opens a caret-anchored
|
|
108
|
+
* menu that inserts an atomic pill (`/` skills, `@` agents/plugins). Set as a
|
|
109
|
+
* JS property; forwarded to the input. */
|
|
110
|
+
triggers?: TriggerDef[];
|
|
111
|
+
/** Default icon per entity kind (kind → image src) for pills/menu items. */
|
|
112
|
+
kindIcons?: Record<string, string>;
|
|
89
113
|
/** Whether each message's action bar is always visible (`'always'`, default)
|
|
90
114
|
* or only revealed on hover of that message row (`'hover'`). */
|
|
91
115
|
actionsReveal?: 'always' | 'hover';
|
|
92
116
|
// callbacks (the facade maps these to dispatch())
|
|
93
117
|
onValueChange?: (value: string) => void;
|
|
94
118
|
onSubmit?: (detail: { value: string; attachments: AttachmentData[] }) => void;
|
|
119
|
+
onAttachmentsChange?: (attachments: AttachmentData[]) => void;
|
|
95
120
|
onSuggestionClick?: (value: string) => void;
|
|
96
121
|
onModelChange?: (modelId: string) => void;
|
|
97
122
|
onMessageAction?: (detail: MessageActionDetail) => void;
|
|
98
123
|
onSearch?: () => void;
|
|
99
124
|
onVoice?: () => void;
|
|
100
|
-
|
|
125
|
+
/** Receive the imperative controller once mounted. The kai-chat facade forwards
|
|
126
|
+
* these as element methods (focus/clear/send/scrollToBottom). */
|
|
127
|
+
controllerRef?: (controller: ChatThreadController) => void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Imperative handle exposed via `controllerRef` — the input half of the chat's
|
|
131
|
+
* interaction surface, forwarded onto `<kai-chat>` as instance methods. */
|
|
132
|
+
export interface ChatThreadController {
|
|
133
|
+
focus(options?: FocusOptions): void;
|
|
134
|
+
clear(): void;
|
|
135
|
+
send(): void;
|
|
136
|
+
scrollToBottom(behavior?: ScrollBehavior): void;
|
|
101
137
|
}
|
|
102
138
|
|
|
103
139
|
export function ChatThread(props: ChatThreadProps) {
|
|
@@ -112,14 +148,26 @@ export function ChatThread(props: ChatThreadProps) {
|
|
|
112
148
|
emit: (detail) => props.onMessageAction?.(detail),
|
|
113
149
|
target: () => rootEl,
|
|
114
150
|
});
|
|
115
|
-
const [internal, setInternal] = createSignal(props.value ?? '');
|
|
151
|
+
const [internal, setInternal] = createSignal<string | ComposerDoc>(props.value ?? '');
|
|
116
152
|
const [attachments, setAttachments] = createSignal<AttachmentData[]>([]);
|
|
117
|
-
|
|
153
|
+
// A string `value` is controlled; a ComposerDoc `value` is a one-time seed that
|
|
154
|
+
// lives in `internal` so the user's (string) edits replace it without a fight.
|
|
155
|
+
const current = (): string | ComposerDoc =>
|
|
156
|
+
typeof props.value === 'string' ? props.value : internal();
|
|
157
|
+
createEffect(() => {
|
|
158
|
+
const v = props.value;
|
|
159
|
+
if (v != null && typeof v !== 'string') setInternal(v);
|
|
160
|
+
});
|
|
118
161
|
const handleChange = (v: string) => { setInternal(v); props.onValueChange?.(v); };
|
|
119
|
-
|
|
162
|
+
// After a send, reset the composer. Clear the internal draft ONLY when the value is
|
|
163
|
+
// uncontrolled (props.value === undefined) — a controlled host owns its own value and
|
|
164
|
+
// clears it itself. This lets the batteries-included hooks (useKaiChat/createKaiChat),
|
|
165
|
+
// whose `bind` does not control `value`, get a clean composer after each submit.
|
|
166
|
+
const afterSubmit = () => { setAttachments([]); if (props.value === undefined) setInternal(''); };
|
|
167
|
+
const handleSubmit = () => { props.onSubmit?.({ value: serializeToText(normalizeValue(current())), attachments: attachments() }); afterSubmit(); };
|
|
120
168
|
const handleSuggestionClick = (v: string) => {
|
|
121
169
|
if ((props.suggestionMode ?? 'submit') === 'fill') { handleChange(v); props.onSuggestionClick?.(v); }
|
|
122
|
-
else { props.onSubmit?.({ value: v, attachments: attachments() });
|
|
170
|
+
else { props.onSubmit?.({ value: v, attachments: attachments() }); afterSubmit(); }
|
|
123
171
|
};
|
|
124
172
|
const showHeader = () => !!(props.chatTitle || props.models || props.context || props.headerStart || props.headerEnd);
|
|
125
173
|
// Suggestions are conversation starters: show only on an empty thread unless
|
|
@@ -128,112 +176,173 @@ export function ChatThread(props: ChatThreadProps) {
|
|
|
128
176
|
props.persistSuggestions || props.messages.length === 0 ? props.suggestions : undefined;
|
|
129
177
|
const showScrollButton = () => props.scrollButton !== false;
|
|
130
178
|
|
|
179
|
+
// Hand the imperative controller to the facade once mounted (rootEl is set).
|
|
180
|
+
onMount(() => {
|
|
181
|
+
props.controllerRef?.({
|
|
182
|
+
focus: (options) =>
|
|
183
|
+
rootEl
|
|
184
|
+
?.querySelector<HTMLElement>('[contenteditable]:not([contenteditable="false"]), textarea')
|
|
185
|
+
?.focus(options),
|
|
186
|
+
clear: () => { setInternal(''); setAttachments([]); props.onValueChange?.(''); },
|
|
187
|
+
send: () => handleSubmit(),
|
|
188
|
+
scrollToBottom: (behavior) => {
|
|
189
|
+
const vp = rootEl?.querySelector<HTMLElement>('.overflow-y-auto');
|
|
190
|
+
vp?.scrollTo({ top: vp.scrollHeight, behavior: behavior ?? 'smooth' });
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
131
195
|
return (
|
|
132
196
|
<ChatConfig proseSize={props.proseSize} codeTheme={props.codeTheme} codeHighlight={props.codeHighlight !== false} portalMount={outer.portalMount()}>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
197
|
+
{/* The root is a ROW so a `sidebar` slot can sit beside the main column.
|
|
198
|
+
With no sidebar projected it collapses to the original column. */}
|
|
199
|
+
<div ref={(e) => (rootEl = e as HTMLElement)} class={`flex h-full bg-background ${props.class ?? ''}`}>
|
|
200
|
+
<Show when={props.sidebar}>
|
|
201
|
+
<aside part="sidebar" class="flex w-64 shrink-0 flex-col overflow-hidden border-r border-border">
|
|
202
|
+
<slot name="sidebar" />
|
|
203
|
+
</aside>
|
|
204
|
+
</Show>
|
|
205
|
+
<div class="flex h-full min-w-0 flex-1 flex-col">
|
|
206
|
+
{/* Header: a full `header` slot REPLACES the built-in bar; otherwise the
|
|
207
|
+
built-in header renders, itself carrying the header-start/header-end
|
|
208
|
+
INJECT slots. */}
|
|
209
|
+
<Show
|
|
210
|
+
when={props.headerFull}
|
|
211
|
+
fallback={
|
|
212
|
+
<Show when={showHeader()}>
|
|
213
|
+
<header part="header-bar" class="flex h-14 shrink-0 items-center justify-between border-b border-border px-5">
|
|
214
|
+
<div class="flex items-center gap-2">
|
|
215
|
+
{/* Consumer-injected leading controls (sidebar-toggle, compose, a
|
|
216
|
+
popover title-button). Projects light-DOM `slot="header-start"`
|
|
217
|
+
children of <kai-chat>; inert outside a shadow root. */}
|
|
218
|
+
<slot name="header-start" />
|
|
219
|
+
<Show when={props.chatTitle}>
|
|
220
|
+
<div class="text-sm font-semibold text-foreground">{props.chatTitle}</div>
|
|
221
|
+
</Show>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="flex items-center gap-2">
|
|
224
|
+
<Show when={props.models}>
|
|
225
|
+
<ModelSwitcher
|
|
226
|
+
models={props.models!}
|
|
227
|
+
currentModelId={props.currentModel ?? props.models![0]?.id ?? ''}
|
|
228
|
+
onModelChange={(modelId) => props.onModelChange?.(modelId)}
|
|
229
|
+
/>
|
|
230
|
+
</Show>
|
|
231
|
+
<Show when={props.context}>
|
|
232
|
+
<Context
|
|
233
|
+
usedTokens={props.context!.usedTokens} maxTokens={props.context!.maxTokens}
|
|
234
|
+
inputTokens={props.context!.inputTokens} outputTokens={props.context!.outputTokens}
|
|
235
|
+
estimatedCost={props.context!.estimatedCost}
|
|
236
|
+
>
|
|
237
|
+
<ContextTrigger />
|
|
238
|
+
<ContextContent>
|
|
239
|
+
<ContextContentHeader />
|
|
240
|
+
<ContextContentBody><div class="space-y-1.5"><ContextInputUsage /><ContextOutputUsage /></div></ContextContentBody>
|
|
241
|
+
<ContextContentFooter />
|
|
242
|
+
</ContextContent>
|
|
243
|
+
</Context>
|
|
244
|
+
</Show>
|
|
245
|
+
{/* Consumer-injected trailing controls (share, settings, …).
|
|
246
|
+
Projects light-DOM `slot="header-end"` children of <kai-chat>. */}
|
|
247
|
+
<slot name="header-end" />
|
|
248
|
+
</div>
|
|
249
|
+
</header>
|
|
143
250
|
</Show>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
<header part="header" class="shrink-0"><slot name="header" /></header>
|
|
254
|
+
</Show>
|
|
255
|
+
<div class="relative flex-1 overflow-hidden">
|
|
256
|
+
<ChatContainer class="h-full px-4 py-3">
|
|
257
|
+
<ChatContainerContent class="mx-auto w-full max-w-3xl space-y-4">
|
|
258
|
+
{/* REPLACE — custom empty-state slot, shown only while the thread is
|
|
259
|
+
empty. The component still owns WHEN it shows (data state); the
|
|
260
|
+
consumer owns WHAT it looks like. */}
|
|
261
|
+
<Show when={props.empty && props.messages.length === 0}>
|
|
262
|
+
<slot name="empty" />
|
|
263
|
+
</Show>
|
|
264
|
+
<For each={props.messages}>
|
|
265
|
+
{(m) => {
|
|
266
|
+
const body = (
|
|
267
|
+
<MessageBody
|
|
268
|
+
content={m.content}
|
|
269
|
+
reasoning={m.reasoning}
|
|
270
|
+
tools={m.tools}
|
|
271
|
+
attachments={m.attachments}
|
|
272
|
+
isUser={m.role === 'user'}
|
|
273
|
+
markdown={m.role === 'assistant'}
|
|
274
|
+
actions={m.actions}
|
|
275
|
+
actionsReveal={reveal()}
|
|
276
|
+
activeFeedback={feedback.resolveFeedback(m)}
|
|
277
|
+
copied={feedback.isCopied(m.id)}
|
|
278
|
+
onAction={(action) => feedback.handleAction(m, action)}
|
|
279
|
+
/>
|
|
280
|
+
);
|
|
281
|
+
const rowGroup = reveal() === 'hover' ? 'group ' : '';
|
|
282
|
+
return (
|
|
283
|
+
<Show
|
|
284
|
+
when={m.avatar}
|
|
285
|
+
fallback={
|
|
286
|
+
<Message class={`${rowGroup}${m.role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}`}>
|
|
287
|
+
{body}
|
|
288
|
+
</Message>
|
|
289
|
+
}
|
|
290
|
+
>
|
|
291
|
+
{(av) => (
|
|
292
|
+
<Message class={rowGroup}>
|
|
293
|
+
<MessageAvatar src={av().src ?? ''} alt={av().alt ?? ''} fallback={av().fallback} />
|
|
294
|
+
<div class={`flex min-w-0 flex-1 flex-col ${m.role === 'user' ? 'items-end' : 'items-start'}`}>
|
|
295
|
+
{body}
|
|
296
|
+
</div>
|
|
297
|
+
</Message>
|
|
298
|
+
)}
|
|
299
|
+
</Show>
|
|
300
|
+
);
|
|
301
|
+
}}
|
|
302
|
+
</For>
|
|
303
|
+
<ChatContainerScrollAnchor />
|
|
304
|
+
</ChatContainerContent>
|
|
305
|
+
<Show when={showScrollButton()}>
|
|
306
|
+
<div class="absolute bottom-4 left-1/2 flex w-full max-w-3xl -translate-x-1/2 justify-center px-5">
|
|
307
|
+
<ScrollButton class="shadow-sm" />
|
|
308
|
+
</div>
|
|
152
309
|
</Show>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
310
|
+
</ChatContainer>
|
|
311
|
+
</div>
|
|
312
|
+
{/* INJECT — accessory row above the composer (extra actions/toolbar). */}
|
|
313
|
+
<Show when={props.composerActions}>
|
|
314
|
+
<div class="shrink-0 px-4">
|
|
315
|
+
<div class="mx-auto flex max-w-3xl items-center gap-2 pb-2"><slot name="composer-actions" /></div>
|
|
316
|
+
</div>
|
|
317
|
+
</Show>
|
|
318
|
+
<div class="shrink-0 px-4 pb-4">
|
|
319
|
+
<div class="mx-auto max-w-3xl">
|
|
320
|
+
{/* REPLACE — a full `composer` slot stands in for the built-in input.
|
|
321
|
+
The slotted content owns its own submit/loading wiring. */}
|
|
322
|
+
<Show
|
|
323
|
+
when={props.composer}
|
|
324
|
+
fallback={
|
|
325
|
+
<DefaultPromptInput
|
|
326
|
+
value={current()} placeholder={props.placeholder} loading={props.loading === true}
|
|
327
|
+
suggestions={visibleSuggestions()} attachments={attachments()}
|
|
328
|
+
search={props.search === true} voice={props.voice === true}
|
|
329
|
+
triggers={props.triggers} kindIcons={props.kindIcons}
|
|
330
|
+
onValueChange={handleChange} onSubmit={handleSubmit} onSuggestionClick={handleSuggestionClick}
|
|
331
|
+
onAttachmentsChange={(a) => { setAttachments(a); props.onAttachmentsChange?.(a); }}
|
|
332
|
+
onSearch={() => props.onSearch?.()} onVoice={() => props.onVoice?.()}
|
|
333
|
+
/>
|
|
334
|
+
}
|
|
335
|
+
>
|
|
336
|
+
<slot name="composer" />
|
|
166
337
|
</Show>
|
|
167
|
-
{/* Consumer-injected trailing controls (share, settings, …).
|
|
168
|
-
Projects light-DOM `slot="header-end"` children of <kai-chat>. */}
|
|
169
|
-
<slot name="header-end" />
|
|
170
338
|
</div>
|
|
171
|
-
</header>
|
|
172
|
-
</Show>
|
|
173
|
-
<div class="relative flex-1 overflow-hidden">
|
|
174
|
-
<ChatContainer class="h-full px-4 py-3">
|
|
175
|
-
<ChatContainerContent class="mx-auto w-full max-w-3xl space-y-4">
|
|
176
|
-
<For each={props.messages}>
|
|
177
|
-
{(m) => {
|
|
178
|
-
const body = (
|
|
179
|
-
<MessageBody
|
|
180
|
-
content={m.content}
|
|
181
|
-
reasoning={m.reasoning}
|
|
182
|
-
tools={m.tools}
|
|
183
|
-
attachments={m.attachments}
|
|
184
|
-
isUser={m.role === 'user'}
|
|
185
|
-
markdown={m.role === 'assistant'}
|
|
186
|
-
actions={m.actions}
|
|
187
|
-
actionsReveal={reveal()}
|
|
188
|
-
activeFeedback={feedback.resolveFeedback(m)}
|
|
189
|
-
copied={feedback.isCopied(m.id)}
|
|
190
|
-
onAction={(action) => feedback.handleAction(m, action)}
|
|
191
|
-
/>
|
|
192
|
-
);
|
|
193
|
-
const rowGroup = reveal() === 'hover' ? 'group ' : '';
|
|
194
|
-
return (
|
|
195
|
-
<Show
|
|
196
|
-
when={m.avatar}
|
|
197
|
-
fallback={
|
|
198
|
-
<Message class={`${rowGroup}${m.role === 'user' ? 'flex-col items-end' : 'flex-col items-start'}`}>
|
|
199
|
-
{body}
|
|
200
|
-
</Message>
|
|
201
|
-
}
|
|
202
|
-
>
|
|
203
|
-
{(av) => (
|
|
204
|
-
<Message class={rowGroup}>
|
|
205
|
-
<MessageAvatar src={av().src ?? ''} alt={av().alt ?? ''} fallback={av().fallback} />
|
|
206
|
-
<div class={`flex min-w-0 flex-1 flex-col ${m.role === 'user' ? 'items-end' : 'items-start'}`}>
|
|
207
|
-
{body}
|
|
208
|
-
</div>
|
|
209
|
-
</Message>
|
|
210
|
-
)}
|
|
211
|
-
</Show>
|
|
212
|
-
);
|
|
213
|
-
}}
|
|
214
|
-
</For>
|
|
215
|
-
<ChatContainerScrollAnchor />
|
|
216
|
-
</ChatContainerContent>
|
|
217
|
-
<Show when={showScrollButton()}>
|
|
218
|
-
<div class="absolute bottom-4 left-1/2 flex w-full max-w-3xl -translate-x-1/2 justify-center px-5">
|
|
219
|
-
<ScrollButton class="shadow-sm" />
|
|
220
|
-
</div>
|
|
221
|
-
</Show>
|
|
222
|
-
</ChatContainer>
|
|
223
|
-
</div>
|
|
224
|
-
<div class="shrink-0 px-4 pb-4">
|
|
225
|
-
<div class="mx-auto max-w-3xl">
|
|
226
|
-
<DefaultPromptInput
|
|
227
|
-
value={current()} placeholder={props.placeholder} loading={props.loading === true}
|
|
228
|
-
suggestions={visibleSuggestions()} attachments={attachments()}
|
|
229
|
-
search={props.search === true} voice={props.voice === true}
|
|
230
|
-
slashCommands={props.slashCommands} slashActiveIds={props.slashActiveIds} slashCompact={props.slashCompact === true}
|
|
231
|
-
onValueChange={handleChange} onSubmit={handleSubmit} onSuggestionClick={handleSuggestionClick}
|
|
232
|
-
onAttachmentsChange={setAttachments}
|
|
233
|
-
onSearch={() => props.onSearch?.()} onVoice={() => props.onVoice?.()}
|
|
234
|
-
onSlashSelect={(command) => props.onSlashSelect?.(command)}
|
|
235
|
-
/>
|
|
236
339
|
</div>
|
|
340
|
+
{/* INJECT — footer row below the composer. */}
|
|
341
|
+
<Show when={props.footer}>
|
|
342
|
+
<div part="footer" class="shrink-0 px-4 pb-3">
|
|
343
|
+
<div class="mx-auto max-w-3xl text-center text-xs text-muted-foreground"><slot name="footer" /></div>
|
|
344
|
+
</div>
|
|
345
|
+
</Show>
|
|
237
346
|
</div>
|
|
238
347
|
</div>
|
|
239
348
|
</ChatConfig>
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
createMemo,
|
|
9
9
|
createEffect,
|
|
10
10
|
on,
|
|
11
|
+
onMount,
|
|
11
12
|
ErrorBoundary,
|
|
12
13
|
createUniqueId,
|
|
13
14
|
} from 'solid-js';
|
|
@@ -151,6 +152,22 @@ export function firstEnabledIndex(options: ChoiceOption[]): number {
|
|
|
151
152
|
// The <ChoiceCard> component.
|
|
152
153
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
154
|
|
|
155
|
+
/** Imperative handle exposed via `controllerRef` — surfaces the choice card's
|
|
156
|
+
* latent selection/submit/dismiss capabilities so the `<kai-choice>` facade can
|
|
157
|
+
* forward them as instance methods (focus/select/send/dismiss/reopen). */
|
|
158
|
+
export interface ChoiceController {
|
|
159
|
+
/** Focus the radiogroup roving tab stop (or the Other input when selected). */
|
|
160
|
+
focus(options?: FocusOptions): void;
|
|
161
|
+
/** Select an option by id locally (no emit) — same as a row click. */
|
|
162
|
+
select(optionId: string): void;
|
|
163
|
+
/** Submit the current selection — emits the `action` verb + resolves single-shot. */
|
|
164
|
+
send(): void;
|
|
165
|
+
/** Dismiss the card — emits `dismiss` + optimistically collapses to the stub. */
|
|
166
|
+
dismiss(): void;
|
|
167
|
+
/** Re-open a dismissed card from its stub — emits `reopen`. */
|
|
168
|
+
reopen(): void;
|
|
169
|
+
}
|
|
170
|
+
|
|
154
171
|
export interface ChoiceCardProps {
|
|
155
172
|
/** The choice definition (CardEnvelope.data). */
|
|
156
173
|
data?: ChoiceCardData;
|
|
@@ -166,6 +183,17 @@ export interface ChoiceCardProps {
|
|
|
166
183
|
class?: string;
|
|
167
184
|
/** When set, render the chromed read-only view instead of the interactive radiogroup. */
|
|
168
185
|
resolution?: CardResolution;
|
|
186
|
+
/** Controlled selection (option id). When set, the consumer owns the current pick. */
|
|
187
|
+
value?: string;
|
|
188
|
+
/** Option id to pre-select on mount (uncontrolled seed). */
|
|
189
|
+
defaultValue?: string;
|
|
190
|
+
/** Disable the whole radiogroup + Submit (e.g. while the agent is busy). */
|
|
191
|
+
disabled?: boolean;
|
|
192
|
+
/** Fires when the selection changes BEFORE submit (row click or select()). */
|
|
193
|
+
onValueChange?: (value: string) => void;
|
|
194
|
+
/** Receive the imperative controller once mounted. The `<kai-choice>` facade
|
|
195
|
+
* forwards these as element methods (focus/select/send/dismiss/reopen). */
|
|
196
|
+
controllerRef?: (controller: ChoiceController) => void;
|
|
169
197
|
}
|
|
170
198
|
|
|
171
199
|
/**
|
|
@@ -182,7 +210,20 @@ export interface ChoiceCardProps {
|
|
|
182
210
|
*/
|
|
183
211
|
export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
184
212
|
const merged = mergeProps({ cardId: 'kai-choice' }, props);
|
|
185
|
-
const [local] = splitProps(merged, [
|
|
213
|
+
const [local] = splitProps(merged, [
|
|
214
|
+
'data',
|
|
215
|
+
'cardId',
|
|
216
|
+
'heading',
|
|
217
|
+
'host',
|
|
218
|
+
'hostElement',
|
|
219
|
+
'class',
|
|
220
|
+
'resolution',
|
|
221
|
+
'value',
|
|
222
|
+
'defaultValue',
|
|
223
|
+
'disabled',
|
|
224
|
+
'onValueChange',
|
|
225
|
+
'controllerRef',
|
|
226
|
+
]);
|
|
186
227
|
|
|
187
228
|
const ctxHost = useCardHost();
|
|
188
229
|
const uid = createUniqueId();
|
|
@@ -214,20 +255,33 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
214
255
|
});
|
|
215
256
|
|
|
216
257
|
const [focusIndex, setFocusIndex] = createSignal(0);
|
|
217
|
-
|
|
258
|
+
// Uncontrolled selection store, seeded from `defaultValue`. When the consumer
|
|
259
|
+
// sets the `value` prop the controlled path wins (same pattern as the composer):
|
|
260
|
+
// `selectedId()` reads value ?? internal, and `setSelection()` writes the
|
|
261
|
+
// internal store + fires onValueChange so a controlled host can mirror it.
|
|
262
|
+
const [internalId, setInternalId] = createSignal<string | undefined>(props.defaultValue);
|
|
263
|
+
const selectedId = createMemo<string | undefined>(() => local.value ?? internalId());
|
|
264
|
+
const setSelection = (id: string): void => {
|
|
265
|
+
if (selectedId() === id) return;
|
|
266
|
+
setInternalId(id);
|
|
267
|
+
local.onValueChange?.(id);
|
|
268
|
+
};
|
|
218
269
|
const [otherText, setOtherText] = createSignal('');
|
|
219
270
|
|
|
271
|
+
const isDisabled = (): boolean => local.disabled === true;
|
|
272
|
+
|
|
220
273
|
let groupRef: HTMLDivElement | undefined;
|
|
221
274
|
let otherInputRef: HTMLInputElement | undefined;
|
|
222
275
|
|
|
223
276
|
const otherSelected = createMemo(() => selectedId() === OTHER_ACTION);
|
|
224
277
|
|
|
225
|
-
// Reset all transient state whenever a NEW definition arrives
|
|
278
|
+
// Reset all transient state whenever a NEW definition arrives (re-seed the
|
|
279
|
+
// uncontrolled selection from `defaultValue`).
|
|
226
280
|
createEffect(
|
|
227
281
|
on(
|
|
228
282
|
() => local.data,
|
|
229
283
|
() => {
|
|
230
|
-
|
|
284
|
+
setInternalId(local.defaultValue);
|
|
231
285
|
setOtherText('');
|
|
232
286
|
setFocusIndex(Math.max(0, firstEnabledIndex(options())));
|
|
233
287
|
},
|
|
@@ -264,11 +318,12 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
264
318
|
|
|
265
319
|
const isOther = (opt: ChoiceOption): boolean => opt.id === OTHER_ACTION;
|
|
266
320
|
|
|
267
|
-
// Select (do NOT emit) the given option locally.
|
|
321
|
+
// Select (do NOT emit) the given option locally; fires onValueChange.
|
|
268
322
|
const select = (opt: ChoiceOption): void => {
|
|
269
323
|
if (res.isResolved()) return; // single-shot
|
|
324
|
+
if (isDisabled()) return; // group-level freeze
|
|
270
325
|
if (opt.disabled) return;
|
|
271
|
-
|
|
326
|
+
setSelection(opt.id);
|
|
272
327
|
if (isOther(opt)) {
|
|
273
328
|
queueMicrotask(() => otherInputRef?.focus());
|
|
274
329
|
}
|
|
@@ -277,6 +332,7 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
277
332
|
// Whether the current selection is submittable.
|
|
278
333
|
const canSubmit = createMemo(() => {
|
|
279
334
|
if (res.isResolved()) return false;
|
|
335
|
+
if (isDisabled()) return false; // group-level freeze
|
|
280
336
|
const id = selectedId();
|
|
281
337
|
if (id === undefined) return false;
|
|
282
338
|
if (id === OTHER_ACTION) return otherText().trim().length > 0;
|
|
@@ -288,6 +344,7 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
288
344
|
// Submit the current selection: emit `action` then resolve (single-shot).
|
|
289
345
|
const submit = (): void => {
|
|
290
346
|
if (res.isResolved()) return;
|
|
347
|
+
if (isDisabled()) return; // group-level freeze
|
|
291
348
|
const id = selectedId();
|
|
292
349
|
if (id === undefined) return;
|
|
293
350
|
if (id === OTHER_ACTION) {
|
|
@@ -321,13 +378,43 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
321
378
|
};
|
|
322
379
|
const onReopen = (): void => emit({ kind: 'reopen', cardId: local.cardId });
|
|
323
380
|
|
|
324
|
-
const focusRadio = (index: number): void => {
|
|
381
|
+
const focusRadio = (index: number, options?: FocusOptions): void => {
|
|
325
382
|
const radios = groupRef?.querySelectorAll<HTMLElement>('[role="radio"]');
|
|
326
|
-
radios?.[index]?.focus();
|
|
383
|
+
radios?.[index]?.focus(options);
|
|
327
384
|
};
|
|
328
385
|
|
|
386
|
+
// ── Imperative controller (Pattern C): hand the facade a handle over the
|
|
387
|
+
// card's latent selection/submit/dismiss capabilities. Every method drives
|
|
388
|
+
// the SAME internal path the buttons/keyboard drive, so the same kai-card
|
|
389
|
+
// events fire (and onValueChange for select()). ─────────────────────────────
|
|
390
|
+
onMount(() => {
|
|
391
|
+
local.controllerRef?.({
|
|
392
|
+
// Focus the roving radiogroup tab stop, or the Other input when it's selected.
|
|
393
|
+
focus: (options) => {
|
|
394
|
+
if (otherSelected() && otherInputRef) otherInputRef.focus(options);
|
|
395
|
+
else focusRadio(focusIndex(), options);
|
|
396
|
+
},
|
|
397
|
+
// Select an option by id locally (no emit) — same as a row click; respects
|
|
398
|
+
// disabled/resolved gating and moves the roving focus to the picked row.
|
|
399
|
+
select: (optionId) => {
|
|
400
|
+
const opts = options();
|
|
401
|
+
const idx = opts.findIndex((o) => o.id === optionId);
|
|
402
|
+
if (idx === -1) return;
|
|
403
|
+
setFocusIndex(idx);
|
|
404
|
+
select(opts[idx]);
|
|
405
|
+
},
|
|
406
|
+
// Submit the current selection (the Submit-button path).
|
|
407
|
+
send: () => submit(),
|
|
408
|
+
// Trigger the dismiss path (the X-button path).
|
|
409
|
+
dismiss: () => onDismiss(),
|
|
410
|
+
// Re-open a dismissed card (the stub's affordance path).
|
|
411
|
+
reopen: () => onReopen(),
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
|
|
329
415
|
const onGroupKeyDown = (e: KeyboardEvent): void => {
|
|
330
416
|
if (res.isResolved()) return;
|
|
417
|
+
if (isDisabled()) return; // group-level freeze
|
|
331
418
|
const opts = options();
|
|
332
419
|
const i = focusIndex();
|
|
333
420
|
if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
|
|
@@ -385,14 +472,18 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
385
472
|
role="radiogroup"
|
|
386
473
|
aria-label={groupLabel()}
|
|
387
474
|
aria-describedby={local.data?.prompt ? promptId : undefined}
|
|
388
|
-
|
|
475
|
+
aria-disabled={isDisabled() ? 'true' : undefined}
|
|
476
|
+
class={cn(
|
|
477
|
+
'divide-y divide-border overflow-hidden rounded-lg border border-input',
|
|
478
|
+
isDisabled() && 'cursor-not-allowed opacity-60',
|
|
479
|
+
)}
|
|
389
480
|
onKeyDown={onGroupKeyDown}
|
|
390
481
|
>
|
|
391
482
|
<For each={options()}>
|
|
392
483
|
{(opt, index) => {
|
|
393
484
|
const checked = () => selectedId() === opt.id;
|
|
394
485
|
const tabStop = () =>
|
|
395
|
-
!opt.disabled && index() === focusIndex() && !res.isResolved();
|
|
486
|
+
!opt.disabled && !isDisabled() && index() === focusIndex() && !res.isResolved();
|
|
396
487
|
const descId = `kai-choice-desc-${uid}-${opt.id}`;
|
|
397
488
|
const hasDesc = () => Boolean(opt.description);
|
|
398
489
|
return (
|
|
@@ -422,7 +513,8 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
422
513
|
id={otherInputId}
|
|
423
514
|
ref={otherInputRef}
|
|
424
515
|
type="text"
|
|
425
|
-
|
|
516
|
+
disabled={isDisabled()}
|
|
517
|
+
class="w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60"
|
|
426
518
|
placeholder={otherCfg()?.placeholder}
|
|
427
519
|
value={otherText()}
|
|
428
520
|
onInput={(e) => setOtherText(e.currentTarget.value)}
|