@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
|
@@ -35,13 +35,43 @@ export type ArtifactTab = 'preview' | 'code';
|
|
|
35
35
|
/** A file the artifact can preview + show source for. */
|
|
36
36
|
export type ArtifactFile = FileTreeFile;
|
|
37
37
|
|
|
38
|
+
/** Imperative handle exposed via `controllerRef` — surfaces the artifact's latent
|
|
39
|
+
* toolbar capabilities (history back/forward/reload/home, programmatic navigate,
|
|
40
|
+
* file selection, open-in-new-tab, maximize/restore) so the `<kai-artifact>`
|
|
41
|
+
* facade can forward them as instance methods. Each delegates to the SAME internal
|
|
42
|
+
* handler the toolbar buttons use, so every existing event still fires. */
|
|
43
|
+
export interface ArtifactController {
|
|
44
|
+
/** Go back in the artifact's own history stack (no-op when there's no prior entry). */
|
|
45
|
+
back(): void;
|
|
46
|
+
/** Go forward in the history stack (no-op when there's no forward entry). */
|
|
47
|
+
forward(): void;
|
|
48
|
+
/** Force-reload the current preview url (also re-renders an inline PDF). */
|
|
49
|
+
reload(): void;
|
|
50
|
+
/** Navigate to the `src` home url (no-op when there's no `src`). */
|
|
51
|
+
home(): void;
|
|
52
|
+
/** Push + load a url in the preview (the path-field submit path). */
|
|
53
|
+
navigate(url: string): void;
|
|
54
|
+
/** Select a file by path: highlights the tree, shows its source, navigates the preview. */
|
|
55
|
+
selectFile(path: string): void;
|
|
56
|
+
/** Open the current url in a new browser tab (no-op when there's no concrete url). */
|
|
57
|
+
openExternal(): void;
|
|
58
|
+
/** Enter the maximized view-state (fires onMaximizeChange/kai-maximize-change). */
|
|
59
|
+
maximize(): void;
|
|
60
|
+
/** Exit the maximized view-state. */
|
|
61
|
+
restore(): void;
|
|
62
|
+
}
|
|
63
|
+
|
|
38
64
|
export interface ArtifactProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onSelect'> {
|
|
39
65
|
/** URL the preview iframe frames. */
|
|
40
66
|
src?: string;
|
|
41
67
|
/** Files for the Code tab's tree (+ each file's preview `url`). */
|
|
42
68
|
files?: ArtifactFile[];
|
|
43
|
-
/**
|
|
69
|
+
/** Controlled active tab — when set, the artifact follows it (re-asserted on
|
|
70
|
+
* every change). When undefined the tab is uncontrolled (see `defaultTab`). */
|
|
44
71
|
tab?: ArtifactTab;
|
|
72
|
+
/** Uncontrolled INITIAL tab (used only when `tab` is undefined). The user can
|
|
73
|
+
* then freely switch tabs; defaults to `preview`. */
|
|
74
|
+
defaultTab?: ArtifactTab;
|
|
45
75
|
/** Selected file path (syncs tree highlight + Code source + preview). */
|
|
46
76
|
activeFile?: string;
|
|
47
77
|
/** iframe `sandbox` override. Default `allow-scripts allow-forms`. */
|
|
@@ -80,6 +110,15 @@ export interface ArtifactProps extends Omit<JSX.HTMLAttributes<HTMLDivElement>,
|
|
|
80
110
|
standalone?: boolean;
|
|
81
111
|
/** Make the path field read-only (visible, nav-tracking, non-editable). */
|
|
82
112
|
readonlyPath?: boolean;
|
|
113
|
+
/** Friendly address shown in the path field INSTEAD of the real current url
|
|
114
|
+
* (read-only, non-navigable). Use when the framed url is not consumer-facing
|
|
115
|
+
* (e.g. a `data:` blob) so a clean address is shown instead of leaking it.
|
|
116
|
+
* Unset = show the real url (editable per `readonlyPath`). */
|
|
117
|
+
displayUrl?: string;
|
|
118
|
+
/** Receive the imperative controller once mounted. The `<kai-artifact>` facade
|
|
119
|
+
* forwards these as element methods (back/forward/reload/home/navigate/
|
|
120
|
+
* selectFile/openExternal/maximize/restore). */
|
|
121
|
+
controllerRef?: (controller: ArtifactController) => void;
|
|
83
122
|
}
|
|
84
123
|
|
|
85
124
|
const DEFAULT_SANDBOX = 'allow-scripts allow-forms';
|
|
@@ -116,7 +155,9 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
116
155
|
const merged = mergeProps(
|
|
117
156
|
{
|
|
118
157
|
files: [] as ArtifactFile[],
|
|
119
|
-
tab:
|
|
158
|
+
// No `tab` default: a present `tab` means controlled. The uncontrolled
|
|
159
|
+
// initial tab comes from `defaultTab` (seeded into the internal signal below).
|
|
160
|
+
defaultTab: 'preview' as ArtifactTab,
|
|
120
161
|
sandbox: DEFAULT_SANDBOX,
|
|
121
162
|
showNav: true,
|
|
122
163
|
showReload: true,
|
|
@@ -135,6 +176,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
135
176
|
'src',
|
|
136
177
|
'files',
|
|
137
178
|
'tab',
|
|
179
|
+
'defaultTab',
|
|
138
180
|
'activeFile',
|
|
139
181
|
'sandbox',
|
|
140
182
|
'iframeTitle',
|
|
@@ -152,6 +194,8 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
152
194
|
'openInTab',
|
|
153
195
|
'standalone',
|
|
154
196
|
'readonlyPath',
|
|
197
|
+
'displayUrl',
|
|
198
|
+
'controllerRef',
|
|
155
199
|
'class',
|
|
156
200
|
]);
|
|
157
201
|
|
|
@@ -169,7 +213,11 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
169
213
|
const canBack = () => cursor() > 0;
|
|
170
214
|
const canForward = () => cursor() < history().length - 1;
|
|
171
215
|
|
|
172
|
-
|
|
216
|
+
// Seed the internal tab: a present `tab` (controlled) wins; otherwise the
|
|
217
|
+
// uncontrolled `defaultTab` (which merges to 'preview'). The user can then
|
|
218
|
+
// switch freely unless `tab` is set (the effect below re-asserts the controlled
|
|
219
|
+
// value on each change).
|
|
220
|
+
const [tab, setTab] = createSignal<ArtifactTab>(local.tab ?? local.defaultTab);
|
|
173
221
|
const [activeFile, setActiveFile] = createSignal<string | undefined>(local.activeFile);
|
|
174
222
|
const [reloadKey, setReloadKey] = createSignal(0);
|
|
175
223
|
|
|
@@ -208,8 +256,12 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
208
256
|
|
|
209
257
|
let iframeEl: HTMLIFrameElement | undefined;
|
|
210
258
|
|
|
211
|
-
// Controlled syncing: when the consumer changes the props, follow them.
|
|
212
|
-
|
|
259
|
+
// Controlled syncing: when the consumer changes the props, follow them. `tab` is
|
|
260
|
+
// followed ONLY when it's actually set — an undefined `tab` means uncontrolled,
|
|
261
|
+
// so the internal signal (seeded from defaultTab) is left for the user to drive.
|
|
262
|
+
createEffect(() => {
|
|
263
|
+
if (local.tab !== undefined) setTab(local.tab);
|
|
264
|
+
});
|
|
213
265
|
createEffect(() => setActiveFile(local.activeFile));
|
|
214
266
|
// `src` change → navigate. Use `on(local.src, …)` so the effect tracks ONLY
|
|
215
267
|
// the `src` prop — NOT `currentUrl()` — otherwise navigating away (file click,
|
|
@@ -290,6 +342,38 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
290
342
|
if (local.src) navigate(local.src);
|
|
291
343
|
};
|
|
292
344
|
|
|
345
|
+
// Explicitly drive the maximize view-state to a target (the imperative twin of
|
|
346
|
+
// the toggle button). Fires onMaximizeChange only when the value actually flips,
|
|
347
|
+
// matching toggleMaximize's "report on change" contract.
|
|
348
|
+
const setMaximizeState = (next: boolean) => {
|
|
349
|
+
if (maximized() === next) return;
|
|
350
|
+
setMaximized(next);
|
|
351
|
+
local.onMaximizeChange?.(next);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// --- Imperative controller (Pattern C): hand the facade a handle over the
|
|
355
|
+
// artifact's latent toolbar capabilities. Every method delegates to the
|
|
356
|
+
// SAME internal handler the toolbar buttons use, so navigate/tab-change/
|
|
357
|
+
// file-select/maximize-change all still fire. ---
|
|
358
|
+
onMount(() => {
|
|
359
|
+
local.controllerRef?.({
|
|
360
|
+
back: () => goBack(),
|
|
361
|
+
forward: () => goForward(),
|
|
362
|
+
reload: () => reload(),
|
|
363
|
+
home: () => goHome(),
|
|
364
|
+
navigate: (url) => navigate(url),
|
|
365
|
+
// Resolve the file for the path so highlight + source + preview all sync;
|
|
366
|
+
// a path with no matching file entry is a no-op (nothing to show).
|
|
367
|
+
selectFile: (path) => {
|
|
368
|
+
const file = fileFor(path);
|
|
369
|
+
if (file) selectFile(path, file);
|
|
370
|
+
},
|
|
371
|
+
openExternal: () => openInNewTab(),
|
|
372
|
+
maximize: () => setMaximizeState(true),
|
|
373
|
+
restore: () => setMaximizeState(false),
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
293
377
|
// Best-effort: if the consumer opted into `allow-same-origin`, keep the path
|
|
294
378
|
// field truthful as the framed doc navigates itself. Cross-origin (the secure
|
|
295
379
|
// default) throws and we keep our own stack.
|
|
@@ -311,9 +395,10 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
311
395
|
const input = (e.currentTarget as HTMLFormElement).elements.namedItem(
|
|
312
396
|
'kai-artifact-path',
|
|
313
397
|
) as HTMLInputElement | null;
|
|
314
|
-
if (local.readonlyPath) {
|
|
315
|
-
// Submit is a no-op while read-only
|
|
316
|
-
|
|
398
|
+
if (local.readonlyPath || local.displayUrl != null) {
|
|
399
|
+
// Submit is a no-op while read-only or showing a friendly displayUrl; keep
|
|
400
|
+
// the field reflecting the shown value (never push the real, possibly data:, url).
|
|
401
|
+
if (input) input.value = local.displayUrl ?? currentUrl();
|
|
317
402
|
return;
|
|
318
403
|
}
|
|
319
404
|
if (input && input.value) navigate(input.value);
|
|
@@ -330,7 +415,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
330
415
|
>
|
|
331
416
|
<Show when={showAnyToolbar()}>
|
|
332
417
|
<ArtifactToolbar
|
|
333
|
-
url={currentUrl}
|
|
418
|
+
url={() => local.displayUrl ?? currentUrl()}
|
|
334
419
|
tab={tab}
|
|
335
420
|
canBack={canBack}
|
|
336
421
|
canForward={canForward}
|
|
@@ -352,7 +437,7 @@ export function Artifact(props: ArtifactProps): JSX.Element {
|
|
|
352
437
|
onToggleMaximize={toggleMaximize}
|
|
353
438
|
canOpenInTab={canOpenInTab}
|
|
354
439
|
onOpenInTab={openInNewTab}
|
|
355
|
-
readonlyPath={() => local.readonlyPath}
|
|
440
|
+
readonlyPath={() => local.readonlyPath || local.displayUrl != null}
|
|
356
441
|
/>
|
|
357
442
|
</Show>
|
|
358
443
|
<div class="relative min-h-0 flex-1">
|
|
@@ -420,7 +505,7 @@ function ArtifactToolbar(props: ToolbarProps): JSX.Element {
|
|
|
420
505
|
return (
|
|
421
506
|
<div
|
|
422
507
|
data-artifact-toolbar
|
|
423
|
-
class="flex shrink-0 items-center gap-1.5 border-b border-border bg-
|
|
508
|
+
class="flex shrink-0 items-center gap-1.5 border-b border-border bg-surface px-2 py-1.5"
|
|
424
509
|
>
|
|
425
510
|
<Show when={props.showNav()}>
|
|
426
511
|
<Button
|
|
@@ -595,7 +680,7 @@ interface CodeProps {
|
|
|
595
680
|
function ArtifactCode(props: CodeProps): JSX.Element {
|
|
596
681
|
return (
|
|
597
682
|
<div class="absolute inset-0 flex">
|
|
598
|
-
<div class="w-56 shrink-0 overflow-auto border-r border-border bg-
|
|
683
|
+
<div class="w-56 shrink-0 overflow-auto border-r border-border bg-surface-sunken py-1.5 scrollbar-thin">
|
|
599
684
|
<FileTree
|
|
600
685
|
files={props.files}
|
|
601
686
|
activeFile={props.activeFile()}
|
|
@@ -728,7 +813,7 @@ function ArtifactPdfPreview(props: { url: string; reloadKey: number }): JSX.Elem
|
|
|
728
813
|
aria-label="PDF preview"
|
|
729
814
|
aria-busy={state() === 'loading'}
|
|
730
815
|
tabindex="0"
|
|
731
|
-
class="absolute inset-0 flex flex-col items-center gap-3 overflow-auto bg-
|
|
816
|
+
class="absolute inset-0 flex flex-col items-center gap-3 overflow-auto bg-surface-sunken p-3 scrollbar-thin focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset"
|
|
732
817
|
/>
|
|
733
818
|
<Show when={isPdfPreviewEnabled() && state() === 'loading'}>
|
|
734
819
|
<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** A message attachment descriptor. Pure type — kept JSX-free for the
|
|
2
|
+
* framework-neutral consumers (state core, React hook typecheck). */
|
|
3
|
+
export interface AttachmentData {
|
|
4
|
+
id: string;
|
|
5
|
+
type: 'file' | 'source-document';
|
|
6
|
+
filename?: string;
|
|
7
|
+
mediaType?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
|
|
13
|
+
export type AttachmentVariant = 'grid' | 'inline' | 'list';
|
|
@@ -11,23 +11,13 @@ import {
|
|
|
11
11
|
Video,
|
|
12
12
|
X,
|
|
13
13
|
} from 'lucide-solid';
|
|
14
|
+
import type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types';
|
|
15
|
+
export type { AttachmentData, AttachmentMediaCategory, AttachmentVariant } from './attachment-types';
|
|
14
16
|
|
|
15
17
|
// ============================================================================
|
|
16
18
|
// Types
|
|
17
19
|
// ============================================================================
|
|
18
20
|
|
|
19
|
-
export interface AttachmentData {
|
|
20
|
-
id: string;
|
|
21
|
-
type: 'file' | 'source-document';
|
|
22
|
-
filename?: string;
|
|
23
|
-
mediaType?: string;
|
|
24
|
-
url?: string;
|
|
25
|
-
title?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
|
|
29
|
-
export type AttachmentVariant = 'grid' | 'inline' | 'list';
|
|
30
|
-
|
|
31
21
|
const mediaCategoryIcons: Record<AttachmentMediaCategory, typeof ImageIcon> = {
|
|
32
22
|
audio: Music2,
|
|
33
23
|
document: FileText,
|
|
@@ -159,12 +149,12 @@ function Attachment(props: AttachmentProps) {
|
|
|
159
149
|
ctx.variant === 'grid' && 'size-24 overflow-hidden rounded-lg',
|
|
160
150
|
ctx.variant === 'inline' && [
|
|
161
151
|
'flex h-8 cursor-pointer select-none items-center gap-1.5',
|
|
162
|
-
'rounded-md bg-
|
|
152
|
+
'rounded-md bg-surface-strong px-1.5 text-foreground',
|
|
163
153
|
'font-medium text-sm transition-all',
|
|
164
154
|
'hover:bg-muted',
|
|
165
155
|
],
|
|
166
156
|
ctx.variant === 'list' && [
|
|
167
|
-
'flex w-full items-center gap-3 rounded-lg bg-
|
|
157
|
+
'flex w-full items-center gap-3 rounded-lg bg-surface p-3 text-foreground',
|
|
168
158
|
'hover:bg-muted/50',
|
|
169
159
|
],
|
|
170
160
|
local.class,
|
package/src/components/card.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type JSX, Show, splitProps, mergeProps, createUniqueId } from 'solid-js';
|
|
1
|
+
import { type JSX, Show, splitProps, mergeProps, createSignal, createUniqueId } from 'solid-js';
|
|
2
|
+
import { Dynamic } from 'solid-js/web';
|
|
2
3
|
import { cn } from '../utils/cn';
|
|
3
|
-
import { AlertTriangle } from 'lucide-solid';
|
|
4
|
+
import { AlertTriangle, X } from 'lucide-solid';
|
|
4
5
|
|
|
5
6
|
export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
6
7
|
/** Heading rendered in the card chrome (the contract's CardEnvelope.title).
|
|
@@ -19,6 +20,25 @@ export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
19
20
|
/** Stable id for the heading (so composing cards can `aria-labelledby` it).
|
|
20
21
|
* Auto-generated when omitted. */
|
|
21
22
|
headingId?: string;
|
|
23
|
+
/** Render a dismiss (×) button that hides the card and calls `onDismiss`.
|
|
24
|
+
* Opt-in, OFF by default — the contract cards never set it, so they are
|
|
25
|
+
* unaffected. */
|
|
26
|
+
dismissible?: boolean;
|
|
27
|
+
/** Called after the card is dismissed via its ×. */
|
|
28
|
+
onDismiss?: () => void;
|
|
29
|
+
/** Render the whole card as a link (`<a>`). Wins over `clickable`. */
|
|
30
|
+
href?: string;
|
|
31
|
+
/** `target` for the `href` anchor. */
|
|
32
|
+
target?: string;
|
|
33
|
+
/** `rel` for the `href` anchor (default `noopener noreferrer` when `target="_blank"`). */
|
|
34
|
+
rel?: string;
|
|
35
|
+
/** Make the whole card behave like a button: `role="button"`, a tabindex, and
|
|
36
|
+
* Enter/Space activation, firing `onCardClick`. Ignored when `href` is set.
|
|
37
|
+
* A clickable/href card must NOT also contain footer action buttons — that
|
|
38
|
+
* nests interactive elements. Use `actions` OR clickable, never both. */
|
|
39
|
+
clickable?: boolean;
|
|
40
|
+
/** Called when a `clickable` (or `href`) card is activated (click, or Enter/Space). */
|
|
41
|
+
onCardClick?: (event: MouseEvent | KeyboardEvent) => void;
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
/**
|
|
@@ -28,6 +48,14 @@ export interface CardProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
28
48
|
* "never a broken/partial card" rule). It is intentionally chrome-only: it reads
|
|
29
49
|
* no `CardContext` and emits no `CardEvent` — the cards that compose it (e.g.
|
|
30
50
|
* `kai-form`) own the contract interaction.
|
|
51
|
+
*
|
|
52
|
+
* The optional `dismissible` / `href` / `clickable` behaviors are all OFF by
|
|
53
|
+
* default, so the contract cards (which set none of them) keep their original
|
|
54
|
+
* presentational behavior unchanged.
|
|
55
|
+
*
|
|
56
|
+
* a11y: a `clickable` or `href` card MUST NOT also contain footer action buttons
|
|
57
|
+
* — that nests interactive controls inside a button/link. Use `actions` OR
|
|
58
|
+
* make the card clickable, never both.
|
|
31
59
|
*/
|
|
32
60
|
export function Card(props: CardProps): JSX.Element {
|
|
33
61
|
const merged = mergeProps({ dense: false }, props);
|
|
@@ -39,6 +67,13 @@ export function Card(props: CardProps): JSX.Element {
|
|
|
39
67
|
'errorMessage',
|
|
40
68
|
'dense',
|
|
41
69
|
'headingId',
|
|
70
|
+
'dismissible',
|
|
71
|
+
'onDismiss',
|
|
72
|
+
'href',
|
|
73
|
+
'target',
|
|
74
|
+
'rel',
|
|
75
|
+
'clickable',
|
|
76
|
+
'onCardClick',
|
|
42
77
|
'class',
|
|
43
78
|
'children',
|
|
44
79
|
]);
|
|
@@ -48,19 +83,36 @@ export function Card(props: CardProps): JSX.Element {
|
|
|
48
83
|
const hasError = () => local.errorMessage !== undefined && local.errorMessage !== '';
|
|
49
84
|
const hasHeader = () => Boolean(local.heading) || Boolean(local.description);
|
|
50
85
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
</Show>
|
|
86
|
+
const [open, setOpen] = createSignal(true);
|
|
87
|
+
const dismiss = (event: MouseEvent) => {
|
|
88
|
+
// Don't let the × activate a clickable/href card underneath it.
|
|
89
|
+
event.stopPropagation();
|
|
90
|
+
setOpen(false);
|
|
91
|
+
local.onDismiss?.();
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const isLink = () => Boolean(local.href);
|
|
95
|
+
const isButton = () => !isLink() && Boolean(local.clickable);
|
|
96
|
+
const isInteractive = () => isLink() || isButton();
|
|
63
97
|
|
|
98
|
+
const activate = (event: MouseEvent | KeyboardEvent) => local.onCardClick?.(event);
|
|
99
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
100
|
+
if (!isButton()) return;
|
|
101
|
+
if (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar') {
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
activate(event);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// The interactive elements (`<a>` / role="button") get keyboard + hover/focus
|
|
108
|
+
// affordances; the plain card is a `<div>`. Anchors carry a safe default `rel`
|
|
109
|
+
// when opening a new tab.
|
|
110
|
+
const rel = () =>
|
|
111
|
+
local.rel ?? (local.target === '_blank' ? 'noopener noreferrer' : undefined);
|
|
112
|
+
|
|
113
|
+
// The card's heading + body + actions column.
|
|
114
|
+
const inner = (
|
|
115
|
+
<>
|
|
64
116
|
<Show when={hasHeader()}>
|
|
65
117
|
<div
|
|
66
118
|
class={cn(
|
|
@@ -105,6 +157,48 @@ export function Card(props: CardProps): JSX.Element {
|
|
|
105
157
|
<span>{local.errorMessage}</span>
|
|
106
158
|
</div>
|
|
107
159
|
</Show>
|
|
108
|
-
|
|
160
|
+
</>
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<Show when={open()}>
|
|
165
|
+
<Dynamic
|
|
166
|
+
component={isLink() ? 'a' : 'div'}
|
|
167
|
+
part="card"
|
|
168
|
+
href={isLink() ? local.href : undefined}
|
|
169
|
+
target={isLink() ? local.target : undefined}
|
|
170
|
+
rel={isLink() ? rel() : undefined}
|
|
171
|
+
role={isButton() ? 'button' : undefined}
|
|
172
|
+
tabindex={isButton() ? 0 : undefined}
|
|
173
|
+
onClick={isInteractive() ? activate : undefined}
|
|
174
|
+
onKeyDown={isButton() ? onKeyDown : undefined}
|
|
175
|
+
class={cn(
|
|
176
|
+
'relative flex flex-col rounded-xl border border-border bg-card text-card-foreground kai-elevation-sm',
|
|
177
|
+
local.dense ? 'gap-2.5 p-3.5' : 'gap-4 p-5',
|
|
178
|
+
isInteractive() &&
|
|
179
|
+
'cursor-pointer no-underline transition-colors hover:border-ring hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
180
|
+
local.class,
|
|
181
|
+
)}
|
|
182
|
+
{...rest}
|
|
183
|
+
>
|
|
184
|
+
<Show when={local.dismissible}>
|
|
185
|
+
<button
|
|
186
|
+
type="button"
|
|
187
|
+
part="dismiss"
|
|
188
|
+
aria-label="Dismiss"
|
|
189
|
+
onClick={dismiss}
|
|
190
|
+
class="absolute right-2.5 top-2.5 z-10 shrink-0 rounded p-1 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
|
191
|
+
>
|
|
192
|
+
<X class="size-3.5" aria-hidden="true" />
|
|
193
|
+
</button>
|
|
194
|
+
</Show>
|
|
195
|
+
|
|
196
|
+
<Show when={local.media}>
|
|
197
|
+
<div class="overflow-hidden rounded-lg">{local.media}</div>
|
|
198
|
+
</Show>
|
|
199
|
+
|
|
200
|
+
{inner}
|
|
201
|
+
</Dynamic>
|
|
202
|
+
</Show>
|
|
109
203
|
);
|
|
110
204
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type JSX,
|
|
3
|
+
type Accessor,
|
|
4
|
+
splitProps,
|
|
5
|
+
createSignal,
|
|
6
|
+
For,
|
|
7
|
+
Show,
|
|
8
|
+
} from 'solid-js';
|
|
2
9
|
import { cn } from '../utils/cn';
|
|
3
10
|
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../ui/collapsible';
|
|
4
11
|
import { ChevronDown, Circle } from 'lucide-solid';
|
|
@@ -114,6 +121,15 @@ export interface ChainOfThoughtStepProps {
|
|
|
114
121
|
children: JSX.Element;
|
|
115
122
|
class?: string;
|
|
116
123
|
isLast?: boolean;
|
|
124
|
+
/** Controlled open state. When provided, the step's Collapsible is driven by
|
|
125
|
+
* this value (the parent owns it — used by the Accordion model below). When
|
|
126
|
+
* OMITTED the step stays uncontrolled, preserving the original behaviour
|
|
127
|
+
* (every step independently toggleable, starting closed). */
|
|
128
|
+
open?: boolean;
|
|
129
|
+
/** Called with the desired next open state when the user clicks the trigger.
|
|
130
|
+
* Only meaningful when `open` is also provided (controlled mode); the parent
|
|
131
|
+
* Accordion routes this through its open-set handler. */
|
|
132
|
+
onOpenChange?: (open: boolean) => void;
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
|
|
@@ -121,6 +137,8 @@ function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
|
|
|
121
137
|
<Collapsible
|
|
122
138
|
class={cn('group', props.class)}
|
|
123
139
|
data-last={props.isLast ?? false}
|
|
140
|
+
open={props.open}
|
|
141
|
+
onOpenChange={props.onOpenChange}
|
|
124
142
|
>
|
|
125
143
|
{props.children}
|
|
126
144
|
<div class="flex justify-start group-data-[last=true]:hidden">
|
|
@@ -130,10 +148,201 @@ function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
|
|
|
130
148
|
);
|
|
131
149
|
}
|
|
132
150
|
|
|
151
|
+
// --- Accordion model (the kai-chain-of-thought interaction surface) ---
|
|
152
|
+
|
|
153
|
+
/** Open-mode: `multiple` (any number of steps open at once — the historical
|
|
154
|
+
* default) or `single` (at most one open; opening a step closes the others). */
|
|
155
|
+
export type ChainOfThoughtType = 'single' | 'multiple';
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A reasoning step descriptor. `id` is an OPTIONAL stable key — the Accordion's
|
|
159
|
+
* open-set keys use `step.id` when present, else `String(index)`. So a consumer
|
|
160
|
+
* who wants to drive `value`/`defaultValue` (or read `kai-value-change`) by a
|
|
161
|
+
* meaningful key should set `id`; otherwise the step's position is its key.
|
|
162
|
+
*/
|
|
163
|
+
export interface ChainOfThoughtStepData {
|
|
164
|
+
/** The step's heading (the always-visible trigger). */
|
|
165
|
+
label: string;
|
|
166
|
+
/** Optional expandable detail. */
|
|
167
|
+
content?: string;
|
|
168
|
+
/** Optional stable key for the open-set/value model (defaults to the index). */
|
|
169
|
+
id?: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Imperative handle exposed via `controllerRef` so the `<kai-chain-of-thought>`
|
|
174
|
+
* facade can drive/observe the Accordion's open set. Index-based to match the
|
|
175
|
+
* `expand(index?)`/`collapse(index?)`/`toggle(index?)` method signatures; the
|
|
176
|
+
* controller resolves the index → step key internally. All setters flow through
|
|
177
|
+
* the same open-set mutate path as user clicks, so they all emit
|
|
178
|
+
* `kai-value-change` via `onValueChange`.
|
|
179
|
+
*/
|
|
180
|
+
export interface ChainOfThoughtController {
|
|
181
|
+
/** Current open keys, normalised per `type` (string in single, string[] in multiple). */
|
|
182
|
+
value: Accessor<string | string[]>;
|
|
183
|
+
/** Open one step by index, or ALL steps when `index` is omitted. */
|
|
184
|
+
expand: (index?: number) => void;
|
|
185
|
+
/** Close one step by index, or ALL steps when `index` is omitted. */
|
|
186
|
+
collapse: (index?: number) => void;
|
|
187
|
+
/** Flip one step's open state by index. */
|
|
188
|
+
toggle: (index?: number) => void;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ChainOfThoughtAccordionProps {
|
|
192
|
+
/** The reasoning steps to render. */
|
|
193
|
+
steps: ChainOfThoughtStepData[];
|
|
194
|
+
/** Open mode. Default `multiple` (the historical behaviour). */
|
|
195
|
+
type?: ChainOfThoughtType;
|
|
196
|
+
/** Controlled open step key(s). When provided it WINS over internal state. */
|
|
197
|
+
value?: string | string[];
|
|
198
|
+
/** Uncontrolled initial open step key(s). Seeds the internal open-set. */
|
|
199
|
+
defaultValue?: string | string[];
|
|
200
|
+
/** Fired whenever the open set changes (user click OR a controller method).
|
|
201
|
+
* The single emit point — the facade turns this into `kai-value-change`. The
|
|
202
|
+
* payload is a string in `single` mode, a string[] in `multiple` mode. */
|
|
203
|
+
onValueChange?: (value: string | string[]) => void;
|
|
204
|
+
/** Receive the imperative controller once set up (Pattern C). */
|
|
205
|
+
controllerRef?: (api: ChainOfThoughtController) => void;
|
|
206
|
+
class?: string;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Normalise a `string | string[]` (or undefined) seed to a Set of keys. */
|
|
210
|
+
function toKeySet(value: string | string[] | undefined): Set<string> {
|
|
211
|
+
if (value == null) return new Set();
|
|
212
|
+
return new Set(Array.isArray(value) ? value : [value]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The full Accordion-model root used by `<kai-chain-of-thought>`. Lifts each
|
|
217
|
+
* step's open state into a controlled/uncontrolled open-set:
|
|
218
|
+
* - seeded from `defaultValue` (normalised string|string[] → Set);
|
|
219
|
+
* - when `value` is provided it WINS (controlled);
|
|
220
|
+
* - a trigger click updates the set respecting `single` vs `multiple` and emits
|
|
221
|
+
* via `onValueChange` — in single mode opening a step closes the others;
|
|
222
|
+
* - each step's Collapsible is driven (controlled) by its membership in the set.
|
|
223
|
+
*
|
|
224
|
+
* With NO new props (type defaults to `multiple`, no value/defaultValue), every
|
|
225
|
+
* step starts closed and is independently toggleable — identical to the old
|
|
226
|
+
* behaviour, just with the open state lifted here so methods/events can observe
|
|
227
|
+
* and drive it.
|
|
228
|
+
*/
|
|
229
|
+
function ChainOfThoughtAccordion(props: ChainOfThoughtAccordionProps) {
|
|
230
|
+
const [internal, setInternal] = createSignal<Set<string>>(toKeySet(props.defaultValue));
|
|
231
|
+
|
|
232
|
+
const type = (): ChainOfThoughtType => props.type ?? 'multiple';
|
|
233
|
+
|
|
234
|
+
// The step key: explicit id when present, else the index as a string.
|
|
235
|
+
const keyOf = (step: ChainOfThoughtStepData, index: number) => step.id ?? String(index);
|
|
236
|
+
|
|
237
|
+
// Controlled `value` wins; otherwise the internal signal owns the open-set.
|
|
238
|
+
const isControlled = () => props.value !== undefined;
|
|
239
|
+
const openSet = (): Set<string> => (isControlled() ? toKeySet(props.value) : internal());
|
|
240
|
+
|
|
241
|
+
const isOpen = (key: string) => openSet().has(key);
|
|
242
|
+
|
|
243
|
+
// Shape the open-set as the public value: a single string (or '') in single
|
|
244
|
+
// mode, a string[] in multiple mode.
|
|
245
|
+
const toValue = (set: Set<string>): string | string[] => {
|
|
246
|
+
if (type() === 'single') {
|
|
247
|
+
const first = set.values().next();
|
|
248
|
+
return first.done ? '' : first.value;
|
|
249
|
+
}
|
|
250
|
+
return [...set];
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// The single mutate path — shared by trigger clicks and controller methods.
|
|
254
|
+
// Computes the next open-set, writes it (uncontrolled only) and ALWAYS emits
|
|
255
|
+
// onValueChange so both user + programmatic changes flow to kai-value-change.
|
|
256
|
+
const commit = (next: Set<string>) => {
|
|
257
|
+
const cur = openSet();
|
|
258
|
+
// No-op if the set is unchanged (avoids redundant events).
|
|
259
|
+
if (cur.size === next.size && [...cur].every((k) => next.has(k))) return;
|
|
260
|
+
if (!isControlled()) setInternal(next);
|
|
261
|
+
props.onValueChange?.(toValue(next));
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// Set a single key's open state, respecting single vs multiple.
|
|
265
|
+
const setKeyOpen = (key: string, open: boolean) => {
|
|
266
|
+
if (type() === 'single') {
|
|
267
|
+
commit(open ? new Set([key]) : new Set());
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const next = new Set(openSet());
|
|
271
|
+
if (open) next.add(key); else next.delete(key);
|
|
272
|
+
commit(next);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Resolve a step index → key (undefined when out of range).
|
|
276
|
+
const keyAt = (index: number): string | undefined => {
|
|
277
|
+
const step = props.steps[index];
|
|
278
|
+
return step ? keyOf(step, index) : undefined;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const allKeys = (): string[] => props.steps.map((s, i) => keyOf(s, i));
|
|
282
|
+
|
|
283
|
+
// --- Controller (Pattern C): hand the facade an index-based handle. Every
|
|
284
|
+
// setter routes through commit() so methods + clicks share one emit point.
|
|
285
|
+
props.controllerRef?.({
|
|
286
|
+
value: () => toValue(openSet()),
|
|
287
|
+
expand: (index?: number) => {
|
|
288
|
+
if (index === undefined) {
|
|
289
|
+
// Open all. In single mode only one can be open — keep the last step.
|
|
290
|
+
if (type() === 'single') {
|
|
291
|
+
const keys = allKeys();
|
|
292
|
+
commit(keys.length ? new Set([keys[keys.length - 1]]) : new Set());
|
|
293
|
+
} else {
|
|
294
|
+
commit(new Set(allKeys()));
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const key = keyAt(index);
|
|
299
|
+
if (key !== undefined) setKeyOpen(key, true);
|
|
300
|
+
},
|
|
301
|
+
collapse: (index?: number) => {
|
|
302
|
+
if (index === undefined) {
|
|
303
|
+
commit(new Set());
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const key = keyAt(index);
|
|
307
|
+
if (key !== undefined) setKeyOpen(key, false);
|
|
308
|
+
},
|
|
309
|
+
toggle: (index?: number) => {
|
|
310
|
+
if (index === undefined) return;
|
|
311
|
+
const key = keyAt(index);
|
|
312
|
+
if (key !== undefined) setKeyOpen(key, !isOpen(key));
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<ChainOfThought class={props.class}>
|
|
318
|
+
<For each={props.steps}>
|
|
319
|
+
{(step, i) => {
|
|
320
|
+
const key = () => keyOf(step, i());
|
|
321
|
+
return (
|
|
322
|
+
<ChainOfThoughtStep
|
|
323
|
+
isLast={i() === props.steps.length - 1}
|
|
324
|
+
open={isOpen(key())}
|
|
325
|
+
onOpenChange={(open) => setKeyOpen(key(), open)}
|
|
326
|
+
>
|
|
327
|
+
<ChainOfThoughtTrigger>{step.label}</ChainOfThoughtTrigger>
|
|
328
|
+
<Show when={step.content}>
|
|
329
|
+
<ChainOfThoughtContent>
|
|
330
|
+
<ChainOfThoughtItem>{step.content}</ChainOfThoughtItem>
|
|
331
|
+
</ChainOfThoughtContent>
|
|
332
|
+
</Show>
|
|
333
|
+
</ChainOfThoughtStep>
|
|
334
|
+
);
|
|
335
|
+
}}
|
|
336
|
+
</For>
|
|
337
|
+
</ChainOfThought>
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
133
341
|
export {
|
|
134
342
|
ChainOfThought,
|
|
135
343
|
ChainOfThoughtStep,
|
|
136
344
|
ChainOfThoughtTrigger,
|
|
137
345
|
ChainOfThoughtContent,
|
|
138
346
|
ChainOfThoughtItem,
|
|
347
|
+
ChainOfThoughtAccordion,
|
|
139
348
|
};
|