@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSignal, createEffect, on, onMount, onCleanup, For, Show, type JSX } from 'solid-js';
|
|
2
2
|
import { defineWebComponent } from './define';
|
|
3
|
-
import { ResizableHandle, normalizeSize } from '../ui/resizable';
|
|
3
|
+
import { ResizableHandle, normalizeSize, clampBasis } from '../ui/resizable';
|
|
4
4
|
|
|
5
5
|
type Orientation = 'horizontal' | 'vertical';
|
|
6
6
|
|
|
@@ -34,6 +34,7 @@ interface ItemInfo {
|
|
|
34
34
|
max?: string;
|
|
35
35
|
locked: boolean;
|
|
36
36
|
hidden: boolean;
|
|
37
|
+
collapsed: boolean;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/** Max number of panels a single group lays out (nest for more). */
|
|
@@ -100,6 +101,13 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
100
101
|
el.setAttribute('data-kai-default-size', el.getAttribute('size') ?? '');
|
|
101
102
|
}
|
|
102
103
|
const defaultSize = el.getAttribute('data-kai-default-size') || undefined;
|
|
104
|
+
// The `collapsed` boolean prop is reflected to a `collapsed` attribute by the
|
|
105
|
+
// item facade (see kai-resizable-item), so reading the attribute here works
|
|
106
|
+
// at INITIAL render from any framework — unlike the bare `hidden` boolean,
|
|
107
|
+
// whose JSX form sets neither the attribute nor the IDL property on a custom
|
|
108
|
+
// element. Treated as hidden-for-layout, independently of `hidden` so the
|
|
109
|
+
// back-compat hidden path (and maximize, which drives `hidden`) is untouched.
|
|
110
|
+
const collapsed = el.hasAttribute('collapsed') && el.getAttribute('collapsed') !== 'false';
|
|
103
111
|
return {
|
|
104
112
|
el,
|
|
105
113
|
size: el.getAttribute('size') ?? undefined,
|
|
@@ -111,14 +119,15 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
111
119
|
// (and direct `el.hidden = true`) set the property, which doesn't reflect
|
|
112
120
|
// to the attribute on a custom element.
|
|
113
121
|
hidden: el.hidden || (el.hasAttribute('hidden') && el.getAttribute('hidden') !== 'false'),
|
|
122
|
+
collapsed,
|
|
114
123
|
};
|
|
115
124
|
});
|
|
116
125
|
|
|
117
126
|
// Assign each visible item to its panel slot by visible order; clear the
|
|
118
|
-
// rest so hidden/extra items don't leak into a slot.
|
|
127
|
+
// rest so hidden/extra/collapsed items don't leak into a slot.
|
|
119
128
|
let visIdx = 0;
|
|
120
129
|
for (const info of parsed) {
|
|
121
|
-
if (info.hidden) {
|
|
130
|
+
if (info.hidden || info.collapsed) {
|
|
122
131
|
info.el.removeAttribute('slot');
|
|
123
132
|
} else {
|
|
124
133
|
info.el.setAttribute('slot', `p${visIdx}`);
|
|
@@ -153,12 +162,12 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
153
162
|
const b = next[i];
|
|
154
163
|
if (a.el !== b.el) return true;
|
|
155
164
|
if (a.size !== b.size || a.min !== b.min || a.max !== b.max) return true;
|
|
156
|
-
if (a.locked !== b.locked || a.hidden !== b.hidden) return true;
|
|
165
|
+
if (a.locked !== b.locked || a.hidden !== b.hidden || a.collapsed !== b.collapsed) return true;
|
|
157
166
|
}
|
|
158
167
|
return false;
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
const visible = () => items().filter((i) => !i.hidden);
|
|
170
|
+
const visible = () => items().filter((i) => !i.hidden && !i.collapsed);
|
|
162
171
|
|
|
163
172
|
/** Compute current panel sizes (percent of container) from the rendered DOM. */
|
|
164
173
|
function currentSizes(): number[] {
|
|
@@ -358,9 +367,11 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
358
367
|
// maximized item is still present and visible.
|
|
359
368
|
const maximizedEl = stash.item;
|
|
360
369
|
const isConnected = maximizedEl.isConnected && element.contains(maximizedEl);
|
|
361
|
-
const isVisible = isConnected
|
|
370
|
+
const isVisible = isConnected
|
|
371
|
+
&& !(maximizedEl.hidden || maximizedEl.hasAttribute('hidden'))
|
|
372
|
+
&& !(maximizedEl.hasAttribute('collapsed') && maximizedEl.getAttribute('collapsed') !== 'false');
|
|
362
373
|
if (!isConnected || !isVisible) {
|
|
363
|
-
// The maximized item was removed or hidden out from under us → restore.
|
|
374
|
+
// The maximized item was removed or hidden/collapsed out from under us → restore.
|
|
364
375
|
restore();
|
|
365
376
|
return;
|
|
366
377
|
}
|
|
@@ -375,7 +386,7 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
375
386
|
// the host's own attributes.
|
|
376
387
|
subtree: true,
|
|
377
388
|
attributes: true,
|
|
378
|
-
attributeFilter: ['size', 'locked', 'min', 'max', 'hidden'],
|
|
389
|
+
attributeFilter: ['size', 'locked', 'min', 'max', 'hidden', 'collapsed'],
|
|
379
390
|
});
|
|
380
391
|
onCleanup(() => {
|
|
381
392
|
mo.disconnect();
|
|
@@ -432,7 +443,9 @@ defineWebComponent<GroupProps, GroupEvents>('kai-resizable', {
|
|
|
432
443
|
const min = boundAttrs(info.min);
|
|
433
444
|
const max = boundAttrs(info.max);
|
|
434
445
|
const def = boundAttrs(info.defaultSize);
|
|
435
|
-
|
|
446
|
+
// Clamp the INITIAL basis into [min, max] so a % size on a wide
|
|
447
|
+
// container never paints past a px max and then snaps on first drag.
|
|
448
|
+
const basis = clampBasis(normalizeSize(info.size), normalizeSize(info.min), normalizeSize(info.max));
|
|
436
449
|
const prev = () => visible()[i() - 1];
|
|
437
450
|
const isStatic = () => !!(info.locked || prev()?.locked);
|
|
438
451
|
return (
|
|
@@ -514,6 +527,16 @@ interface ItemProps extends Record<string, unknown> {
|
|
|
514
527
|
locked?: boolean;
|
|
515
528
|
/** Hide this panel; its divider is dropped and the rest reflow. */
|
|
516
529
|
hidden?: boolean;
|
|
530
|
+
/**
|
|
531
|
+
* Collapse this panel — same layout effect as `hidden` (divider dropped, the
|
|
532
|
+
* rest reflow), but it WORKS as a bare boolean from framework JSX. A plain
|
|
533
|
+
* `<kai-resizable-item collapsed>` in React/Solid/Vue/Svelte collapses the panel
|
|
534
|
+
* at the first render; `hidden` does not, because a JSX boolean sets neither the
|
|
535
|
+
* `hidden` attribute nor the IDL property on a custom element, so the parent never
|
|
536
|
+
* sees it. The facade reflects `collapsed` to a `collapsed` attribute the parent
|
|
537
|
+
* reads. Prefer this over `hidden` for declarative collapse.
|
|
538
|
+
*/
|
|
539
|
+
collapsed?: boolean;
|
|
517
540
|
}
|
|
518
541
|
|
|
519
542
|
/**
|
|
@@ -527,7 +550,20 @@ defineWebComponent<ItemProps>('kai-resizable-item', {
|
|
|
527
550
|
max: undefined,
|
|
528
551
|
locked: false,
|
|
529
552
|
hidden: false,
|
|
530
|
-
|
|
553
|
+
collapsed: false,
|
|
554
|
+
}, (_props, { element, flag }) => {
|
|
555
|
+
// Reflect the reactive `collapsed` prop to the `collapsed` ATTRIBUTE the parent
|
|
556
|
+
// <kai-resizable> reads (and observes). This is what makes a bare JSX boolean
|
|
557
|
+
// collapse the panel at INITIAL render from any framework: component-register
|
|
558
|
+
// parses `<kai-resizable-item collapsed>` to `undefined` (not `true`), and a JSX
|
|
559
|
+
// boolean sets neither `hidden` nor the IDL property — so `flag('collapsed')`
|
|
560
|
+
// (which also honours the attribute) is the reliable source, and toggleAttribute
|
|
561
|
+
// mirrors it back so the parent's readItems()/MutationObserver lay it out. Same
|
|
562
|
+
// pattern as <kai-chat>'s `loading` reflection. We touch only `collapsed`, never
|
|
563
|
+
// `hidden`, so the back-compat hidden path and maximize (which drive `hidden`)
|
|
564
|
+
// are untouched.
|
|
565
|
+
createEffect(() => { element.toggleAttribute('collapsed', flag('collapsed')); });
|
|
566
|
+
return (
|
|
531
567
|
<>
|
|
532
568
|
{/* The item host fills the panel's single grid cell (the panel stretches it on
|
|
533
569
|
both axes) and OWNS the scroll: `display:block; width/height:100%;
|
|
@@ -550,4 +586,5 @@ defineWebComponent<ItemProps>('kai-resizable-item', {
|
|
|
550
586
|
<slot />
|
|
551
587
|
</div>
|
|
552
588
|
</>
|
|
553
|
-
) as unknown as JSX.Element
|
|
589
|
+
) as unknown as JSX.Element;
|
|
590
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { defineWebComponent } from './define';
|
|
2
|
+
import { Screen, type ScreenController } from '../components/screen';
|
|
3
|
+
import { wireDisclosure } from './disclosure';
|
|
4
|
+
|
|
5
|
+
interface Props extends Record<string, unknown> {
|
|
6
|
+
/** Drive/observe open state (Shoelace-style: settable + reflected to the `open`
|
|
7
|
+
* attribute; the element still self-manages). Set `el.open = true`, or
|
|
8
|
+
* `<kai-screen open>`; listen for `kai-open-change`. */
|
|
9
|
+
open?: boolean;
|
|
10
|
+
/** Initial open state on mount (uncontrolled seed). */
|
|
11
|
+
defaultOpen?: boolean;
|
|
12
|
+
/** Header title text. A projected `title` slot overrides it. (Named `headline`
|
|
13
|
+
* because `title` collides with the global `HTMLElement.title` attribute.) */
|
|
14
|
+
headline?: string;
|
|
15
|
+
/** Show the back button (default true). */
|
|
16
|
+
back?: boolean;
|
|
17
|
+
/** Opt out of marking sibling elements inert/aria-hidden while open (for unusual layouts). */
|
|
18
|
+
noInert?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Events fired by `<kai-screen>`. */
|
|
22
|
+
interface Events {
|
|
23
|
+
/** Back navigation intent: the back button or Escape. The consumer flips their
|
|
24
|
+
* own routing in response (the screen knows nothing about the trigger). */
|
|
25
|
+
'kai-back': Record<string, never>;
|
|
26
|
+
/** The screen opened or closed (a method, `Escape` close, or driven `open`). */
|
|
27
|
+
'kai-open-change': { open: boolean };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `<kai-screen>` is a developer-swapped, full-bleed overlay destination: the
|
|
32
|
+
* push/drill-in surface that takes over its mount point under a back-header. The
|
|
33
|
+
* developer owns the swap (their own routing flips `open` in response to their
|
|
34
|
+
* trigger button and the screen's `kai-back`); the screen owns being the takeover.
|
|
35
|
+
*
|
|
36
|
+
* It fills whatever it is mounted in (mount at the app root for a full takeover,
|
|
37
|
+
* in a positioned region for a scoped one), marks sibling elements `inert` while
|
|
38
|
+
* open (the standard modal pattern; opt out with `no-inert`), moves focus in on
|
|
39
|
+
* open and restores it on close, and runs an enter/exit transition that honors
|
|
40
|
+
* `prefers-reduced-motion`. `Escape` fires `kai-back`.
|
|
41
|
+
*
|
|
42
|
+
* ```html
|
|
43
|
+
* <kai-screen headline="Design">
|
|
44
|
+
* <button slot="actions"><kai-avatar></kai-avatar></button>
|
|
45
|
+
* <div>…your surface…</div>
|
|
46
|
+
* </kai-screen>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Open state is the standard disclosure surface: settable+reflecting `open`,
|
|
50
|
+
* `kai-open-change`, and `show()`/`hide()`/`toggle()`; seed with `default-open`.
|
|
51
|
+
* Parts: `header` · `back` · `title` · `body`.
|
|
52
|
+
*/
|
|
53
|
+
defineWebComponent<Props, Events>('kai-screen', {
|
|
54
|
+
open: undefined,
|
|
55
|
+
defaultOpen: undefined,
|
|
56
|
+
headline: undefined,
|
|
57
|
+
back: undefined,
|
|
58
|
+
noInert: undefined,
|
|
59
|
+
}, (props, ctx) => {
|
|
60
|
+
const { flag, element, dispatch, expose } = ctx;
|
|
61
|
+
let api: ScreenController | undefined;
|
|
62
|
+
let surface: HTMLElement | undefined;
|
|
63
|
+
|
|
64
|
+
// The standard disclosure surface: settable+reflecting `open`, kai-open-change,
|
|
65
|
+
// show/hide/toggle. See ./disclosure. This is the SOLE emitter of kai-open-change.
|
|
66
|
+
wireDisclosure(ctx, () => api, () => props.open);
|
|
67
|
+
|
|
68
|
+
// focus() shadows the host's native focus so it targets the screen surface
|
|
69
|
+
// inside the shadow root (the WebAwesome/Shoelace convention).
|
|
70
|
+
expose({
|
|
71
|
+
/** Move focus to the screen surface (no-op while closed). */
|
|
72
|
+
focus: (options?: FocusOptions) => surface?.focus(options),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Screen
|
|
77
|
+
defaultOpen={flag('defaultOpen')}
|
|
78
|
+
back={!(props.back === false || element.getAttribute('back') === 'false')}
|
|
79
|
+
noInert={flag('noInert')}
|
|
80
|
+
host={() => element}
|
|
81
|
+
titleSlot={<slot name="title">{props.headline as string | undefined}</slot>}
|
|
82
|
+
actions={<slot name="actions" />}
|
|
83
|
+
onBack={() => { dispatch('kai-back', {}); }}
|
|
84
|
+
controllerRef={(a) => (api = a)}
|
|
85
|
+
surfaceRef={(el) => (surface = el)}
|
|
86
|
+
>
|
|
87
|
+
<slot />
|
|
88
|
+
</Screen>
|
|
89
|
+
);
|
|
90
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ScrollArea, type ScrollOrientation } from '../ui/scroll-area';
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
|
+
|
|
4
|
+
interface Props extends Record<string, unknown> {
|
|
5
|
+
/** Which axis scrolls. `vertical` (default) · `horizontal` · `both`. The cross
|
|
6
|
+
* axis is clamped so content can't overflow it. */
|
|
7
|
+
orientation?: ScrollOrientation;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `<kai-scroll-area>` — a scroll container with a themed, thin, cross-browser
|
|
12
|
+
* scrollbar and a keyboard-reachable region. Project the scrollable content as
|
|
13
|
+
* light-DOM children and give the element a bounded size in your layout; the
|
|
14
|
+
* content then scrolls inside. Use `orientation` to pick the axis. Restyle the
|
|
15
|
+
* container via `::part(viewport)`; the scrollbar follows the
|
|
16
|
+
* `--color-scrollbar-thumb` token.
|
|
17
|
+
*
|
|
18
|
+
* ```html
|
|
19
|
+
* <kai-scroll-area style="height: 16rem">…long content…</kai-scroll-area>
|
|
20
|
+
* <kai-scroll-area orientation="horizontal" style="max-width: 24rem">…wide row…</kai-scroll-area>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
defineWebComponent<Props>('kai-scroll-area', {
|
|
24
|
+
orientation: 'vertical',
|
|
25
|
+
}, (props) => (
|
|
26
|
+
<>
|
|
27
|
+
{/* The element is a block box; the consumer sets its size, the viewport fills it. */}
|
|
28
|
+
<style>{':host{display:block;min-height:0}'}</style>
|
|
29
|
+
<ScrollArea class="h-full max-h-full w-full max-w-full" part="viewport" orientation={props.orientation as ScrollOrientation}>
|
|
30
|
+
<slot />
|
|
31
|
+
</ScrollArea>
|
|
32
|
+
</>
|
|
33
|
+
));
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createEffect, createSignal, untrack, Show } from 'solid-js';
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
|
+
import { Input } from '../ui/input';
|
|
4
|
+
import { Kbd } from '../ui/kbd';
|
|
5
|
+
import { Loader } from '../components/loader';
|
|
6
|
+
import { renderIcon } from '../ui/icon';
|
|
7
|
+
|
|
8
|
+
interface Props extends Record<string, unknown> {
|
|
9
|
+
/** Controlled query — settable and reflected to the `value` attribute. Read
|
|
10
|
+
* `el.value` for live state. */
|
|
11
|
+
value?: string;
|
|
12
|
+
/** Placeholder. Defaults to `Search…`. */
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
/** Leading icon-NAME string (a curated name, URL, or text), resolved to a glyph
|
|
15
|
+
* the same way `kai-button`'s `icon` is. Defaults to `search`. */
|
|
16
|
+
icon?: string;
|
|
17
|
+
/** Debounce window for `kai-search`, in ms. Defaults to `200`. */
|
|
18
|
+
debounce?: number;
|
|
19
|
+
/** Show a spinner in place of the leading icon while results load. */
|
|
20
|
+
loading?: boolean;
|
|
21
|
+
/** Optional shortcut hint shown (as a `kai-kbd`) while the field is empty,
|
|
22
|
+
* e.g. `Mod+K`. Display only; it does not bind the key. */
|
|
23
|
+
shortcut?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Events fired by `<kai-search>`. */
|
|
27
|
+
interface Events {
|
|
28
|
+
/** The query changed (debounced live, and on clear). */
|
|
29
|
+
'kai-search': { value: string };
|
|
30
|
+
/** Enter was pressed. */
|
|
31
|
+
'kai-submit': { value: string };
|
|
32
|
+
/** The field committed (blur). */
|
|
33
|
+
'kai-change': { value: string };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* `<kai-search>` — a debounced filter field built on `kai-input`. Type to fire a
|
|
38
|
+
* debounced `kai-search`, press Enter for `kai-submit`. A clear (×) button shows
|
|
39
|
+
* once there's a value; an optional `shortcut` renders a `kai-kbd` hint while the
|
|
40
|
+
* field is empty.
|
|
41
|
+
*
|
|
42
|
+
* ```html
|
|
43
|
+
* <kai-search placeholder="Search docs" shortcut="Mod+K"></kai-search>
|
|
44
|
+
* <script type="module">
|
|
45
|
+
* import '@kitn.ai/ui/elements';
|
|
46
|
+
* const s = document.querySelector('kai-search');
|
|
47
|
+
* s.addEventListener('kai-search', (e) => filter(e.detail.value));
|
|
48
|
+
* s.addEventListener('kai-submit', (e) => go(e.detail.value));
|
|
49
|
+
* </script>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* Drive/read the query with the `value` property (reflected to the attribute).
|
|
53
|
+
* Restyle via `::part(field)`, `::part(input)`, and `::part(clear)`.
|
|
54
|
+
*/
|
|
55
|
+
defineWebComponent<Props, Events>('kai-search', {
|
|
56
|
+
value: undefined,
|
|
57
|
+
placeholder: 'Search…',
|
|
58
|
+
icon: 'search',
|
|
59
|
+
debounce: 200,
|
|
60
|
+
loading: false,
|
|
61
|
+
shortcut: undefined,
|
|
62
|
+
}, (props, ctx) => {
|
|
63
|
+
const { element, dispatch, flag, expose } = ctx;
|
|
64
|
+
|
|
65
|
+
// Lift the query into the facade and drive Input CONTROLLED so the host can read
|
|
66
|
+
// it (`el.value` / `:host([value])`) and set it after mount. Mirrors kai-segmented.
|
|
67
|
+
const [value, setValue] = createSignal(
|
|
68
|
+
(props.value as string | undefined) ?? element.getAttribute('value') ?? '',
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const coerce = (v: unknown): string =>
|
|
72
|
+
v == null ? (element.getAttribute('value') ?? '') : String(v);
|
|
73
|
+
|
|
74
|
+
// Reads return LIVE state; host writes (`el.value = …`) drive it WITHOUT firing
|
|
75
|
+
// events. The equality guard kills the reflect write-back loop.
|
|
76
|
+
Object.defineProperty(element, 'value', {
|
|
77
|
+
get: () => value(),
|
|
78
|
+
set: (v: unknown) => { const next = coerce(v); if (untrack(value) !== next) setValue(next); },
|
|
79
|
+
configurable: true,
|
|
80
|
+
enumerable: true,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Reflect internal value → `[value]` host attribute (for `:host([value])`).
|
|
84
|
+
createEffect(() => {
|
|
85
|
+
const v = value();
|
|
86
|
+
if (v) {
|
|
87
|
+
if (element.getAttribute('value') !== v) element.setAttribute('value', v);
|
|
88
|
+
} else if (element.hasAttribute('value')) {
|
|
89
|
+
element.removeAttribute('value');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Debounced kai-search: each keystroke resets the timer; it fires once after the
|
|
94
|
+
// window. Clearing fires immediately (timer cancelled).
|
|
95
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
96
|
+
const debounceMs = () => {
|
|
97
|
+
const ms = Number(props.debounce ?? 200);
|
|
98
|
+
return Number.isFinite(ms) && ms >= 0 ? ms : 200;
|
|
99
|
+
};
|
|
100
|
+
const scheduleSearch = (next: string) => {
|
|
101
|
+
if (timer) clearTimeout(timer);
|
|
102
|
+
timer = setTimeout(() => dispatch('kai-search', { value: next }), debounceMs());
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const getInput = (): HTMLInputElement | null =>
|
|
106
|
+
element.shadowRoot?.querySelector<HTMLInputElement>('input') ?? null;
|
|
107
|
+
|
|
108
|
+
const onInput = (next: string) => {
|
|
109
|
+
setValue(next);
|
|
110
|
+
scheduleSearch(next);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Empty the field, fire kai-search '' immediately (cancelling any pending), refocus.
|
|
114
|
+
const clear = () => {
|
|
115
|
+
if (timer) clearTimeout(timer);
|
|
116
|
+
setValue('');
|
|
117
|
+
dispatch('kai-search', { value: '' });
|
|
118
|
+
getInput()?.focus();
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const onKeyDown = (e: KeyboardEvent) => {
|
|
122
|
+
if (e.key === 'Enter') dispatch('kai-submit', { value: untrack(value) });
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
expose({
|
|
126
|
+
focus: () => getInput()?.focus(),
|
|
127
|
+
clear,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Leading: a spinner while loading, otherwise the resolved icon glyph.
|
|
131
|
+
const leading = (
|
|
132
|
+
<Show
|
|
133
|
+
when={flag('loading')}
|
|
134
|
+
fallback={renderIcon((props.icon as string) ?? 'search', {
|
|
135
|
+
class: 'size-4 shrink-0',
|
|
136
|
+
imgClass: 'size-4 shrink-0',
|
|
137
|
+
spanClass: 'inline-flex size-4 shrink-0 items-center justify-center',
|
|
138
|
+
ariaHidden: true,
|
|
139
|
+
})}
|
|
140
|
+
>
|
|
141
|
+
<Loader variant="circular" size="sm" />
|
|
142
|
+
</Show>
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// Trailing: a clear button once there's a value, else a shortcut hint if set.
|
|
146
|
+
const trailing = (
|
|
147
|
+
<>
|
|
148
|
+
<Show when={value()}>
|
|
149
|
+
<button
|
|
150
|
+
type="button"
|
|
151
|
+
part="clear"
|
|
152
|
+
aria-label="Clear"
|
|
153
|
+
class="inline-flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
154
|
+
onClick={clear}
|
|
155
|
+
>
|
|
156
|
+
{renderIcon('x', { class: 'size-4', imgClass: 'size-4' })}
|
|
157
|
+
</button>
|
|
158
|
+
</Show>
|
|
159
|
+
<Show when={!value() && props.shortcut}>
|
|
160
|
+
<Kbd keys={props.shortcut as string} size="sm" class="opacity-80" />
|
|
161
|
+
</Show>
|
|
162
|
+
</>
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<>
|
|
167
|
+
<style>{':host{display:block}'}</style>
|
|
168
|
+
<Input
|
|
169
|
+
type="search"
|
|
170
|
+
value={value()}
|
|
171
|
+
placeholder={(props.placeholder as string) ?? 'Search…'}
|
|
172
|
+
leading={leading}
|
|
173
|
+
trailing={trailing}
|
|
174
|
+
onValueInput={onInput}
|
|
175
|
+
onValueChange={(v) => dispatch('kai-change', { value: v })}
|
|
176
|
+
onKeyDown={onKeyDown}
|
|
177
|
+
/>
|
|
178
|
+
</>
|
|
179
|
+
);
|
|
180
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createEffect, createSignal, untrack } from 'solid-js';
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
|
+
import { Segmented, type SegmentedOption } from '../ui/segmented';
|
|
4
|
+
import { renderIcon } from '../ui/icon';
|
|
5
|
+
|
|
6
|
+
/** A single segment. `icon` is an icon-NAME string (web-component-friendly) —
|
|
7
|
+
* a curated name (e.g. `"code"`), a URL/data-URI, or plain text — resolved to a
|
|
8
|
+
* glyph via the kit's icon renderer (the same path `kai-button`'s `icon` uses). */
|
|
9
|
+
interface Option {
|
|
10
|
+
value: string;
|
|
11
|
+
label: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Props extends Record<string, unknown> {
|
|
16
|
+
/** The selectable segments, left to right. Set as a JS property (array). */
|
|
17
|
+
options: Option[];
|
|
18
|
+
/** Controlled selected `value` — settable and reflected to the `value`
|
|
19
|
+
* attribute. `el.value = 'preview'` drives it; choosing a segment updates it
|
|
20
|
+
* and fires `kai-change`. Read `el.value` for live state. */
|
|
21
|
+
value?: string;
|
|
22
|
+
/** Control density: `sm` or `md`. Defaults to `md`. */
|
|
23
|
+
size?: 'sm' | 'md';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Events fired by `<kai-segmented>`. */
|
|
27
|
+
interface Events {
|
|
28
|
+
/** A segment was chosen. */
|
|
29
|
+
'kai-change': { value: string };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `<kai-segmented>` — a single-select pill track (segmented / toggle group).
|
|
34
|
+
* Feed it `options` (a JS-property array), drive/read the selection with the
|
|
35
|
+
* `value` property (settable + reflected to the `value` attribute, so
|
|
36
|
+
* `:host([value])` and `el.value` see live state), and listen for `kai-change`.
|
|
37
|
+
*
|
|
38
|
+
* ```html
|
|
39
|
+
* <kai-segmented value="preview"></kai-segmented>
|
|
40
|
+
* <script type="module">
|
|
41
|
+
* import '@kitn.ai/ui/elements';
|
|
42
|
+
* const seg = document.querySelector('kai-segmented');
|
|
43
|
+
* seg.options = [
|
|
44
|
+
* { value: 'code', label: 'Code', icon: 'code' },
|
|
45
|
+
* { value: 'preview', label: 'Preview', icon: 'monitor' },
|
|
46
|
+
* ];
|
|
47
|
+
* seg.addEventListener('kai-change', (e) => console.log(e.detail.value));
|
|
48
|
+
* seg.value = 'code'; // drive it (no kai-change — the host already knows)
|
|
49
|
+
* </script>
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* Each option's `icon` is an icon-name string resolved to a glyph (curated name,
|
|
53
|
+
* URL, or text). Restyle via `::part(track)` and `::part(segment)`.
|
|
54
|
+
*/
|
|
55
|
+
defineWebComponent<Props, Events>('kai-segmented', {
|
|
56
|
+
options: [],
|
|
57
|
+
value: undefined,
|
|
58
|
+
size: 'md',
|
|
59
|
+
}, (props, ctx) => {
|
|
60
|
+
const { element, dispatch } = ctx;
|
|
61
|
+
|
|
62
|
+
// Lift the selected value into the facade and drive Segmented CONTROLLED so the
|
|
63
|
+
// host can read it (`el.value` / `:host([value])`) and set it after mount. Seed
|
|
64
|
+
// from the `value` property/attribute present on mount (options usually arrive
|
|
65
|
+
// as a property after upgrade, so we don't auto-select the first segment here).
|
|
66
|
+
const [value, setValue] = createSignal(
|
|
67
|
+
(props.value as string | undefined) ?? element.getAttribute('value') ?? '',
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Coerce any value coming through the `value` setter (a JS assignment, or the
|
|
71
|
+
// write-back from `attributeChangedCallback`) to a string — falling back to the
|
|
72
|
+
// live attribute when it's nullish, so the reflect write-back equals the signal
|
|
73
|
+
// and the equality guards below absorb it (no attr⇄prop feedback loop; mirrors
|
|
74
|
+
// kai-switch's `checked`).
|
|
75
|
+
const coerce = (v: unknown): string =>
|
|
76
|
+
v == null ? (element.getAttribute('value') ?? '') : String(v);
|
|
77
|
+
|
|
78
|
+
// Apply a new value and fire kai-change once (the user-choice path).
|
|
79
|
+
const apply = (next: string) => {
|
|
80
|
+
if (untrack(value) === next) return;
|
|
81
|
+
setValue(next);
|
|
82
|
+
dispatch('kai-change', { value: next });
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Override the `value` property so reads return LIVE state (the signal) and host
|
|
86
|
+
// writes (`el.value = …`) drive it WITHOUT firing kai-change — distinct from the
|
|
87
|
+
// user-choice path. The equality guard kills the reflect write-back loop.
|
|
88
|
+
Object.defineProperty(element, 'value', {
|
|
89
|
+
get: () => value(),
|
|
90
|
+
set: (v: unknown) => { const next = coerce(v); if (untrack(value) !== next) setValue(next); },
|
|
91
|
+
configurable: true,
|
|
92
|
+
enumerable: true,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Reflect internal value → the `[value]` host attribute (for `:host([value])`).
|
|
96
|
+
// The guard against the live attribute keeps the write-back the reflect triggers
|
|
97
|
+
// (attributeChangedCallback → setter) from looping.
|
|
98
|
+
createEffect(() => {
|
|
99
|
+
const v = value();
|
|
100
|
+
if (v) {
|
|
101
|
+
if (element.getAttribute('value') !== v) element.setAttribute('value', v);
|
|
102
|
+
} else if (element.hasAttribute('value')) {
|
|
103
|
+
element.removeAttribute('value');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Resolve each option's icon-NAME string into a rendered glyph, the same path
|
|
108
|
+
// kai-button uses for its `icon` prop, and hand the primitive `{value,label,icon:JSX}`.
|
|
109
|
+
const resolved = (): SegmentedOption[] =>
|
|
110
|
+
(props.options ?? []).map((o) => ({
|
|
111
|
+
value: o.value,
|
|
112
|
+
label: o.label,
|
|
113
|
+
icon: o.icon
|
|
114
|
+
? renderIcon(o.icon, {
|
|
115
|
+
class: 'size-4 shrink-0',
|
|
116
|
+
imgClass: 'size-4 shrink-0',
|
|
117
|
+
spanClass: 'inline-flex size-4 shrink-0 items-center justify-center',
|
|
118
|
+
ariaHidden: true,
|
|
119
|
+
})
|
|
120
|
+
: undefined,
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<>
|
|
125
|
+
{/* Base sets `:host{display:block}`; the track flows inline like a pill. */}
|
|
126
|
+
<style>{':host{display:inline-flex}'}</style>
|
|
127
|
+
<Segmented
|
|
128
|
+
options={resolved()}
|
|
129
|
+
value={value()}
|
|
130
|
+
size={(props.size as 'sm' | 'md' | undefined) ?? 'md'}
|
|
131
|
+
onChange={(next) => apply(next)}
|
|
132
|
+
/>
|
|
133
|
+
</>
|
|
134
|
+
);
|
|
135
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Separator } from '../ui/separator';
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
|
+
|
|
4
|
+
interface Props extends Record<string, unknown> {
|
|
5
|
+
/** `horizontal` (default, block + full-width) or `vertical` (a rule inside a
|
|
6
|
+
* flex/grid row — it stretches to the row height). */
|
|
7
|
+
orientation?: 'horizontal' | 'vertical';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `<kai-separator>` — a themed divider between groups of content (toolbar
|
|
12
|
+
* sections, menu groups, header/sidebar splits). Carries `role="separator"`.
|
|
13
|
+
* Restyle via `::part(separator)`.
|
|
14
|
+
*
|
|
15
|
+
* ```html
|
|
16
|
+
* <kai-separator></kai-separator>
|
|
17
|
+
* <div style="display:flex; gap:.5rem">
|
|
18
|
+
* <span>A</span><kai-separator orientation="vertical"></kai-separator><span>B</span>
|
|
19
|
+
* </div>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
defineWebComponent<Props>('kai-separator', {
|
|
23
|
+
orientation: 'horizontal',
|
|
24
|
+
}, (props) => {
|
|
25
|
+
const vertical = () => props.orientation === 'vertical';
|
|
26
|
+
return (
|
|
27
|
+
<>
|
|
28
|
+
{/* The host owns its layout box: a horizontal rule is block + full-width;
|
|
29
|
+
a vertical rule is an inline-block flex item that stretches to the row. */}
|
|
30
|
+
<style>{vertical()
|
|
31
|
+
? ':host{display:inline-block;align-self:stretch}'
|
|
32
|
+
: ':host{display:block}'}</style>
|
|
33
|
+
<Separator orientation={vertical() ? 'vertical' : 'horizontal'} part="separator" />
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createSignal, onCleanup, onMount } from 'solid-js';
|
|
2
|
+
import { SettingItem } from '../ui/settings-group';
|
|
3
|
+
import { defineWebComponent } from './define';
|
|
4
|
+
|
|
5
|
+
interface Props extends Record<string, unknown> {
|
|
6
|
+
/** Row label (primary text). Attribute: `label`. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** Optional secondary description under the label. Attribute: `description`. */
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `<kai-setting-item>` — one row inside a `<kai-settings-group>`: a left
|
|
14
|
+
* label/description block and an optional right-aligned control. Slot the control
|
|
15
|
+
* (a `<kai-switch>`, segmented control, select, …) into `slot="control"`; omit it
|
|
16
|
+
* for a plain label row.
|
|
17
|
+
*
|
|
18
|
+
* Themeable `::part`s: `label` (the label/description block) and `control`.
|
|
19
|
+
*
|
|
20
|
+
* ```html
|
|
21
|
+
* <kai-setting-item label="Reduce motion" description="Minimize animations.">
|
|
22
|
+
* <kai-switch slot="control"></kai-switch>
|
|
23
|
+
* </kai-setting-item>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
defineWebComponent<Props>('kai-setting-item', {
|
|
27
|
+
label: '',
|
|
28
|
+
description: undefined,
|
|
29
|
+
}, (props, { element }) => {
|
|
30
|
+
// The control region only renders when `slot="control"` is actually filled, so
|
|
31
|
+
// a plain label row has no stray right-side wrapper. An empty `<slot>` is always
|
|
32
|
+
// a truthy node, so we track occupancy and gate on it (the kai-card approach).
|
|
33
|
+
// Re-read on child/attribute mutations so a late-slotted control lights up.
|
|
34
|
+
const [hasControl, setHasControl] = createSignal(false);
|
|
35
|
+
onMount(() => {
|
|
36
|
+
const read = () => setHasControl(!!element.querySelector(':scope > [slot="control"]'));
|
|
37
|
+
read();
|
|
38
|
+
const observer = new MutationObserver(read);
|
|
39
|
+
observer.observe(element, { childList: true, attributes: true, subtree: true });
|
|
40
|
+
onCleanup(() => observer.disconnect());
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<SettingItem
|
|
45
|
+
label={props.label ?? ''}
|
|
46
|
+
description={props.description}
|
|
47
|
+
control={hasControl() ? <slot name="control" /> : undefined}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
});
|