@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
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Show,
|
|
11
11
|
} from 'solid-js';
|
|
12
12
|
import { cn } from '../utils/cn';
|
|
13
|
+
import { Skeleton } from '../ui/skeleton';
|
|
13
14
|
import { Link as LinkIcon } from 'lucide-solid';
|
|
14
15
|
import type { CardEvent } from '../primitives/card-contract';
|
|
15
16
|
import {
|
|
@@ -153,11 +154,11 @@ export function LinkPreview(props: LinkPreviewProps): JSX.Element {
|
|
|
153
154
|
>
|
|
154
155
|
<Show when={loading()}>
|
|
155
156
|
{/* Skeleton while the bare-URL fetcher resolves. */}
|
|
156
|
-
<
|
|
157
|
+
<Skeleton class="aspect-[16/9] w-full rounded-none" />
|
|
157
158
|
<div class="space-y-2 p-3">
|
|
158
|
-
<
|
|
159
|
-
<
|
|
160
|
-
<
|
|
159
|
+
<Skeleton class="h-3 w-1/3" />
|
|
160
|
+
<Skeleton class="h-4 w-2/3" />
|
|
161
|
+
<Skeleton class="h-3 w-full" />
|
|
161
162
|
</div>
|
|
162
163
|
</Show>
|
|
163
164
|
<Show when={!loading()}>
|
|
@@ -11,6 +11,9 @@ export interface MarkdownProps {
|
|
|
11
11
|
id?: string;
|
|
12
12
|
class?: string;
|
|
13
13
|
codeTheme?: string;
|
|
14
|
+
/** Optional `::part` name(s) to expose on the rendered root. Lets callers
|
|
15
|
+
* (e.g. the message bubble) surface a styleable part through the shadow. */
|
|
16
|
+
part?: string;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
interface ParsedBlock {
|
|
@@ -49,13 +52,13 @@ function MarkdownBlock(props: { content: string }) {
|
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
function Markdown(props: MarkdownProps) {
|
|
52
|
-
const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme']);
|
|
55
|
+
const [local] = splitProps(props, ['content', 'id', 'class', 'codeTheme', 'part']);
|
|
53
56
|
const config = useChatConfig();
|
|
54
57
|
const blockId = () => local.id ?? createUniqueId();
|
|
55
58
|
const blocks = createMemo(() => parseMarkdownIntoBlocks(local.content));
|
|
56
59
|
|
|
57
60
|
return (
|
|
58
|
-
<div class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
|
|
61
|
+
<div part={local.part} class={cn('chat-markdown max-w-none break-words whitespace-normal [&>div:first-child>p:first-child]:mt-0 [&>div:last-child>p:last-child]:mb-0', textClass(config.proseSize()), local.class)}>
|
|
59
62
|
<For each={blocks()}>
|
|
60
63
|
{(block) => (
|
|
61
64
|
<Switch>
|
|
@@ -5,8 +5,11 @@ import { cn } from "../utils/cn";
|
|
|
5
5
|
import { Markdown } from "./markdown";
|
|
6
6
|
import { Button } from "../ui/button";
|
|
7
7
|
import { actionIcon, BUILTIN_ACTION_LABEL } from "../ui/action-icons";
|
|
8
|
-
import type { ChatMessageAction, CustomAction } from "../elements/chat-types";
|
|
8
|
+
import type { ChatMessageAction, CustomAction, FeedbackVote } from "../elements/chat-types";
|
|
9
9
|
import { useChatConfig, textClass } from "../primitives/chat-config";
|
|
10
|
+
import { Reasoning, ReasoningTrigger, ReasoningContent } from "./reasoning";
|
|
11
|
+
import { Tool, type ToolPart } from "./tool";
|
|
12
|
+
import { Attachments, Attachment, AttachmentPreview, AttachmentInfo, type AttachmentData } from "./attachments";
|
|
10
13
|
|
|
11
14
|
// --- Message ---
|
|
12
15
|
|
|
@@ -17,7 +20,7 @@ export interface MessageProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
17
20
|
function Message(props: MessageProps) {
|
|
18
21
|
const [local, rest] = splitProps(props, ["children", "class"]);
|
|
19
22
|
return (
|
|
20
|
-
<div class={cn("flex items-start gap-3", local.class)} {...rest}>
|
|
23
|
+
<div part="row" class={cn("flex items-start gap-3", local.class)} {...rest}>
|
|
21
24
|
{local.children}
|
|
22
25
|
</div>
|
|
23
26
|
);
|
|
@@ -35,6 +38,7 @@ export interface MessageAvatarProps {
|
|
|
35
38
|
function MessageAvatar(props: MessageAvatarProps) {
|
|
36
39
|
return (
|
|
37
40
|
<div
|
|
41
|
+
part="avatar"
|
|
38
42
|
class={cn("h-8 w-8 shrink-0 overflow-hidden rounded-full", props.class)}
|
|
39
43
|
>
|
|
40
44
|
<Show
|
|
@@ -62,10 +66,14 @@ function MessageAvatar(props: MessageAvatarProps) {
|
|
|
62
66
|
export interface MessageContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
63
67
|
children: JSX.Element | string;
|
|
64
68
|
markdown?: boolean;
|
|
69
|
+
/** `::part` name(s) exposed on the content node. The body passes
|
|
70
|
+
* `"bubble content"` so consumers can target either the rounded bubble or the
|
|
71
|
+
* text region from outside the shadow boundary. */
|
|
72
|
+
part?: string;
|
|
65
73
|
}
|
|
66
74
|
|
|
67
75
|
function MessageContent(props: MessageContentProps) {
|
|
68
|
-
const [local, rest] = splitProps(props, ["children", "markdown", "class"]);
|
|
76
|
+
const [local, rest] = splitProps(props, ["children", "markdown", "class", "part"]);
|
|
69
77
|
const config = useChatConfig();
|
|
70
78
|
const classNames = () =>
|
|
71
79
|
cn(
|
|
@@ -78,12 +86,12 @@ function MessageContent(props: MessageContentProps) {
|
|
|
78
86
|
<Show
|
|
79
87
|
when={local.markdown}
|
|
80
88
|
fallback={
|
|
81
|
-
<div class={classNames()} {...rest}>
|
|
89
|
+
<div part={local.part} class={classNames()} {...rest}>
|
|
82
90
|
{local.children}
|
|
83
91
|
</div>
|
|
84
92
|
}
|
|
85
93
|
>
|
|
86
|
-
<Markdown content={local.children as string} class={classNames()} />
|
|
94
|
+
<Markdown part={local.part} content={local.children as string} class={classNames()} />
|
|
87
95
|
</Show>
|
|
88
96
|
);
|
|
89
97
|
}
|
|
@@ -119,6 +127,13 @@ export interface MessageActionBarProps {
|
|
|
119
127
|
reveal?: 'always' | 'hover';
|
|
120
128
|
/** Fired with the built-in name or the custom action id when a button is clicked. */
|
|
121
129
|
onAction: (id: string) => void;
|
|
130
|
+
/** The active feedback vote. When `'like'`/`'dislike'` is set, that button is
|
|
131
|
+
* marked pressed (filled) and the OTHER vote button animates out. `undefined`
|
|
132
|
+
* shows both. Pure/prop-driven so the bar survives re-renders. */
|
|
133
|
+
activeFeedback?: FeedbackVote;
|
|
134
|
+
/** When true, the `copy` button shows its emerald check icon instead of the
|
|
135
|
+
* copy glyph (cleared by the owner after ~2s). */
|
|
136
|
+
copied?: boolean;
|
|
122
137
|
class?: string;
|
|
123
138
|
}
|
|
124
139
|
|
|
@@ -130,16 +145,30 @@ function normalizeAction(a: ChatMessageAction | CustomAction) {
|
|
|
130
145
|
return { id: a.id, label: a.label, Icon: actionIcon(a.icon) };
|
|
131
146
|
}
|
|
132
147
|
|
|
148
|
+
/** Is this entry one of the two feedback votes? */
|
|
149
|
+
function feedbackVoteOf(a: ChatMessageAction | CustomAction): FeedbackVote | undefined {
|
|
150
|
+
return a === 'like' || a === 'dislike' ? a : undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
133
153
|
/**
|
|
134
154
|
* The shared message action toolbar. Renders one ghost icon button per entry —
|
|
135
155
|
* built-in names pull their label+icon from the curated registry; custom
|
|
136
156
|
* descriptors use their `label` plus `actionIcon(icon)` (label-only when the
|
|
137
157
|
* icon is unknown or absent). `reveal="hover"` makes the bar fade in on the
|
|
138
158
|
* parent `.group`'s hover.
|
|
159
|
+
*
|
|
160
|
+
* Pure/prop-driven: feedback (`activeFeedback`) and copy (`copied`) state are
|
|
161
|
+
* owned by the parent facade and passed in — the bar holds no internal signals,
|
|
162
|
+
* so it survives the new-array-per-chunk re-renders of a streaming thread. With
|
|
163
|
+
* a vote active, the chosen `like`/`dislike` button is marked `aria-pressed` +
|
|
164
|
+
* filled and the other vote button collapses its width (sliding the active thumb
|
|
165
|
+
* into its place) via a 0fr↔1fr grid transition. The
|
|
166
|
+
* `copy` button swaps to an emerald check while `copied`.
|
|
139
167
|
*/
|
|
140
168
|
function MessageActionBar(props: MessageActionBarProps) {
|
|
141
169
|
return (
|
|
142
170
|
<MessageActions
|
|
171
|
+
part="actions"
|
|
143
172
|
class={cn(
|
|
144
173
|
'mt-1 flex gap-0',
|
|
145
174
|
props.reveal === 'hover' && 'opacity-0 transition-opacity group-hover:opacity-100',
|
|
@@ -150,38 +179,169 @@ function MessageActionBar(props: MessageActionBarProps) {
|
|
|
150
179
|
{(a) => {
|
|
151
180
|
const item = normalizeAction(a);
|
|
152
181
|
const tooltipText = () => (typeof a !== 'string' && a.tooltip) ? a.tooltip : item.label;
|
|
182
|
+
const vote = feedbackVoteOf(a);
|
|
183
|
+
// A vote button is the active one when it matches the resolved vote.
|
|
184
|
+
const isActiveVote = () => vote !== undefined && props.activeFeedback === vote;
|
|
185
|
+
// The COPY button reflects the copied check.
|
|
186
|
+
const isCopy = item.id === 'copy';
|
|
187
|
+
const showCheck = () => isCopy && props.copied === true;
|
|
188
|
+
|
|
153
189
|
// Factory (not a shared node): each Show branch gets its own Button so
|
|
154
190
|
// the eager DOM node is never referenced from two places.
|
|
155
191
|
const button = () => (
|
|
156
192
|
<Button
|
|
157
193
|
variant="ghost"
|
|
158
194
|
size="icon-sm"
|
|
159
|
-
class=
|
|
195
|
+
class={cn('rounded-full', isActiveVote() && 'text-primary')}
|
|
160
196
|
data-action={item.id}
|
|
161
|
-
aria-label={item.label}
|
|
197
|
+
aria-label={showCheck() ? 'Copied' : item.label}
|
|
198
|
+
aria-pressed={vote !== undefined ? isActiveVote() : undefined}
|
|
162
199
|
onClick={() => props.onAction(item.id)}
|
|
163
200
|
>
|
|
164
|
-
<Show
|
|
165
|
-
{(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
201
|
+
<Show
|
|
202
|
+
when={showCheck()}
|
|
203
|
+
fallback={
|
|
204
|
+
<Show when={item.Icon} fallback={<span class="px-1 text-xs">{item.label}</span>}>
|
|
205
|
+
{(Icon) => {
|
|
206
|
+
const I = Icon();
|
|
207
|
+
return <I class={cn('size-3.5', isActiveVote() && 'fill-current')} />;
|
|
208
|
+
}}
|
|
209
|
+
</Show>
|
|
210
|
+
}
|
|
211
|
+
>
|
|
212
|
+
<Check class="size-3.5 text-emerald-400" />
|
|
169
213
|
</Show>
|
|
170
214
|
</Button>
|
|
171
215
|
);
|
|
172
216
|
// Icon-only buttons get a tooltip; label-only buttons (text already
|
|
173
217
|
// visible) don't.
|
|
174
|
-
|
|
218
|
+
const rendered = () => (
|
|
175
219
|
<Show when={item.Icon} fallback={button()}>
|
|
176
220
|
<Tooltip content={tooltipText()}>{button()}</Tooltip>
|
|
177
221
|
</Show>
|
|
178
222
|
);
|
|
223
|
+
|
|
224
|
+
// When the OTHER vote is active, this vote button collapses — its WIDTH
|
|
225
|
+
// animates to zero via a 0fr↔1fr grid so the remaining thumb slides into
|
|
226
|
+
// its place, while it fades out; it slides + fades back on un-vote. (Kept
|
|
227
|
+
// mounted-but-collapsed, not unmounted, so the sibling can slide.) No
|
|
228
|
+
// vote active → both shown; non-vote entries always render.
|
|
229
|
+
return (
|
|
230
|
+
<Show when={vote !== undefined} fallback={rendered()}>
|
|
231
|
+
{(() => {
|
|
232
|
+
const show = () => props.activeFeedback === undefined || props.activeFeedback === vote;
|
|
233
|
+
return (
|
|
234
|
+
<span
|
|
235
|
+
data-feedback-collapsed={show() ? undefined : ''}
|
|
236
|
+
class={cn(
|
|
237
|
+
'grid transition-[grid-template-columns,opacity] duration-300 ease-out',
|
|
238
|
+
show() ? 'grid-cols-[1fr] opacity-100' : 'grid-cols-[0fr] opacity-0',
|
|
239
|
+
)}
|
|
240
|
+
>
|
|
241
|
+
<span class="min-w-0 overflow-hidden">{rendered()}</span>
|
|
242
|
+
</span>
|
|
243
|
+
);
|
|
244
|
+
})()}
|
|
245
|
+
</Show>
|
|
246
|
+
);
|
|
179
247
|
}}
|
|
180
248
|
</For>
|
|
181
249
|
</MessageActions>
|
|
182
250
|
);
|
|
183
251
|
}
|
|
184
252
|
|
|
253
|
+
// --- MessageBody ---
|
|
254
|
+
|
|
255
|
+
export interface MessageBodyProps {
|
|
256
|
+
/** The message text/markdown. */
|
|
257
|
+
content: string;
|
|
258
|
+
/** Optional collapsible reasoning block, rendered above the content. */
|
|
259
|
+
reasoning?: { text: string; label?: string };
|
|
260
|
+
/** Tool-call parts, rendered above the content. */
|
|
261
|
+
tools?: ToolPart[];
|
|
262
|
+
/** Inline attachment previews, rendered above the content. */
|
|
263
|
+
attachments?: AttachmentData[];
|
|
264
|
+
/** Whether this is a user message (right-aligned bubble) vs an assistant
|
|
265
|
+
* message (full-width transparent). */
|
|
266
|
+
isUser: boolean;
|
|
267
|
+
/** Whether the content renders as markdown. */
|
|
268
|
+
markdown: boolean;
|
|
269
|
+
/** Action-bar entries — built-in names and/or custom descriptors. When empty
|
|
270
|
+
* the bar is not rendered. */
|
|
271
|
+
actions?: (ChatMessageAction | CustomAction)[];
|
|
272
|
+
/** `'always'` (default) keeps the bar visible; `'hover'` reveals it on parent
|
|
273
|
+
* `.group` hover. */
|
|
274
|
+
actionsReveal?: 'always' | 'hover';
|
|
275
|
+
/** Fired with the built-in name or custom id when an action is clicked. */
|
|
276
|
+
onAction?: (id: string) => void;
|
|
277
|
+
/** The currently-active feedback vote, so the bar can mark like/dislike and
|
|
278
|
+
* hide the other. */
|
|
279
|
+
activeFeedback?: FeedbackVote;
|
|
280
|
+
/** When true, the copy button shows its "copied" check icon. */
|
|
281
|
+
copied?: boolean;
|
|
282
|
+
/** INJECT slot projected at the TOP of the body — above the reasoning / tools /
|
|
283
|
+
* content. A per-message header: a model-name label, a role + timestamp line.
|
|
284
|
+
* In the `<kai-message>` shadow this is `<slot name="before-body" />`. */
|
|
285
|
+
beforeBody?: JSX.Element;
|
|
286
|
+
/** INJECT slot projected at the BOTTOM of the body — below the action bar. A
|
|
287
|
+
* citation / sources row, a token-cost / latency line. In the `<kai-message>`
|
|
288
|
+
* shadow this is `<slot name="after-body" />`. */
|
|
289
|
+
afterBody?: JSX.Element;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The shared message body: an optional reasoning block, tool calls, inline
|
|
294
|
+
* attachments, the content bubble, and the action bar — in that order. This is
|
|
295
|
+
* the single source of truth for how a message renders, consumed by `ChatThread`
|
|
296
|
+
* (the `<For>` over `messages`), the standalone `<kai-message>` facade, and (in
|
|
297
|
+
* future) `kai-compare` for each candidate. Pure/prop-driven: all interaction
|
|
298
|
+
* state (copied, feedback vote) is owned above and passed in.
|
|
299
|
+
*/
|
|
300
|
+
function MessageBody(props: MessageBodyProps) {
|
|
301
|
+
return (
|
|
302
|
+
<>
|
|
303
|
+
{/* before-body (inject): a per-message header above everything else. */}
|
|
304
|
+
<Show when={props.beforeBody}>{props.beforeBody}</Show>
|
|
305
|
+
<Show when={props.reasoning}>
|
|
306
|
+
{(r) => (
|
|
307
|
+
<Reasoning class="mb-2 w-full">
|
|
308
|
+
<ReasoningTrigger>{r().label ?? 'Reasoning'}</ReasoningTrigger>
|
|
309
|
+
<ReasoningContent markdown>{r().text}</ReasoningContent>
|
|
310
|
+
</Reasoning>
|
|
311
|
+
)}
|
|
312
|
+
</Show>
|
|
313
|
+
<For each={props.tools ?? []}>{(tp) => <Tool toolPart={tp} class="mb-2 w-full" />}</For>
|
|
314
|
+
<Show when={props.attachments?.length}>
|
|
315
|
+
<Attachments variant="inline" class={props.isUser ? 'mb-2 justify-end' : 'mb-2'}>
|
|
316
|
+
<For each={props.attachments!}>
|
|
317
|
+
{(att) => (<Attachment data={att}><AttachmentPreview /><AttachmentInfo /></Attachment>)}
|
|
318
|
+
</For>
|
|
319
|
+
</Attachments>
|
|
320
|
+
</Show>
|
|
321
|
+
<MessageContent
|
|
322
|
+
part="bubble content"
|
|
323
|
+
markdown={props.markdown}
|
|
324
|
+
class={props.isUser
|
|
325
|
+
? 'bg-muted text-primary max-w-[85%] rounded-2xl px-4 py-2'
|
|
326
|
+
: 'bg-transparent p-0'}
|
|
327
|
+
>
|
|
328
|
+
{props.content}
|
|
329
|
+
</MessageContent>
|
|
330
|
+
<Show when={(props.actions?.length ?? 0) > 0}>
|
|
331
|
+
<MessageActionBar
|
|
332
|
+
actions={props.actions!}
|
|
333
|
+
reveal={props.actionsReveal === 'hover' ? 'hover' : 'always'}
|
|
334
|
+
activeFeedback={props.activeFeedback}
|
|
335
|
+
copied={props.copied}
|
|
336
|
+
onAction={(id) => props.onAction?.(id)}
|
|
337
|
+
/>
|
|
338
|
+
</Show>
|
|
339
|
+
{/* after-body (inject): a citation row / cost line below the action bar. */}
|
|
340
|
+
<Show when={props.afterBody}>{props.afterBody}</Show>
|
|
341
|
+
</>
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
185
345
|
// --- MessageAction ---
|
|
186
346
|
|
|
187
347
|
export interface MessageActionProps {
|
|
@@ -224,4 +384,4 @@ function MessageCopyButton(props: MessageCopyButtonProps) {
|
|
|
224
384
|
);
|
|
225
385
|
}
|
|
226
386
|
|
|
227
|
-
export { Message, MessageAvatar, MessageContent, MessageActions, MessageActionBar, MessageAction, MessageCopyButton };
|
|
387
|
+
export { Message, MessageAvatar, MessageContent, MessageActions, MessageActionBar, MessageBody, MessageAction, MessageCopyButton };
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { splitProps, For, Show, createSignal } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
|
-
import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '../ui/dropdown';
|
|
3
|
+
import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem, type DropdownController } from '../ui/dropdown';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import type { ModelOption } from '../types';
|
|
6
6
|
|
|
7
|
-
export interface ModelSwitcherProps {
|
|
7
|
+
export interface ModelSwitcherProps {
|
|
8
|
+
models: ModelOption[];
|
|
9
|
+
currentModelId: string;
|
|
10
|
+
onModelChange: (modelId: string) => void;
|
|
11
|
+
class?: string;
|
|
12
|
+
/** Initial open state of the dropdown (uncontrolled seed). */
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
/** Disable the trigger — click/keyboard no longer open the dropdown. */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** Receive the dropdown's open controller (forwarded from the inner Dropdown). */
|
|
17
|
+
controllerRef?: (api: DropdownController) => void;
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
const Chevron = (props: { class?: string }) => (
|
|
10
21
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class={props.class}>
|
|
@@ -25,7 +36,7 @@ function ModelRow(props: { model: ModelOption; currentModelId: string; onModelCh
|
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
export function ModelSwitcher(props: ModelSwitcherProps) {
|
|
28
|
-
const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class']);
|
|
39
|
+
const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class', 'defaultOpen', 'disabled', 'controllerRef']);
|
|
29
40
|
const currentModel = () => local.models.find((m) => m.id === local.currentModelId);
|
|
30
41
|
|
|
31
42
|
// Ungrouped models list first; grouped models collect under their group name,
|
|
@@ -44,7 +55,11 @@ export function ModelSwitcher(props: ModelSwitcherProps) {
|
|
|
44
55
|
|
|
45
56
|
return (
|
|
46
57
|
<Show when={local.models.length > 1}>
|
|
47
|
-
<Dropdown
|
|
58
|
+
<Dropdown
|
|
59
|
+
defaultOpen={local.defaultOpen}
|
|
60
|
+
disabled={local.disabled}
|
|
61
|
+
controllerRef={local.controllerRef}
|
|
62
|
+
>
|
|
48
63
|
<DropdownTrigger as={(triggerProps: any) => (
|
|
49
64
|
<Button variant="ghost" size="sm" class={cn('gap-1 text-meta text-muted-foreground', local.class)} {...triggerProps}>
|
|
50
65
|
{currentModel()?.name ?? local.currentModelId}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { type JSX, splitProps, createSignal, createContext, useContext
|
|
1
|
+
import { type JSX, splitProps, createSignal, createContext, useContext } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { useChatConfig, textClass } from '../primitives/chat-config';
|
|
4
|
+
import { Composer, type TriggerDef, type ComposerChange } from './composer';
|
|
5
|
+
import type { ComposerDoc } from '../primitives/composer-model';
|
|
4
6
|
|
|
5
7
|
// --- Context ---
|
|
6
8
|
|
|
7
9
|
interface PromptInputContextType {
|
|
8
10
|
isLoading: boolean;
|
|
9
|
-
|
|
11
|
+
// A string (controlled text) or a ComposerDoc (a seed that pre-populates pills).
|
|
12
|
+
value: () => string | ComposerDoc;
|
|
10
13
|
setValue: (value: string) => void;
|
|
11
14
|
maxHeight: number | string;
|
|
12
15
|
onSubmit?: () => void;
|
|
13
16
|
disabled?: boolean;
|
|
14
|
-
textareaRef:
|
|
15
|
-
setTextareaRef: (el:
|
|
17
|
+
textareaRef: HTMLElement | undefined;
|
|
18
|
+
setTextareaRef: (el: HTMLElement) => void;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
const PromptInputContext = createContext<PromptInputContextType>();
|
|
@@ -27,7 +30,8 @@ function usePromptInput() {
|
|
|
27
30
|
|
|
28
31
|
export interface PromptInputProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
29
32
|
isLoading?: boolean;
|
|
30
|
-
|
|
33
|
+
/** String = controlled text; ComposerDoc = a seed that pre-populates pills. */
|
|
34
|
+
value?: string | ComposerDoc;
|
|
31
35
|
onValueChange?: (value: string) => void;
|
|
32
36
|
maxHeight?: number | string;
|
|
33
37
|
onSubmit?: () => void;
|
|
@@ -41,8 +45,8 @@ function PromptInput(props: PromptInputProps) {
|
|
|
41
45
|
'children', 'disabled', 'class', 'onClick',
|
|
42
46
|
]);
|
|
43
47
|
|
|
44
|
-
const [internalValue, setInternalValue] = createSignal(local.value ?? '');
|
|
45
|
-
let textareaRef:
|
|
48
|
+
const [internalValue, setInternalValue] = createSignal<string | ComposerDoc>(local.value ?? '');
|
|
49
|
+
let textareaRef: HTMLElement | undefined;
|
|
46
50
|
|
|
47
51
|
const handleChange = (newValue: string) => {
|
|
48
52
|
setInternalValue(newValue);
|
|
@@ -70,13 +74,14 @@ function PromptInput(props: PromptInputProps) {
|
|
|
70
74
|
}}
|
|
71
75
|
>
|
|
72
76
|
<div
|
|
77
|
+
data-prompt-input
|
|
73
78
|
onClick={handleClick}
|
|
74
79
|
class={cn(
|
|
75
80
|
// The inner textarea neutralizes its own ring (focus-visible:ring-0),
|
|
76
81
|
// so the FRAME owns the focus affordance: a blue ring whenever a
|
|
77
82
|
// control inside it is focused. Without this the composer had no
|
|
78
83
|
// visible keyboard-focus state.
|
|
79
|
-
'bg-
|
|
84
|
+
'bg-surface cursor-text rounded-xl p-2 shadow-xs',
|
|
80
85
|
'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-0',
|
|
81
86
|
local.disabled && 'cursor-not-allowed opacity-60',
|
|
82
87
|
local.class
|
|
@@ -93,83 +98,49 @@ function PromptInput(props: PromptInputProps) {
|
|
|
93
98
|
|
|
94
99
|
export interface PromptInputTextareaProps extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
95
100
|
disableAutosize?: boolean;
|
|
101
|
+
/** Rich entity triggers (`/`, `@`) forwarded to the composer. */
|
|
102
|
+
triggers?: TriggerDef[];
|
|
103
|
+
/** Default icon per entity kind (kind → image src), forwarded to the composer. */
|
|
104
|
+
kindIcons?: Record<string, string>;
|
|
105
|
+
/** Structured change (doc + entities) from the composer, on every edit. */
|
|
106
|
+
onComposerChange?: (change: ComposerChange) => void;
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
function PromptInputTextarea(props: PromptInputTextareaProps) {
|
|
99
|
-
const [local
|
|
110
|
+
const [local] = splitProps(props, ['class', 'placeholder', 'aria-label', 'triggers', 'kindIcons', 'onComposerChange']);
|
|
100
111
|
const ctx = usePromptInput();
|
|
101
112
|
const config = useChatConfig();
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
function handleRef(el: HTMLTextAreaElement) {
|
|
115
|
-
ctx.setTextareaRef(el);
|
|
116
|
-
adjustHeight(el);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
createEffect(on(
|
|
120
|
-
() => [ctx.value(), ctx.maxHeight, local.disableAutosize],
|
|
121
|
-
() => {
|
|
122
|
-
if (ctx.textareaRef && !local.disableAutosize) {
|
|
123
|
-
adjustHeight(ctx.textareaRef);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
));
|
|
127
|
-
|
|
128
|
-
function handleInput(e: InputEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
129
|
-
const el = e.currentTarget;
|
|
130
|
-
let value = el.value;
|
|
131
|
-
// Disallow leading whitespace — a prompt can't start with a space or blank
|
|
132
|
-
// line. Strip it (covers typing a space at the start AND pasting) and keep
|
|
133
|
-
// the caret in the right place.
|
|
134
|
-
if (/^\s/.test(value)) {
|
|
135
|
-
const stripped = value.replace(/^\s+/, '');
|
|
136
|
-
const removed = value.length - stripped.length;
|
|
137
|
-
const caret = Math.max(0, (el.selectionStart ?? 0) - removed);
|
|
138
|
-
el.value = stripped;
|
|
139
|
-
el.setSelectionRange(caret, caret);
|
|
140
|
-
value = stripped;
|
|
141
|
-
}
|
|
142
|
-
adjustHeight(el);
|
|
143
|
-
ctx.setValue(value);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function handleKeyDown(e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
147
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
148
|
-
e.preventDefault();
|
|
149
|
-
// A disabled composer is non-interactive: Enter must not submit.
|
|
150
|
-
if (!ctx.disabled) ctx.onSubmit?.();
|
|
151
|
-
}
|
|
152
|
-
if (typeof local.onKeyDown === 'function') {
|
|
153
|
-
(local.onKeyDown as (e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) => void)(e);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
114
|
+
// The editable mirrors the original <textarea>'s classes EXACTLY so swapping in
|
|
115
|
+
// the contenteditable composer is visually identical. The frame (PromptInput
|
|
116
|
+
// root) still owns radius/bg/padding/focus-ring; auto-grow is native to a
|
|
117
|
+
// contenteditable block (capped via max-height on the editable).
|
|
118
|
+
const editableClass = () =>
|
|
119
|
+
cn(
|
|
120
|
+
'text-foreground min-h-[44px] w-full bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0 overflow-y-auto whitespace-pre-wrap break-words',
|
|
121
|
+
textClass(config.proseSize()),
|
|
122
|
+
local.class,
|
|
123
|
+
);
|
|
156
124
|
|
|
157
125
|
return (
|
|
158
|
-
<
|
|
159
|
-
|
|
126
|
+
<Composer
|
|
127
|
+
bare
|
|
160
128
|
value={ctx.value()}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
class={cn(
|
|
164
|
-
// Typed text uses the normal foreground (light/dark), NOT the brand/accent
|
|
165
|
-
// color — the composer should read as plain text, not tinted.
|
|
166
|
-
'text-foreground min-h-[44px] w-full resize-none border-none bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
|
|
167
|
-
textClass(config.proseSize()),
|
|
168
|
-
local.class
|
|
169
|
-
)}
|
|
170
|
-
rows={1}
|
|
129
|
+
placeholder={local.placeholder as string | undefined}
|
|
130
|
+
ariaLabel={local['aria-label'] as string | undefined}
|
|
171
131
|
disabled={ctx.disabled}
|
|
172
|
-
{
|
|
132
|
+
maxHeight={ctx.maxHeight}
|
|
133
|
+
editableClass={editableClass()}
|
|
134
|
+
editableRef={(el) => ctx.setTextareaRef(el)}
|
|
135
|
+
triggers={local.triggers}
|
|
136
|
+
kindIcons={local.kindIcons}
|
|
137
|
+
// Surface the structured change (doc/entities) BEFORE the string value, so a
|
|
138
|
+
// consumer that enriches its events has the latest doc when value-change fires.
|
|
139
|
+
// A prompt can't start with whitespace — strip leading whitespace from the
|
|
140
|
+
// string value (parity with the old textarea); the controlled round-trip
|
|
141
|
+
// re-renders the editable so the stripped value shows too.
|
|
142
|
+
onChange={(c) => { local.onComposerChange?.(c); ctx.setValue(c.text.replace(/^\s+/, '')); }}
|
|
143
|
+
onSubmit={() => { if (!ctx.disabled) ctx.onSubmit?.(); }}
|
|
173
144
|
/>
|
|
174
145
|
);
|
|
175
146
|
}
|