@kitn.ai/ui 0.16.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 +55 -17
- 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 +39 -16
- package/dist/components/choice-card.d.ts +27 -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/dismissed-stub.d.ts +22 -0
- 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 +71 -2
- 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-types.d.ts +66 -0
- package/dist/components/response-compare.d.ts +65 -0
- package/dist/components/screen.d.ts +46 -0
- package/dist/components/tasks-card.d.ts +50 -1
- package/dist/components/toast.d.ts +58 -0
- package/dist/components/tool-types.d.ts +10 -0
- package/dist/components/tool.d.ts +8 -8
- package/dist/components/use-card-resolution.d.ts +10 -6
- 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 +8472 -1691
- package/dist/elements/agent-card.d.ts +1 -0
- package/dist/elements/agent-card.js +1 -0
- package/dist/elements/artifact.js +1 -0
- package/dist/elements/attachments.js +1 -0
- package/dist/elements/autoloader.d.ts +2 -0
- package/dist/elements/autoloader.js +1 -0
- 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 -0
- package/dist/elements/cards.d.ts +1 -0
- package/dist/elements/cards.js +1 -0
- package/dist/elements/chain-of-thought.d.ts +0 -12
- package/dist/elements/chain-of-thought.js +1 -0
- package/dist/elements/chat-scope-picker.js +1 -0
- package/dist/elements/chat-types.d.ts +8 -2
- package/dist/elements/chat-workspace.js +1 -0
- package/dist/elements/chat.js +1 -0
- package/dist/elements/checkpoint.js +1 -0
- package/dist/elements/choice.js +1 -0
- 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/bash-InADTalH.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/card-routing-C986KuUA.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/core-AYMC6_lb.js +12 -0
- package/dist/elements/chunks/css-M7EaDHN_.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/engine-javascript-vq0WuIJl.js +141 -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/github-dark-dimmed-DUshB20C.js +1 -0
- package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
- package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
- package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
- package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
- package/dist/elements/chunks/index-BCRhi84a.js +1 -0
- package/dist/elements/chunks/info-B_toFS0l.js +1 -0
- package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
- package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
- package/dist/elements/chunks/json-DxJze_jm.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-DBrcqq4S.js +64 -0
- 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/svelte--5p79yCD.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/tsx-B8rCNbgL.js +1 -0
- package/dist/elements/chunks/typescript-RycA9KXf.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/vue-BmIZj4XD.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 -0
- package/dist/elements/command.d.ts +1 -0
- package/dist/elements/command.js +1 -0
- package/dist/elements/compare.d.ts +1 -0
- package/dist/elements/compare.js +1 -0
- package/dist/elements/composer.d.ts +1 -0
- package/dist/elements/composer.js +1 -0
- package/dist/elements/confirm-card.js +1 -0
- package/dist/elements/context-meter.js +1 -0
- package/dist/elements/conversation-list.d.ts +1 -11
- package/dist/elements/conversation-list.js +1 -0
- package/dist/elements/default-input.d.ts +20 -9
- package/dist/elements/define.d.ts +11 -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 -0
- package/dist/elements/empty.js +1 -0
- package/dist/elements/feedback-bar.js +1 -0
- package/dist/elements/file-tree.js +1 -0
- package/dist/elements/file-upload.js +1 -0
- package/dist/elements/form.js +1 -0
- 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 -0
- 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 -0
- package/dist/elements/loader.js +1 -0
- package/dist/elements/markdown.js +1 -0
- package/dist/elements/menu.d.ts +1 -0
- package/dist/elements/menu.js +1 -0
- package/dist/elements/message-skills.d.ts +0 -16
- package/dist/elements/message-skills.js +1 -0
- package/dist/elements/message.js +1 -0
- package/dist/elements/model-switcher.d.ts +1 -11
- package/dist/elements/model-switcher.js +1 -0
- 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 -0
- 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.d.ts +1 -9
- package/dist/elements/prompt-input.js +1 -0
- package/dist/elements/prompt-suggestions.d.ts +0 -6
- package/dist/elements/prompt-suggestions.js +1 -0
- package/dist/elements/reasoning.js +1 -0
- package/dist/elements/register.d.ts +10 -0
- package/dist/elements/remote.js +1 -0
- package/dist/elements/resizable.d.ts +1 -17
- package/dist/elements/resizable.js +1 -0
- package/dist/elements/response-stream.js +14 -0
- 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 -0
- 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.d.ts +0 -17
- package/dist/elements/source.js +1 -0
- package/dist/elements/status.d.ts +1 -0
- package/dist/elements/status.js +1 -0
- package/dist/elements/switch.js +1 -0
- package/dist/elements/tabs.d.ts +1 -0
- package/dist/elements/tabs.js +1 -0
- package/dist/elements/tasks.js +1 -0
- package/dist/elements/text-shimmer.js +1 -0
- package/dist/elements/thinking-bar.js +1 -0
- package/dist/elements/toast.d.ts +1 -0
- package/dist/elements/toast.js +1 -0
- package/dist/elements/tool.js +1 -0
- package/dist/elements/tooltip.d.ts +1 -0
- package/dist/elements/tooltip.js +1 -0
- package/dist/elements/voice-input.js +1 -0
- package/dist/elements/voice-output.d.ts +1 -0
- package/dist/elements/voice-output.js +1 -0
- package/dist/elements.d.ts +772 -61
- package/dist/engine-javascript-C1x7zo1_.js +141 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +14405 -9810
- package/dist/kai.es.js +1 -0
- package/dist/llms/llms-full.txt +1234 -107
- package/dist/llms/llms.txt +4 -4
- package/dist/mcp.es.js +217 -6
- package/dist/primitives/card-contract.d.ts +20 -4
- package/dist/primitives/card-recovery.d.ts +55 -0
- 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/message-feedback.d.ts +35 -0
- package/dist/primitives/toast-store.d.ts +118 -0
- package/dist/primitives/use-speech-recognition.d.ts +26 -0
- package/dist/react/index.d.ts +1166 -98
- package/dist/react/runtime.d.ts +9 -2
- package/dist/react/use-kai-chat.d.ts +41 -0
- package/dist/react.js +575 -186
- package/dist/register-impl-CYWGkgbI.js +141 -0
- package/dist/schemas/choice.schema.json +5 -0
- package/dist/schemas/tasks.schema.json +7 -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 +20 -1
- package/frameworks/react/index.tsx +1100 -163
- package/frameworks/react/runtime.tsx +30 -1
- package/frameworks/react/use-kai-chat.tsx +87 -0
- package/llms-full.txt +1234 -107
- package/llms.txt +4 -4
- package/package.json +36 -12
- package/src/agent-tooling/mcp/manifest.ts +14 -0
- package/src/agent-tooling/mcp/tools/debug.ts +194 -6
- package/src/agent-tooling/mcp/tools/reference.ts +32 -0
- package/src/agent-tooling/mcp/tools/scaffold.ts +162 -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 +238 -137
- package/src/components/choice-card.tsx +136 -15
- package/src/components/coachmark.tsx +245 -0
- package/src/components/component-meta.json +921 -108
- 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 +69 -2
- package/src/components/conversation-item.tsx +69 -7
- package/src/components/conversation-list.tsx +134 -37
- package/src/components/dismissed-stub.tsx +80 -0
- package/src/components/file-tree.tsx +197 -47
- package/src/components/form-widgets.tsx +9 -7
- package/src/components/form.tsx +161 -20
- 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 +174 -14
- 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-types.ts +127 -0
- package/src/components/response-compare.tsx +460 -0
- package/src/components/screen.tsx +176 -0
- package/src/components/tasks-card.tsx +374 -23
- package/src/components/toast.tsx +486 -0
- package/src/components/tool-types.ts +10 -0
- package/src/components/tool.tsx +22 -16
- package/src/components/use-card-resolution.ts +24 -8
- 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/autoloader.ts +89 -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 +9 -2
- package/src/elements/chat-workspace.tsx +157 -43
- package/src/elements/chat.tsx +49 -25
- 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 +110 -0
- 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 +310 -0
- package/src/elements/element-meta.json +5954 -1605
- package/src/elements/element-types.d.ts +771 -60
- 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 +84 -55
- 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 +41 -3
- package/src/elements/register.ts +20 -3
- package/src/elements/remote.tsx +4 -0
- 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/styles.css +19 -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 +89 -0
- 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 +35 -2
- package/src/primitives/card-contract.ts +14 -5
- package/src/primitives/card-recovery.ts +149 -0
- package/src/primitives/card-routing.ts +3 -0
- package/src/primitives/card-schemas/choice.schema.json +5 -0
- package/src/primitives/card-schemas/tasks.schema.json +7 -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/message-feedback.ts +123 -0
- package/src/primitives/toast-store.ts +276 -0
- 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 +57 -7
- package/theme.css +113 -0
- package/dist/components/slash-command.d.ts +0 -15
- package/dist/kitn-chat.es.js +0 -1
- package/dist/register-impl-wCsFPoh9.js +0 -77
- package/dist/register-ujhr1nBS.js +0 -1
- package/src/components/slash-command.tsx +0 -232
- package/src/elements/framework-usage.json +0 -563
- /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
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createSignal } from 'solid-js';
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
|
+
import type { ComposerDoc, EntityRef } from '../primitives/composer-model';
|
|
4
|
+
import type { TriggerDef, TriggerItem, HighlightRule, ComposerChange, ComposerController } from '../components/composer';
|
|
5
|
+
import { Composer } from '../components/composer';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse a single light-DOM `<kai-trigger-item>` element into a TriggerItem.
|
|
9
|
+
* Attribute mapping:
|
|
10
|
+
* - `item-id` → TriggerItem.id (required; empty string fallback)
|
|
11
|
+
* - textContent → TriggerItem.label (primary); `label` attr as fallback
|
|
12
|
+
* - `icon` → TriggerItem.icon
|
|
13
|
+
* - `prompt-text` → TriggerItem.promptText
|
|
14
|
+
* - `data` is left undefined (complex object, not representable as an attribute)
|
|
15
|
+
*
|
|
16
|
+
* @param n The `<kai-trigger-item>` element to parse.
|
|
17
|
+
* @param char The trigger character this item belongs to (e.g. `/` or `@`).
|
|
18
|
+
* @param kind The entity kind for this trigger (e.g. `'skill'` or `'mention'`).
|
|
19
|
+
*/
|
|
20
|
+
export function parseKaiTriggerItemElement(n: Element, char: string, kind: string): TriggerItem {
|
|
21
|
+
// char and kind inform the consumer but are not stored on TriggerItem itself.
|
|
22
|
+
void char;
|
|
23
|
+
void kind;
|
|
24
|
+
return {
|
|
25
|
+
id: n.getAttribute('item-id') ?? '',
|
|
26
|
+
label: n.textContent?.trim() || n.getAttribute('label') || n.getAttribute('item-id') || '',
|
|
27
|
+
icon: n.getAttribute('icon') ?? undefined,
|
|
28
|
+
promptText: n.getAttribute('prompt-text') ?? undefined,
|
|
29
|
+
data: undefined,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface Props extends Record<string, unknown> {
|
|
34
|
+
/** Controlled value — string or a full ComposerDoc (set as JS property). */
|
|
35
|
+
value?: string | ComposerDoc;
|
|
36
|
+
/** Placeholder text shown when the composer is empty. */
|
|
37
|
+
placeholder?: string;
|
|
38
|
+
/** Disable the composer entirely (non-interactive). */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Show a loading/streaming state and block submit. */
|
|
41
|
+
loading?: boolean;
|
|
42
|
+
/** Maximum height in px before the content scrolls. Default 240. */
|
|
43
|
+
maxHeight?: number | string;
|
|
44
|
+
/** Whether pressing Enter (without Shift) submits. Default true. */
|
|
45
|
+
submitOnEnter?: boolean;
|
|
46
|
+
/** Trigger definitions — set as a JS property. */
|
|
47
|
+
triggers?: TriggerDef[];
|
|
48
|
+
/** Keyword highlight rules — set as a JS property. */
|
|
49
|
+
highlights?: HighlightRule[];
|
|
50
|
+
/** Default icon per entity kind (kind → image URL/data-URI) for items without
|
|
51
|
+
* their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. */
|
|
52
|
+
kindIcons?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Events fired by `<kai-composer>`. */
|
|
56
|
+
interface Events {
|
|
57
|
+
/** The user submitted the composer (Enter or programmatic submit). */
|
|
58
|
+
'kai-submit': { doc: ComposerDoc; text: string; entities: EntityRef[] };
|
|
59
|
+
/** The content changed (fires on every input event). */
|
|
60
|
+
'kai-value-change': { doc: ComposerDoc; text: string; entities: EntityRef[] };
|
|
61
|
+
/** An entity pill was inserted into the composer. */
|
|
62
|
+
'kai-entity-add': { entity: EntityRef };
|
|
63
|
+
/** An entity pill was deleted from the composer. */
|
|
64
|
+
'kai-entity-remove': { entity: EntityRef };
|
|
65
|
+
/** A trigger character was detected at the caret (e.g. `/` or `@`). */
|
|
66
|
+
'kai-trigger': { char: string; query: string; rect: DOMRect };
|
|
67
|
+
/** The active trigger was dismissed (Escape, space, or outside click). */
|
|
68
|
+
'kai-trigger-close': Record<string, never>;
|
|
69
|
+
/** The composer gained focus. `focus`/`blur` are NOT composed natively, so they
|
|
70
|
+
* don't escape the shadow root — these re-expose them on the host. (For
|
|
71
|
+
* `keydown`/`paste`/`focusin`/`focusout`, listen NATIVELY on `<kai-composer>`:
|
|
72
|
+
* they're composed and already cross the shadow boundary.) */
|
|
73
|
+
'kai-focus': { originalEvent: FocusEvent };
|
|
74
|
+
/** The composer lost focus. */
|
|
75
|
+
'kai-blur': { originalEvent: FocusEvent };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
defineWebComponent<Props, Events>('kai-composer', {
|
|
79
|
+
value: undefined,
|
|
80
|
+
placeholder: undefined,
|
|
81
|
+
disabled: false,
|
|
82
|
+
loading: false,
|
|
83
|
+
maxHeight: 240,
|
|
84
|
+
submitOnEnter: true,
|
|
85
|
+
triggers: undefined,
|
|
86
|
+
highlights: undefined,
|
|
87
|
+
kindIcons: undefined,
|
|
88
|
+
}, (props, { dispatch, flag, expose }) => {
|
|
89
|
+
// Internal signal for uncontrolled value; when the consumer sets `value` as a
|
|
90
|
+
// JS property the controlled path takes precedence (same pattern as prompt-input).
|
|
91
|
+
const [internal, setInternal] = createSignal<string | ComposerDoc | undefined>(props.value);
|
|
92
|
+
|
|
93
|
+
const current = () => props.value ?? internal();
|
|
94
|
+
|
|
95
|
+
// ── Imperative API (instance methods on the host) ──────────────────────────
|
|
96
|
+
// Pattern C: the Composer component owns the editable + doc state and hands up
|
|
97
|
+
// a controller; the facade captures it and exposes delegating methods. The
|
|
98
|
+
// composer's own callbacks still fire (clear → onChange → kai-value-change;
|
|
99
|
+
// send → onSubmit → kai-submit; insertEntity → onEntityAdd → kai-entity-add).
|
|
100
|
+
let controller: ComposerController | undefined;
|
|
101
|
+
expose({
|
|
102
|
+
/** Focus the editable element. `focus`/`blur` are NOT composed natively, so a
|
|
103
|
+
* host-level focus() can't reach the editable inside the shadow root — this is
|
|
104
|
+
* the only way to focus the composer programmatically. */
|
|
105
|
+
focus: (options?: FocusOptions) => controller?.focus(options),
|
|
106
|
+
/** Blur the editable element. */
|
|
107
|
+
blur: () => controller?.blur(),
|
|
108
|
+
/** Empty the composer to a blank doc (resets the internal value + history
|
|
109
|
+
* baseline; fires kai-value-change). */
|
|
110
|
+
clear: () => controller?.clear(),
|
|
111
|
+
/** Submit the current content programmatically — same path as Enter (fires
|
|
112
|
+
* kai-submit). Named `send`, not `submit`, to match the shared vocabulary and
|
|
113
|
+
* avoid any submit collision. */
|
|
114
|
+
send: () => controller?.send(),
|
|
115
|
+
/** Insert an atomic entity pill (skill/agent/plugin) at the caret without
|
|
116
|
+
* typing a trigger (fires kai-entity-add). */
|
|
117
|
+
insertEntity: (entity: EntityRef) => controller?.insertEntity(entity),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const handleChange = (change: ComposerChange) => {
|
|
121
|
+
setInternal(change.doc);
|
|
122
|
+
dispatch('kai-value-change', { doc: change.doc, text: change.text, entities: change.entities });
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const handleSubmit = (change: ComposerChange) => {
|
|
126
|
+
dispatch('kai-submit', { doc: change.doc, text: change.text, entities: change.entities });
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Composer
|
|
131
|
+
value={current()}
|
|
132
|
+
placeholder={props.placeholder}
|
|
133
|
+
disabled={flag('disabled')}
|
|
134
|
+
loading={flag('loading')}
|
|
135
|
+
maxHeight={props.maxHeight as number | string | undefined}
|
|
136
|
+
submitOnEnter={flag('submitOnEnter')}
|
|
137
|
+
triggers={props.triggers}
|
|
138
|
+
highlights={props.highlights}
|
|
139
|
+
kindIcons={props.kindIcons as Record<string, string> | undefined}
|
|
140
|
+
onChange={handleChange}
|
|
141
|
+
onSubmit={handleSubmit}
|
|
142
|
+
onEntityAdd={(entity) => dispatch('kai-entity-add', { entity })}
|
|
143
|
+
onEntityRemove={(entity) => dispatch('kai-entity-remove', { entity })}
|
|
144
|
+
onTrigger={(info) => dispatch('kai-trigger', info)}
|
|
145
|
+
onTriggerClose={() => dispatch('kai-trigger-close', {})}
|
|
146
|
+
onFocus={(e) => dispatch('kai-focus', { originalEvent: e })}
|
|
147
|
+
onBlur={(e) => dispatch('kai-blur', { originalEvent: e })}
|
|
148
|
+
controllerRef={(c) => (controller = c)}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineWebComponent } from './define';
|
|
2
|
-
import { ConfirmCard, type ConfirmCardData } from '../components/confirm-card';
|
|
2
|
+
import { ConfirmCard, type ConfirmCardData, type ConfirmController } from '../components/confirm-card';
|
|
3
3
|
import type { CardResolution } from '../primitives/card-contract';
|
|
4
4
|
|
|
5
5
|
interface Props extends Record<string, unknown> {
|
|
@@ -28,6 +28,9 @@ interface Props extends Record<string, unknown> {
|
|
|
28
28
|
* optional close affordance, and `error` for a malformed definition (inline error).
|
|
29
29
|
* Routes through a `CardProvider` when present, else the bubbling `kai-card` event.
|
|
30
30
|
* Isolated in Shadow DOM; theme-aware via the shared kit tokens.
|
|
31
|
+
*
|
|
32
|
+
* Exposes instance methods `focus()`/`confirm(actionId?)`/`dismiss()`/`reopen()`
|
|
33
|
+
* that drive the same internal paths as the buttons.
|
|
31
34
|
*/
|
|
32
35
|
defineWebComponent<Props>(
|
|
33
36
|
'kai-confirm',
|
|
@@ -38,14 +41,36 @@ defineWebComponent<Props>(
|
|
|
38
41
|
autofocus: false,
|
|
39
42
|
resolution: undefined,
|
|
40
43
|
},
|
|
41
|
-
(props, { element, flag }) =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
(props, { element, flag, expose }) => {
|
|
45
|
+
// Pattern C: the ConfirmCard owns the action/dismiss state and hands up a
|
|
46
|
+
// controller; the facade captures it and exposes delegating methods. The card's
|
|
47
|
+
// own paths still fire (confirm → kai-card `action`; dismiss/reopen → kai-card
|
|
48
|
+
// `dismiss`/`reopen`).
|
|
49
|
+
let controller: ConfirmController | undefined;
|
|
50
|
+
expose({
|
|
51
|
+
/** Focus the default action button (or the first action if none is default) —
|
|
52
|
+
* the same target `autofocus` focuses on mount, but on demand. */
|
|
53
|
+
focus: (options?: FocusOptions) => controller?.focus(options),
|
|
54
|
+
/** Activate an action by id — emits the `action` verb on kai-card and resolves
|
|
55
|
+
* the card (single-shot). With no id, invokes the default action. */
|
|
56
|
+
confirm: (actionId?: string) => controller?.confirm(actionId),
|
|
57
|
+
/** Trigger the dismiss path — emits `dismiss` on kai-card and optimistically
|
|
58
|
+
* collapses the card to its re-openable stub. */
|
|
59
|
+
dismiss: () => controller?.dismiss(),
|
|
60
|
+
/** Re-open a dismissed card from its stub — emits `reopen` on kai-card. */
|
|
61
|
+
reopen: () => controller?.reopen(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<ConfirmCard
|
|
66
|
+
data={props.data as ConfirmCardData | undefined}
|
|
67
|
+
cardId={props.cardId ?? (element.id || 'kai-confirm')}
|
|
68
|
+
heading={props.heading}
|
|
69
|
+
autofocus={flag('autofocus')}
|
|
70
|
+
resolution={props.resolution as CardResolution | undefined}
|
|
71
|
+
controllerRef={(c) => (controller = c)}
|
|
72
|
+
hostElement={element}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
},
|
|
51
76
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { createSignal, onMount, onCleanup } from 'solid-js';
|
|
1
|
+
import { createSignal, onMount, onCleanup, Show } from 'solid-js';
|
|
2
2
|
import { defineWebComponent } from './define';
|
|
3
|
-
import {
|
|
3
|
+
import { createControllableSignal } from '../primitives/controllable';
|
|
4
|
+
import { readSlots, CONVERSATIONS_SLOTS } from './slots';
|
|
5
|
+
import { ConversationList, CollapsedRail, type ConversationListController } from '../components/conversation-list';
|
|
4
6
|
import type { ConversationGroup, ConversationSummary, ConversationScope } from '../types';
|
|
5
7
|
|
|
6
8
|
interface Props extends Record<string, unknown> {
|
|
@@ -13,6 +15,14 @@ interface Props extends Record<string, unknown> {
|
|
|
13
15
|
conversations: ConversationSummary[];
|
|
14
16
|
/** The id of the currently-open conversation, highlighted in the list. */
|
|
15
17
|
activeId?: string;
|
|
18
|
+
/** Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to
|
|
19
|
+
* drive the rail from your app, updating it in response to `kai-collapse-toggle`.
|
|
20
|
+
* Omit for uncontrolled (the element manages it). Collapsed shrinks the rail to
|
|
21
|
+
* a floating reopen button. */
|
|
22
|
+
collapsed?: boolean;
|
|
23
|
+
/** Initial collapsed state when uncontrolled (default false). Use the
|
|
24
|
+
* `default-collapsed` attribute to start collapsed in plain HTML. */
|
|
25
|
+
defaultCollapsed?: boolean;
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
interface Events {
|
|
@@ -22,6 +32,12 @@ interface Events {
|
|
|
22
32
|
'kai-new-chat': Record<string, never>;
|
|
23
33
|
/** The sidebar toggle was clicked. */
|
|
24
34
|
'kai-toggle-sidebar': Record<string, never>;
|
|
35
|
+
/** The rail was collapsed or expanded (via the toggle, the reopen button, or a
|
|
36
|
+
* `collapse()`/`expand()`/`toggle()` call). */
|
|
37
|
+
'kai-collapse-toggle': { collapsed: boolean };
|
|
38
|
+
/** The built-in search box query changed (typing, or a programmatic `clear()`
|
|
39
|
+
* which fires it with `''`). Lets a consumer mirror or server-side the filter. */
|
|
40
|
+
'kai-search': { query: string };
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
/** Parse a single light-DOM `<kai-conversation>` element into a `ConversationSummary`.
|
|
@@ -49,14 +65,19 @@ defineWebComponent<Props, Events>('kai-conversations', {
|
|
|
49
65
|
groups: [],
|
|
50
66
|
conversations: [],
|
|
51
67
|
activeId: undefined,
|
|
52
|
-
|
|
68
|
+
collapsed: undefined,
|
|
69
|
+
defaultCollapsed: undefined,
|
|
70
|
+
}, (props, { dispatch, element, expose, flag }) => {
|
|
53
71
|
// Read declarative <kai-conversation> children from light DOM.
|
|
54
72
|
// Shadow DOM with no <slot> suppresses them visually — they're invisible data carriers.
|
|
55
73
|
const [slottedConversations, setSlottedConversations] = createSignal<ConversationSummary[]>([]);
|
|
74
|
+
// Which composition slots (header/empty/footer) the consumer has filled.
|
|
75
|
+
const [slots, setSlots] = createSignal<Record<string, boolean>>({});
|
|
56
76
|
onMount(() => {
|
|
57
77
|
const read = () => {
|
|
58
78
|
const nodes = [...element.querySelectorAll('kai-conversation')];
|
|
59
79
|
setSlottedConversations(nodes.map(parseKaiConversationElement));
|
|
80
|
+
setSlots(readSlots(element, CONVERSATIONS_SLOTS));
|
|
60
81
|
};
|
|
61
82
|
read();
|
|
62
83
|
const observer = new MutationObserver(read);
|
|
@@ -67,14 +88,62 @@ defineWebComponent<Props, Events>('kai-conversations', {
|
|
|
67
88
|
// Prop conversations take precedence; slotted children are appended after.
|
|
68
89
|
const allConversations = () => [...(props.conversations ?? []), ...slottedConversations()];
|
|
69
90
|
|
|
91
|
+
// ── Rail collapse (controlled/uncontrolled, same pattern as kai-workspace) ──
|
|
92
|
+
// `collapsed` (when set) wins; otherwise the element manages its own state,
|
|
93
|
+
// seeded from `defaultCollapsed`. `setCollapsedTo` always writes the internal
|
|
94
|
+
// value (a no-op visually while controlled) and emits `kai-collapse-toggle` so
|
|
95
|
+
// a controlling app can update its own state. Collapsed → the shared
|
|
96
|
+
// CollapsedRail (the same floating reopen button kai-workspace renders).
|
|
97
|
+
const [collapsed, setCollapsed] = createControllableSignal(
|
|
98
|
+
() => props.collapsed as boolean | undefined,
|
|
99
|
+
flag('defaultCollapsed'),
|
|
100
|
+
);
|
|
101
|
+
const setCollapsedTo = (next: boolean) => { setCollapsed(next); dispatch('kai-collapse-toggle', { collapsed: next }); };
|
|
102
|
+
|
|
103
|
+
// ── Imperative API (instance methods on the host) ──────────────────────────
|
|
104
|
+
// The search box's query lives inside ConversationList; we capture its
|
|
105
|
+
// controller (Pattern C) to focus / clear it from the facade.
|
|
106
|
+
let controller: ConversationListController | undefined;
|
|
107
|
+
expose({
|
|
108
|
+
/** Focus the built-in search input inside the shadow root. */
|
|
109
|
+
focus: (options?: FocusOptions) => {
|
|
110
|
+
// Prefer the captured controller; fall back to a shadow query (the search
|
|
111
|
+
// box only renders when there are conversations).
|
|
112
|
+
if (controller) controller.focus(options);
|
|
113
|
+
else element.shadowRoot?.querySelector<HTMLInputElement>('input')?.focus(options);
|
|
114
|
+
},
|
|
115
|
+
/** Clear the internal search query (resets the list filter) and fire
|
|
116
|
+
* kai-search with an empty string. */
|
|
117
|
+
clear: () => controller?.clearSearch(),
|
|
118
|
+
/** Programmatically select a conversation by id — mirror of the
|
|
119
|
+
* kai-conversation-select event (a convenience over driving `activeId`). */
|
|
120
|
+
select: (id: string) => dispatch('kai-conversation-select', { id }),
|
|
121
|
+
/** Collapse the rail to its floating reopen button (fires `kai-collapse-toggle`). */
|
|
122
|
+
collapse: () => setCollapsedTo(true),
|
|
123
|
+
/** Expand the rail back to the full list (fires `kai-collapse-toggle`). */
|
|
124
|
+
expand: () => setCollapsedTo(false),
|
|
125
|
+
/** Toggle the rail collapsed/expanded (fires `kai-collapse-toggle`). */
|
|
126
|
+
toggle: () => setCollapsedTo(!collapsed()),
|
|
127
|
+
});
|
|
128
|
+
|
|
70
129
|
return (
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
130
|
+
<Show
|
|
131
|
+
when={!collapsed()}
|
|
132
|
+
fallback={<CollapsedRail onExpand={() => setCollapsedTo(false)} />}
|
|
133
|
+
>
|
|
134
|
+
<ConversationList
|
|
135
|
+
groups={props.groups}
|
|
136
|
+
conversations={allConversations()}
|
|
137
|
+
activeId={props.activeId}
|
|
138
|
+
onSelect={(id) => dispatch('kai-conversation-select', { id })}
|
|
139
|
+
onNewChat={() => dispatch('kai-new-chat')}
|
|
140
|
+
onToggleSidebar={() => { dispatch('kai-toggle-sidebar'); setCollapsedTo(true); }}
|
|
141
|
+
onSearchChange={(query) => dispatch('kai-search', { query })}
|
|
142
|
+
controllerRef={(c) => (controller = c)}
|
|
143
|
+
header={slots().header ? <slot name="header" /> : undefined}
|
|
144
|
+
footer={slots().footer ? <slot name="footer" /> : undefined}
|
|
145
|
+
empty={slots().empty ? <slot name="empty" /> : undefined}
|
|
146
|
+
/>
|
|
147
|
+
</Show>
|
|
79
148
|
);
|
|
80
149
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { For, Show } from 'solid-js';
|
|
2
2
|
import { PromptInput, PromptInputTextarea, PromptInputActions } from '../components/prompt-input';
|
|
3
|
+
import type { TriggerDef, ComposerChange } from '../components/composer';
|
|
4
|
+
import { type ComposerDoc, normalizeValue, serializeToText } from '../primitives/composer-model';
|
|
3
5
|
import { PromptSuggestion } from '../components/prompt-suggestion';
|
|
4
|
-
import { SlashCommand, type SlashCommandItem } from '../components/slash-command';
|
|
5
6
|
import { Button } from '../ui/button';
|
|
6
7
|
import { Tooltip } from '../ui/tooltip';
|
|
7
8
|
import { Paperclip, Globe, Mic, Square } from 'lucide-solid';
|
|
@@ -17,7 +18,8 @@ import { actionIcon } from '../ui/action-icons';
|
|
|
17
18
|
import type { CustomAction } from './chat-types';
|
|
18
19
|
|
|
19
20
|
export interface DefaultPromptInputProps {
|
|
20
|
-
|
|
21
|
+
/** String = controlled text mirror; ComposerDoc = a seed that pre-populates pills. */
|
|
22
|
+
value: string | ComposerDoc;
|
|
21
23
|
placeholder?: string;
|
|
22
24
|
disabled?: boolean;
|
|
23
25
|
loading?: boolean;
|
|
@@ -25,23 +27,26 @@ export interface DefaultPromptInputProps {
|
|
|
25
27
|
/** Attachments staged in the input. Provide `onAttachmentsChange` to enable
|
|
26
28
|
* the attach button + removable previews. */
|
|
27
29
|
attachments?: AttachmentData[];
|
|
30
|
+
/** When `false`, the built-in paperclip attach button is hidden even if
|
|
31
|
+
* `onAttachmentsChange` is provided (e.g. when a `+` menu already covers
|
|
32
|
+
* file-attach). Defaults to `true`. */
|
|
33
|
+
attach?: boolean;
|
|
28
34
|
/** Show a Search (Globe) button in the left toolbar; calls `onSearch`. */
|
|
29
35
|
search?: boolean;
|
|
30
36
|
/** Show a Voice (Mic) button in the left toolbar; calls `onVoice`. */
|
|
31
37
|
voice?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
/** Send-button visibility. `'always'` (default) always shows it; `'auto'` shows
|
|
39
|
+
* it only when there's text/attachments (an empty composer hides it — Enter
|
|
40
|
+
* still submits). To hide it entirely (Enter-only), it's pure CSS:
|
|
41
|
+
* `::part(send){display:none}` — no prop needed. Restyle via `::part(send)`.
|
|
42
|
+
* The Stop button (stoppable + loading) is unaffected. */
|
|
43
|
+
submit?: 'always' | 'auto';
|
|
38
44
|
onValueChange: (v: string) => void;
|
|
39
45
|
onSubmit: () => void;
|
|
40
46
|
onSuggestionClick: (v: string) => void;
|
|
41
47
|
onAttachmentsChange?: (attachments: AttachmentData[]) => void;
|
|
42
48
|
onSearch?: () => void;
|
|
43
49
|
onVoice?: () => void;
|
|
44
|
-
onSlashSelect?: (command: SlashCommandItem) => void;
|
|
45
50
|
/** When `true` and `loading` is also `true`, the send button is replaced by
|
|
46
51
|
* a Stop button that calls `onStop`. */
|
|
47
52
|
stoppable?: boolean;
|
|
@@ -51,6 +56,12 @@ export interface DefaultPromptInputProps {
|
|
|
51
56
|
toolbarActions?: CustomAction[];
|
|
52
57
|
/** Called when a custom toolbar action button is clicked, with the action id. */
|
|
53
58
|
onAction?: (id: string) => void;
|
|
59
|
+
/** Rich entity triggers (`/` skills, `@` agents) passed to the composer. */
|
|
60
|
+
triggers?: TriggerDef[];
|
|
61
|
+
/** Default icon per entity kind (kind → image src) passed to the composer. */
|
|
62
|
+
kindIcons?: Record<string, string>;
|
|
63
|
+
/** Structured change (doc + entities) from the composer, on every edit. */
|
|
64
|
+
onComposerChange?: (change: ComposerChange) => void;
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
function fileToAttachment(file: File): AttachmentData {
|
|
@@ -79,10 +90,20 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
79
90
|
const removeAttachment = (id: string) =>
|
|
80
91
|
props.onAttachmentsChange?.(attachments().filter((a) => a.id !== id));
|
|
81
92
|
|
|
93
|
+
// `value` may be a string or a seeded ComposerDoc — compute emptiness from the
|
|
94
|
+
// flattened text so a pill-only seed still enables Send.
|
|
95
|
+
const valueText = () => serializeToText(normalizeValue(props.value));
|
|
82
96
|
const sendDisabled = () =>
|
|
83
|
-
props.disabled || props.loading || (!
|
|
97
|
+
props.disabled || props.loading || (!valueText().trim() && attachments().length === 0);
|
|
84
98
|
|
|
85
99
|
const showStop = () => !!props.loading && !!props.stoppable;
|
|
100
|
+
const hasContent = () => !!valueText().trim() || attachments().length > 0;
|
|
101
|
+
// Send-button visibility: 'always' (default) or 'auto' (only with content).
|
|
102
|
+
// Full-hide (Enter-only) is CSS: `::part(send){display:none}`.
|
|
103
|
+
const showSend = () => {
|
|
104
|
+
const mode = props.submit ?? 'always';
|
|
105
|
+
return mode === 'always' || (mode === 'auto' && hasContent());
|
|
106
|
+
};
|
|
86
107
|
|
|
87
108
|
return (
|
|
88
109
|
<>
|
|
@@ -103,17 +124,6 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
103
124
|
disabled={props.disabled}
|
|
104
125
|
class="relative"
|
|
105
126
|
>
|
|
106
|
-
<Show when={props.slashCommands?.length}>
|
|
107
|
-
{/* Rendered inside PromptInput so SlashCommand's usePromptInput()
|
|
108
|
-
context (input value + textarea ref) resolves; the `relative` root
|
|
109
|
-
anchors its `absolute bottom-full` palette above the input. */}
|
|
110
|
-
<SlashCommand
|
|
111
|
-
commands={props.slashCommands!}
|
|
112
|
-
activeIds={props.slashActiveIds}
|
|
113
|
-
compact={props.slashCompact}
|
|
114
|
-
onSelect={(command) => props.onSlashSelect?.(command)}
|
|
115
|
-
/>
|
|
116
|
-
</Show>
|
|
117
127
|
<Show when={canAttach() && attachments().length}>
|
|
118
128
|
<div class="px-3 pt-3">
|
|
119
129
|
<Attachments variant="inline">
|
|
@@ -129,13 +139,18 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
129
139
|
</Attachments>
|
|
130
140
|
</div>
|
|
131
141
|
</Show>
|
|
132
|
-
{/* Consumer-injected
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
<
|
|
142
|
+
{/* Consumer-injected content inside the card, above the textarea (e.g. an
|
|
143
|
+
inline status strip). A shadow-internal hole — unreachable from outside.
|
|
144
|
+
Native slot; inert outside a shadow root, projected by the custom element. */}
|
|
145
|
+
<slot name="input-top" />
|
|
146
|
+
<PromptInputTextarea placeholder={props.placeholder} aria-label={props.placeholder || 'Message'} class="min-h-[44px] pt-3 pl-4" triggers={props.triggers} kindIcons={props.kindIcons} onComposerChange={props.onComposerChange} />
|
|
147
|
+
<PromptInputActions class="mt-2 flex w-full items-center justify-between gap-2 px-3 pb-0">
|
|
137
148
|
<div class="flex items-center gap-2">
|
|
138
|
-
|
|
149
|
+
{/* Consumer-injected leading toolbar controls (e.g. a + menu). display:contents
|
|
150
|
+
ensures an empty slot adds no stray gap; projected nodes lay out as toolbar
|
|
151
|
+
items. Native slot; projected by the custom element. */}
|
|
152
|
+
<slot name="toolbar-start" style={{ display: 'contents' }} />
|
|
153
|
+
<Show when={canAttach() && props.attach !== false}>
|
|
139
154
|
<input
|
|
140
155
|
ref={fileInput}
|
|
141
156
|
type="file"
|
|
@@ -209,37 +224,44 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
209
224
|
}}
|
|
210
225
|
</For>
|
|
211
226
|
</div>
|
|
212
|
-
{/*
|
|
213
|
-
the send button.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
227
|
+
{/* Right cluster — consumer trailing controls (model/effort/voice…)
|
|
228
|
+
hug the send button, right-aligned. The `toolbar-end` slot and the send
|
|
229
|
+
button live together so justify-between pins them to the right edge
|
|
230
|
+
(left group stays left). Native slot; projected by the element. */}
|
|
231
|
+
<div class="flex items-center gap-2">
|
|
232
|
+
<slot name="toolbar-end" />
|
|
233
|
+
<Show
|
|
234
|
+
when={showStop()}
|
|
235
|
+
fallback={
|
|
236
|
+
<Show when={showSend()}>
|
|
237
|
+
<Button
|
|
238
|
+
size="icon-sm"
|
|
239
|
+
class="rounded-full"
|
|
240
|
+
part="send"
|
|
241
|
+
data-testid="send"
|
|
242
|
+
aria-label="Send message"
|
|
243
|
+
disabled={sendDisabled()}
|
|
244
|
+
onClick={props.onSubmit}
|
|
245
|
+
>
|
|
246
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
247
|
+
<line x1="12" y1="19" x2="12" y2="5" /><polyline points="5 12 12 5 19 12" />
|
|
248
|
+
</svg>
|
|
249
|
+
</Button>
|
|
250
|
+
</Show>
|
|
251
|
+
}
|
|
252
|
+
>
|
|
218
253
|
<Button
|
|
219
254
|
size="icon-sm"
|
|
255
|
+
variant="outline"
|
|
220
256
|
class="rounded-full"
|
|
221
|
-
data-testid="
|
|
222
|
-
aria-label="
|
|
223
|
-
|
|
224
|
-
onClick={props.onSubmit}
|
|
257
|
+
data-testid="stop"
|
|
258
|
+
aria-label="Stop"
|
|
259
|
+
onClick={props.onStop}
|
|
225
260
|
>
|
|
226
|
-
<
|
|
227
|
-
<line x1="12" y1="19" x2="12" y2="5" /><polyline points="5 12 12 5 19 12" />
|
|
228
|
-
</svg>
|
|
261
|
+
<Square class="size-3" />
|
|
229
262
|
</Button>
|
|
230
|
-
|
|
231
|
-
>
|
|
232
|
-
<Button
|
|
233
|
-
size="icon-sm"
|
|
234
|
-
variant="outline"
|
|
235
|
-
class="rounded-full"
|
|
236
|
-
data-testid="stop"
|
|
237
|
-
aria-label="Stop"
|
|
238
|
-
onClick={props.onStop}
|
|
239
|
-
>
|
|
240
|
-
<Square class="size-3" />
|
|
241
|
-
</Button>
|
|
242
|
-
</Show>
|
|
263
|
+
</Show>
|
|
264
|
+
</div>
|
|
243
265
|
</PromptInputActions>
|
|
244
266
|
</PromptInput>
|
|
245
267
|
</>
|
package/src/elements/define.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { customElement } from 'solid-element';
|
|
2
2
|
import { ChatConfig } from '../primitives/chat-config';
|
|
3
3
|
import { ELEMENT_CSS } from './css';
|
|
4
|
-
import { createSignal, onCleanup,
|
|
4
|
+
import { createSignal, onCleanup, Show, type JSX } from 'solid-js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Shared constructable stylesheet, built once and adopted by every element's
|
|
@@ -67,6 +67,17 @@ export interface WebComponentContext<E = Record<string, unknown>> {
|
|
|
67
67
|
* `name` is the camelCase prop name; the matching kebab attribute is derived.
|
|
68
68
|
*/
|
|
69
69
|
flag: (name: string) => boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Expose imperative methods on the host element instance — the input half of a
|
|
72
|
+
* component's interaction surface (`el.focus()`, `el.clear()`, `el.scrollToBottom()`,
|
|
73
|
+
* …), the counterpart to the events `dispatch` fires. Call once from the facade
|
|
74
|
+
* with closures over its internal state/refs; each entry is assigned to the host,
|
|
75
|
+
* so a consumer calls it directly: `document.querySelector('kai-prompt-input').focus()`.
|
|
76
|
+
* Overriding a native method name (e.g. `focus`) shadows it on the instance so it
|
|
77
|
+
* can target the right control inside the shadow root (the WebAwesome/Shoelace
|
|
78
|
+
* convention). Methods are attached when the facade renders (on element upgrade).
|
|
79
|
+
*/
|
|
80
|
+
expose: (methods: Record<string, (...args: never[]) => unknown>) => void;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
/** camelCase prop name → kebab-case attribute (`hoverCard` → `hover-card`). */
|
|
@@ -77,6 +88,8 @@ function toAttr(name: string): string {
|
|
|
77
88
|
/** Underlying flag resolution; see `WebComponentContext.flag`. */
|
|
78
89
|
function resolveFlag(element: HTMLElement, value: unknown, attribute: string): boolean {
|
|
79
90
|
if (value === true) return true;
|
|
91
|
+
// An explicit JS false wins over a present attribute (see WebComponentContext.flag); this also prevents a feedback loop when an effect writes the attribute it reads.
|
|
92
|
+
if (value === false) return false;
|
|
80
93
|
return element.hasAttribute(attribute) && element.getAttribute(attribute) !== 'false';
|
|
81
94
|
}
|
|
82
95
|
|
|
@@ -144,17 +157,45 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
144
157
|
const flag = (name: string) =>
|
|
145
158
|
resolveFlag(element, (props as Record<string, unknown>)[name], toAttr(name));
|
|
146
159
|
|
|
160
|
+
// Attach imperative methods onto the host instance. See WebComponentContext.expose.
|
|
161
|
+
// Uses defineProperty so a method can shadow an inherited getter-only accessor
|
|
162
|
+
// (e.g. `focus`/`blur`) which a plain assignment would throw on. A method name
|
|
163
|
+
// must NOT collide with a declared prop (the prop's accessor would be clobbered)
|
|
164
|
+
// — pick a distinct verb (e.g. `send()` next to a `submit` prop). Failures warn
|
|
165
|
+
// and skip rather than breaking the element's render.
|
|
166
|
+
const expose: WebComponentContext<E>['expose'] = (methods) => {
|
|
167
|
+
for (const [name, fn] of Object.entries(methods)) {
|
|
168
|
+
try {
|
|
169
|
+
Object.defineProperty(element, name, { value: fn, writable: true, configurable: true });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.warn(`defineWebComponent(${tag}): could not expose method "${name}"`, err);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
147
176
|
const isDark = createDarkMode(() => props.theme as string | undefined);
|
|
148
177
|
|
|
149
178
|
// Prefer a single shared stylesheet adopted into this shadow root; only emit
|
|
150
179
|
// an inline <style> when Constructable Stylesheets aren't supported.
|
|
180
|
+
//
|
|
181
|
+
// Adopt SYNCHRONOUSLY, before solid-element inserts the rendered content into
|
|
182
|
+
// the shadow root. component-register attaches the shadow root lazily on first
|
|
183
|
+
// `renderRoot` access — which happens during that insert, AFTER this facade
|
|
184
|
+
// function returns. So we pre-attach the shadow root here (with the same
|
|
185
|
+
// `mode: 'open'` the renderRoot getter would use, making it a no-op for
|
|
186
|
+
// solid-element) and adopt the sheet now. If we deferred to onMount instead,
|
|
187
|
+
// the content would paint UNSTYLED and then animate from that bare state to
|
|
188
|
+
// its resting style once the sheet landed — every `transition-colors` button
|
|
189
|
+
// and menu/dropdown trigger flashing as if hovered-then-un-hovered on first
|
|
190
|
+
// render. Adopting first means the very first computed style of every node is
|
|
191
|
+
// already the resting style, so no transition can fire on load.
|
|
151
192
|
const sheet = getSharedSheet();
|
|
152
|
-
|
|
153
|
-
const root = element.shadowRoot;
|
|
154
|
-
if (
|
|
193
|
+
if (sheet) {
|
|
194
|
+
const root = element.shadowRoot ?? element.attachShadow({ mode: 'open' });
|
|
195
|
+
if ('adoptedStyleSheets' in root && !root.adoptedStyleSheets.includes(sheet)) {
|
|
155
196
|
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
|
|
156
197
|
}
|
|
157
|
-
}
|
|
198
|
+
}
|
|
158
199
|
|
|
159
200
|
return (
|
|
160
201
|
<>
|
|
@@ -168,7 +209,7 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
168
209
|
<div classList={{ dark: isDark() }} style={{ display: 'contents', color: 'var(--color-foreground)' }}>
|
|
169
210
|
<div ref={portalNode} />
|
|
170
211
|
<ChatConfig portalMount={portalNode}>
|
|
171
|
-
{Facade(props as unknown as P, { element, dispatch, flag })}
|
|
212
|
+
{Facade(props as unknown as P, { element, dispatch, flag, expose })}
|
|
172
213
|
</ChatConfig>
|
|
173
214
|
</div>
|
|
174
215
|
</>
|