@kitn.ai/ui 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -5
- package/dist/components/artifact.d.ts +39 -1
- package/dist/components/attachment-types.d.ts +12 -0
- package/dist/components/attachments.d.ts +2 -10
- package/dist/components/card.d.ts +27 -0
- package/dist/components/chain-of-thought.d.ts +78 -2
- package/dist/components/chat-scope-picker.d.ts +7 -0
- package/dist/components/chat-thread.d.ts +37 -12
- package/dist/components/choice-card.d.ts +26 -0
- package/dist/components/coachmark.d.ts +49 -0
- package/dist/components/composer-dom.d.ts +35 -0
- package/dist/components/composer-highlight.d.ts +48 -0
- package/dist/components/composer-history.d.ts +34 -0
- package/dist/components/composer.d.ts +86 -0
- package/dist/components/confirm-card.d.ts +17 -0
- package/dist/components/conversation-item.d.ts +9 -0
- package/dist/components/conversation-list.d.ts +36 -1
- package/dist/components/file-tree.d.ts +15 -0
- package/dist/components/form.d.ts +34 -0
- package/dist/components/markdown.d.ts +3 -0
- package/dist/components/message.d.ts +12 -0
- package/dist/components/model-switcher.d.ts +7 -0
- package/dist/components/prompt-input.d.ts +13 -4
- package/dist/components/prompt-suggestion.d.ts +9 -1
- package/dist/components/reasoning.d.ts +14 -1
- package/dist/components/response-compare.d.ts +14 -0
- package/dist/components/screen.d.ts +46 -0
- package/dist/components/tasks-card.d.ts +49 -1
- package/dist/components/toast.d.ts +12 -2
- package/dist/components/tool-types.d.ts +10 -0
- package/dist/components/tool.d.ts +8 -8
- package/dist/components/voice-input.d.ts +27 -0
- package/dist/components/voice-output.d.ts +33 -0
- package/dist/core-C8fzo39E.js +12 -0
- package/dist/custom-elements.json +8392 -1860
- package/dist/elements/agent-card.d.ts +1 -0
- package/dist/elements/agent-card.js +1 -0
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/autoloader.js +1 -1
- package/dist/elements/avatar.d.ts +1 -0
- package/dist/elements/avatar.js +1 -0
- package/dist/elements/badge.d.ts +1 -0
- package/dist/elements/badge.js +1 -0
- package/dist/elements/button.d.ts +1 -0
- package/dist/elements/button.js +1 -0
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-types.d.ts +47 -0
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/Icon-DLLBeLG5.js +1 -0
- package/dist/elements/chunks/action-icons-UB4V5FNq.js +1 -0
- package/dist/elements/chunks/arrow-left-C-gwlUmx.js +1 -0
- package/dist/elements/chunks/attachments-CgbErf1r.js +1 -0
- package/dist/elements/chunks/badge-DTQpgB6N.js +1 -0
- package/dist/elements/chunks/button-XDUM6lXs.js +1 -0
- package/dist/elements/chunks/card-CBlFZ-7I.js +1 -0
- package/dist/elements/chunks/chat-thread-BIhmqwNL.js +1 -0
- package/dist/elements/chunks/check-hGUoVIKb.js +1 -0
- package/dist/elements/chunks/chevron-down-DDoaSUtO.js +1 -0
- package/dist/elements/chunks/chevron-right-CdGEUakB.js +1 -0
- package/dist/elements/chunks/circle-BKAZdmzb.js +1 -0
- package/dist/elements/chunks/circle-check-CmYG-y3I.js +1 -0
- package/dist/elements/chunks/circle-x-DewgAEym.js +1 -0
- package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
- package/dist/elements/chunks/code-block-DedwxwFI.js +1 -0
- package/dist/elements/chunks/collapsible-FfgmTHtM.js +1 -0
- package/dist/elements/chunks/composer-Cfbw5HKT.js +65 -0
- package/dist/elements/chunks/context-DwJmxlRE.js +1 -0
- package/dist/elements/chunks/conversation-list-DsCQMfPq.js +1 -0
- package/dist/elements/chunks/default-input-BuqsoENC.js +1 -0
- package/dist/elements/chunks/define-rGsaWsLL.js +1 -0
- package/dist/elements/chunks/disclosure-CwoAkpMH.js +1 -0
- package/dist/elements/chunks/download-DJ06G3Bz.js +1 -0
- package/dist/elements/chunks/dropdown-BFg8ri_R.js +1 -0
- package/dist/elements/chunks/ellipsis-dlN6YvGw.js +1 -0
- package/dist/elements/chunks/external-link-DBX2lc_R.js +1 -0
- package/dist/elements/chunks/file-text-HfwUx4F9.js +1 -0
- package/dist/elements/chunks/file-tree-C6Toj71u.js +1 -0
- package/dist/elements/chunks/folder-MYuqa9Xp.js +1 -0
- package/dist/elements/chunks/hover-card-gE7iyXdW.js +1 -0
- package/dist/elements/chunks/icon-Vqz4ivrY.js +1 -0
- package/dist/elements/chunks/{index-CTBEJeo6.js → index-BCRhi84a.js} +1 -1
- package/dist/elements/chunks/info-B_toFS0l.js +1 -0
- package/dist/elements/chunks/input-gzbHYZvQ.js +1 -0
- package/dist/elements/chunks/kbd-n49c4KZm.js +1 -0
- package/dist/elements/chunks/link-CKPs9yM0.js +1 -0
- package/dist/elements/chunks/loader-q6MamGus.js +1 -0
- package/dist/elements/chunks/{markdown-CMTGx9r8.js → markdown-DBrcqq4S.js} +9 -9
- package/dist/elements/chunks/message-CTqaYI0s.js +1 -0
- package/dist/elements/chunks/message-feedback-Cvzm03w0.js +1 -0
- package/dist/elements/chunks/message-square-CUg9eRCi.js +1 -0
- package/dist/elements/chunks/minimize-2-l2klxP6K.js +1 -0
- package/dist/elements/chunks/model-switcher-CmyjiHO1.js +1 -0
- package/dist/elements/chunks/overlay-C8sOYqjT.js +1 -0
- package/dist/elements/chunks/paperclip-42Ia7xHD.js +1 -0
- package/dist/elements/chunks/progress-bar-tzKMWAwO.js +1 -0
- package/dist/elements/chunks/prompt-suggestion-D3F6vP_A.js +1 -0
- package/dist/elements/chunks/reasoning-BJNmmyov.js +1 -0
- package/dist/elements/chunks/resizable-BRsk19qI.js +1 -0
- package/dist/elements/chunks/rotate-cw-BgcggLHC.js +1 -0
- package/dist/elements/chunks/scroll-area-CgGbFFbT.js +1 -0
- package/dist/elements/chunks/separator-8hM4GdHE.js +1 -0
- package/dist/elements/chunks/settings-CaUIYOg-.js +1 -0
- package/dist/elements/chunks/settings-group-CYjOVIZQ.js +1 -0
- package/dist/elements/chunks/share-BTYM3Ddt.js +1 -0
- package/dist/elements/chunks/skeleton-DsTYrPUe.js +1 -0
- package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
- package/dist/elements/chunks/star-9R3LBQ9n.js +1 -0
- package/dist/elements/chunks/store-CYl5Y0_u.js +1 -0
- package/dist/elements/chunks/text-shimmer-CaYGv8Se.js +1 -0
- package/dist/elements/chunks/textarea-NEr2SLcD.js +1 -0
- package/dist/elements/chunks/thumbs-up-DZYZkFfc.js +1 -0
- package/dist/elements/chunks/toast-store-ClxVZRcc.js +1 -0
- package/dist/elements/chunks/tool-CHZOmoe9.js +1 -0
- package/dist/elements/chunks/tooltip-WpHElELC.js +1 -0
- package/dist/elements/chunks/triangle-alert-BlS80PUg.js +1 -0
- package/dist/elements/chunks/use-card-resolution-VSVoxDeJ.js +1 -0
- package/dist/elements/chunks/video-DxTPZqT8.js +1 -0
- package/dist/elements/chunks/x-C1qtndxn.js +1 -0
- package/dist/elements/coachmark.d.ts +1 -0
- package/dist/elements/coachmark.js +1 -0
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.d.ts +1 -0
- package/dist/elements/command.js +1 -0
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.d.ts +1 -0
- package/dist/elements/composer.js +1 -0
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/css.d.ts +1 -0
- package/dist/elements/default-input.d.ts +51 -0
- package/dist/elements/define.d.ts +53 -0
- package/dist/elements/dialog.d.ts +1 -0
- package/dist/elements/dialog.js +1 -0
- package/dist/elements/disclosure.d.ts +37 -0
- package/dist/elements/editable-label.d.ts +1 -0
- package/dist/elements/editable-label.js +1 -0
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.d.ts +1 -0
- package/dist/elements/hover-card.js +1 -0
- package/dist/elements/icon.d.ts +1 -0
- package/dist/elements/icon.js +1 -0
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.d.ts +1 -0
- package/dist/elements/input.js +1 -0
- package/dist/elements/kbd.d.ts +1 -0
- package/dist/elements/kbd.js +1 -0
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.d.ts +1 -0
- package/dist/elements/menu.js +1 -0
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.d.ts +1 -0
- package/dist/elements/nav.js +1 -0
- package/dist/elements/notice.d.ts +1 -0
- package/dist/elements/notice.js +1 -0
- package/dist/elements/pane-group.d.ts +1 -0
- package/dist/elements/pane-group.js +1 -0
- package/dist/elements/pane.d.ts +1 -0
- package/dist/elements/pane.js +1 -0
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.d.ts +1 -0
- package/dist/elements/progress-bar.js +1 -0
- package/dist/elements/prompt-dock.d.ts +1 -0
- package/dist/elements/prompt-dock.js +1 -0
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/register-impl.d.ts +0 -0
- package/dist/elements/register.d.ts +13 -0
- package/dist/elements/remote.d.ts +1 -0
- package/dist/elements/remote.js +1 -0
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +2 -2
- package/dist/elements/screen.d.ts +1 -0
- package/dist/elements/screen.js +1 -0
- package/dist/elements/scroll-area.d.ts +1 -0
- package/dist/elements/scroll-area.js +1 -0
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.d.ts +1 -0
- package/dist/elements/search.js +1 -0
- package/dist/elements/segmented.d.ts +1 -0
- package/dist/elements/segmented.js +1 -0
- package/dist/elements/separator.d.ts +1 -0
- package/dist/elements/separator.js +1 -0
- package/dist/elements/setting-item.d.ts +1 -0
- package/dist/elements/setting-item.js +1 -0
- package/dist/elements/settings-group.d.ts +1 -0
- package/dist/elements/settings-group.js +1 -0
- package/dist/elements/skeleton.d.ts +1 -0
- package/dist/elements/skeleton.js +1 -0
- package/dist/elements/slots.d.ts +176 -0
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.d.ts +1 -0
- package/dist/elements/status.js +1 -0
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.d.ts +1 -0
- package/dist/elements/tabs.js +1 -0
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.d.ts +1 -0
- package/dist/elements/tooltip.js +1 -0
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.d.ts +1 -0
- package/dist/elements/voice-output.js +1 -0
- package/dist/elements.d.ts +733 -58
- package/dist/engine-javascript-C1x7zo1_.js +141 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +8256 -5703
- package/dist/kai.es.js +1 -0
- package/dist/llms/llms-full.txt +1146 -64
- package/dist/llms/llms.txt +4 -4
- package/dist/mcp.es.js +62 -7
- package/dist/primitives/composer-model.d.ts +22 -0
- package/dist/primitives/composer-triggers.d.ts +19 -0
- package/dist/primitives/create-kai-chat.d.ts +38 -0
- package/dist/primitives/toast-store.d.ts +32 -2
- package/dist/primitives/use-speech-recognition.d.ts +26 -0
- package/dist/react/index.d.ts +1085 -97
- package/dist/react/runtime.d.ts +9 -2
- package/dist/react/use-kai-chat.d.ts +41 -0
- package/dist/react.js +572 -193
- package/dist/register-impl-CYWGkgbI.js +141 -0
- package/dist/schemas/tasks.schema.json +2 -2
- package/dist/state/index.d.ts +4 -0
- package/dist/state/messages.d.ts +11 -0
- package/dist/state/stream.d.ts +22 -0
- package/dist/state/suggestions.d.ts +4 -0
- package/dist/state.js +117 -0
- package/dist/theme.tokens.css +87 -0
- package/dist/types.d.ts +4 -0
- package/dist/ui/agent-card.d.ts +61 -0
- package/dist/ui/button.d.ts +5 -2
- package/dist/ui/card.d.ts +61 -0
- package/dist/ui/collapsible.d.ts +15 -1
- package/dist/ui/command.d.ts +33 -0
- package/dist/ui/dialog.d.ts +42 -0
- package/dist/ui/dropdown.d.ts +95 -1
- package/dist/ui/editable-label.d.ts +26 -0
- package/dist/ui/hover-card.d.ts +13 -1
- package/dist/ui/icon.d.ts +18 -0
- package/dist/ui/input.d.ts +33 -0
- package/dist/ui/kbd.d.ts +21 -0
- package/dist/ui/nav.d.ts +62 -0
- package/dist/ui/notice.d.ts +31 -0
- package/dist/ui/overlay.d.ts +12 -0
- package/dist/ui/pane-grid.d.ts +58 -0
- package/dist/ui/pane-group.d.ts +75 -0
- package/dist/ui/pane.d.ts +78 -0
- package/dist/ui/popover.d.ts +11 -1
- package/dist/ui/progress-bar.d.ts +23 -0
- package/dist/ui/prompt-dock.d.ts +96 -0
- package/dist/ui/resizable.d.ts +12 -0
- package/dist/ui/scroll-area.d.ts +4 -0
- package/dist/ui/segmented.d.ts +28 -0
- package/dist/ui/settings-group.d.ts +32 -0
- package/dist/ui/skeleton.d.ts +12 -0
- package/dist/ui/stat.d.ts +16 -0
- package/dist/ui/status.d.ts +13 -0
- package/dist/ui/switch.d.ts +8 -1
- package/dist/ui/tabs.d.ts +35 -0
- package/dist/ui/tooltip.d.ts +19 -1
- package/frameworks/react/index.tsx +1042 -157
- package/frameworks/react/runtime.tsx +30 -1
- package/frameworks/react/use-kai-chat.tsx +87 -0
- package/llms-full.txt +1146 -64
- package/llms.txt +4 -4
- package/package.json +23 -8
- package/src/agent-tooling/mcp/manifest.ts +14 -0
- package/src/agent-tooling/mcp/tools/debug.ts +10 -6
- package/src/agent-tooling/mcp/tools/reference.ts +32 -0
- package/src/agent-tooling/mcp/tools/scaffold.ts +30 -1
- package/src/components/artifact.tsx +98 -13
- package/src/components/attachment-types.ts +13 -0
- package/src/components/attachments.tsx +4 -14
- package/src/components/card.tsx +109 -15
- package/src/components/chain-of-thought.tsx +210 -1
- package/src/components/chat-scope-picker.tsx +13 -3
- package/src/components/chat-thread.tsx +225 -116
- package/src/components/choice-card.tsx +103 -11
- package/src/components/coachmark.tsx +245 -0
- package/src/components/component-meta.json +691 -102
- package/src/components/composer-dom.ts +159 -0
- package/src/components/composer-highlight.ts +242 -0
- package/src/components/composer-history.ts +82 -0
- package/src/components/composer.tsx +979 -0
- package/src/components/confirm-card.tsx +54 -1
- package/src/components/conversation-item.tsx +69 -7
- package/src/components/conversation-list.tsx +134 -37
- package/src/components/file-tree.tsx +197 -47
- package/src/components/form-widgets.tsx +9 -7
- package/src/components/form.tsx +140 -17
- package/src/components/image.tsx +3 -5
- package/src/components/link-preview.tsx +5 -4
- package/src/components/markdown.tsx +5 -2
- package/src/components/message.tsx +23 -4
- package/src/components/model-switcher.tsx +19 -4
- package/src/components/prompt-input.tsx +46 -75
- package/src/components/prompt-suggestion.tsx +48 -16
- package/src/components/reasoning.tsx +29 -6
- package/src/components/response-compare.tsx +31 -2
- package/src/components/screen.tsx +176 -0
- package/src/components/tasks-card.tsx +342 -21
- package/src/components/toast.tsx +119 -31
- package/src/components/tool-types.ts +10 -0
- package/src/components/tool.tsx +22 -16
- package/src/components/voice-input.tsx +102 -19
- package/src/components/voice-output.tsx +202 -0
- package/src/elements/agent-card.tsx +67 -0
- package/src/elements/artifact.tsx +53 -4
- package/src/elements/attachments.tsx +1 -0
- package/src/elements/avatar.tsx +33 -0
- package/src/elements/badge.tsx +32 -0
- package/src/elements/button.tsx +126 -0
- package/src/elements/card-media.jpg +0 -0
- package/src/elements/card.tsx +124 -31
- package/src/elements/cards.tsx +81 -7
- package/src/elements/chain-of-thought.tsx +77 -32
- package/src/elements/chat-scope-picker.tsx +35 -8
- package/src/elements/chat-types.ts +2 -2
- package/src/elements/chat-workspace.tsx +153 -41
- package/src/elements/chat.tsx +45 -23
- package/src/elements/choice.tsx +71 -19
- package/src/elements/coachmark.tsx +90 -0
- package/src/elements/command.tsx +209 -0
- package/src/elements/compare.tsx +24 -2
- package/src/elements/compiled.css +1 -1
- package/src/elements/composer.tsx +151 -0
- package/src/elements/confirm-card.tsx +36 -11
- package/src/elements/conversation-list.tsx +80 -11
- package/src/elements/default-input.tsx +75 -53
- package/src/elements/define.tsx +47 -6
- package/src/elements/dialog.tsx +98 -0
- package/src/elements/disclosure.ts +73 -0
- package/src/elements/editable-label.tsx +125 -0
- package/src/elements/element-manifest.json +129 -1
- package/src/elements/element-meta.json +5562 -1336
- package/src/elements/element-types.d.ts +732 -57
- package/src/elements/file-tree.tsx +12 -2
- package/src/elements/file-upload.tsx +1 -1
- package/src/elements/form.tsx +54 -11
- package/src/elements/hover-card.tsx +80 -0
- package/src/elements/icon-names.json +48 -0
- package/src/elements/icon.tsx +41 -0
- package/src/elements/input.tsx +203 -0
- package/src/elements/kbd.tsx +45 -0
- package/src/elements/menu.tsx +217 -0
- package/src/elements/message.tsx +58 -11
- package/src/elements/model-switcher.tsx +27 -1
- package/src/elements/nav.tsx +87 -0
- package/src/elements/notice.tsx +53 -0
- package/src/elements/pane-group.tsx +119 -0
- package/src/elements/pane.tsx +129 -0
- package/src/elements/popover.tsx +39 -17
- package/src/elements/progress-bar.tsx +39 -0
- package/src/elements/prompt-dock.tsx +81 -0
- package/src/elements/prompt-input.tsx +112 -63
- package/src/elements/prompt-suggestions.tsx +25 -9
- package/src/elements/reasoning.tsx +30 -9
- package/src/elements/register-impl.ts +38 -3
- package/src/elements/register.ts +11 -3
- package/src/elements/resizable.tsx +48 -11
- package/src/elements/screen.tsx +90 -0
- package/src/elements/scroll-area.tsx +33 -0
- package/src/elements/search.tsx +180 -0
- package/src/elements/segmented.tsx +135 -0
- package/src/elements/separator.tsx +36 -0
- package/src/elements/setting-item.tsx +50 -0
- package/src/elements/settings-group.tsx +37 -0
- package/src/elements/skeleton.tsx +45 -0
- package/src/elements/slots.ts +607 -0
- package/src/elements/source.tsx +13 -6
- package/src/elements/status.tsx +42 -0
- package/src/elements/switch.tsx +89 -14
- package/src/elements/tabs.tsx +95 -0
- package/src/elements/tasks.tsx +70 -14
- package/src/elements/toast.tsx +17 -2
- package/src/elements/tool.tsx +43 -9
- package/src/elements/tooltip.tsx +85 -0
- package/src/elements/video-poster.jpg +0 -0
- package/src/elements/voice-input.tsx +66 -16
- package/src/elements/voice-output.tsx +69 -0
- package/src/index.ts +5 -2
- package/src/primitives/card-schemas/tasks.schema.json +2 -2
- package/src/primitives/composer-model.ts +49 -0
- package/src/primitives/composer-triggers.ts +35 -0
- package/src/primitives/create-kai-chat.ts +66 -0
- package/src/primitives/toast-store.ts +43 -2
- package/src/primitives/use-speech-recognition.ts +135 -0
- package/src/state/index.ts +6 -0
- package/src/state/messages.ts +36 -0
- package/src/state/stream.ts +92 -0
- package/src/state/suggestions.ts +9 -0
- package/src/types.ts +4 -0
- package/src/ui/agent-card.tsx +212 -0
- package/src/ui/button.tsx +16 -5
- package/src/ui/card.tsx +245 -0
- package/src/ui/collapsible.tsx +30 -4
- package/src/ui/command.tsx +99 -0
- package/src/ui/dialog.tsx +217 -0
- package/src/ui/dropdown.tsx +375 -13
- package/src/ui/editable-label.tsx +112 -0
- package/src/ui/hover-card.tsx +28 -5
- package/src/ui/icon.tsx +110 -0
- package/src/ui/input.tsx +132 -0
- package/src/ui/kbd.tsx +118 -0
- package/src/ui/nav.tsx +282 -0
- package/src/ui/notice.tsx +84 -0
- package/src/ui/overlay.tsx +108 -2
- package/src/ui/pane-grid.tsx +116 -0
- package/src/ui/pane-group.tsx +280 -0
- package/src/ui/pane.tsx +193 -0
- package/src/ui/popover.tsx +16 -2
- package/src/ui/progress-bar.tsx +83 -0
- package/src/ui/prompt-dock.tsx +145 -0
- package/src/ui/resizable.tsx +69 -1
- package/src/ui/scroll-area.tsx +14 -3
- package/src/ui/segmented.tsx +109 -0
- package/src/ui/settings-group.tsx +67 -0
- package/src/ui/skeleton.tsx +62 -5
- package/src/ui/stat.tsx +41 -0
- package/src/ui/status.tsx +48 -0
- package/src/ui/switch.tsx +49 -23
- package/src/ui/tabs.tsx +168 -0
- package/src/ui/tooltip.tsx +52 -8
- package/theme.css +113 -0
- package/dist/components/slash-command.d.ts +0 -15
- package/dist/elements/chunks/Icon-D_Zmwmwh.js +0 -1
- package/dist/elements/chunks/action-icons-PVfIvcuG.js +0 -1
- package/dist/elements/chunks/attachments-Dp6vqtrc.js +0 -1
- package/dist/elements/chunks/button-CKyYD9G4.js +0 -1
- package/dist/elements/chunks/card-Cjho-PYk.js +0 -1
- package/dist/elements/chunks/chat-thread-RT808iPf.js +0 -1
- package/dist/elements/chunks/check-CDYaMYrM.js +0 -1
- package/dist/elements/chunks/chevron-down-3SBiq8HF.js +0 -1
- package/dist/elements/chunks/chevron-right-k2UFlIVK.js +0 -1
- package/dist/elements/chunks/cn-Dogu6PbV.js +0 -1
- package/dist/elements/chunks/code-block-BYUmfFSd.js +0 -1
- package/dist/elements/chunks/collapsible-BiNwdOn6.js +0 -1
- package/dist/elements/chunks/context-18g6oi4J.js +0 -1
- package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +0 -1
- package/dist/elements/chunks/default-input-CSAoYYMd.js +0 -1
- package/dist/elements/chunks/download-D8K0Je8r.js +0 -1
- package/dist/elements/chunks/dropdown-bUs2iTia.js +0 -1
- package/dist/elements/chunks/external-link-6mA0wxab.js +0 -1
- package/dist/elements/chunks/file-text-DnaYk3Dm.js +0 -1
- package/dist/elements/chunks/file-tree-CgtRLlxK.js +0 -1
- package/dist/elements/chunks/hover-card-CIGw62gP.js +0 -1
- package/dist/elements/chunks/link-BlGSeoPy.js +0 -1
- package/dist/elements/chunks/loader-DvhR4qa0.js +0 -1
- package/dist/elements/chunks/message-CDZwXjhL.js +0 -1
- package/dist/elements/chunks/message-feedback-CYx81QiA.js +0 -1
- package/dist/elements/chunks/model-switcher-O_tUhoOT.js +0 -1
- package/dist/elements/chunks/overlay-DydXYMQW.js +0 -1
- package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +0 -1
- package/dist/elements/chunks/reasoning-2PUnHlmr.js +0 -1
- package/dist/elements/chunks/resizable-B5WNbSeb.js +0 -1
- package/dist/elements/chunks/star-t9tI-P0q.js +0 -1
- package/dist/elements/chunks/store-C6rIooZz.js +0 -1
- package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +0 -1
- package/dist/elements/chunks/textarea-3nm6Ia9U.js +0 -1
- package/dist/elements/chunks/thumbs-up-BBtguGld.js +0 -1
- package/dist/elements/chunks/toast-store-D8cL3fWl.js +0 -1
- package/dist/elements/chunks/tool-GJ3vwp1U.js +0 -1
- package/dist/elements/chunks/tooltip-DwKAz4XR.js +0 -1
- package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +0 -1
- package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +0 -1
- package/dist/elements/chunks/video-mVYiM99-.js +0 -1
- package/dist/elements/chunks/x-CzxL27Ch.js +0 -1
- package/dist/kitn-chat.es.js +0 -1
- package/dist/register-JyTglR6m.js +0 -1
- package/dist/register-impl-C6wlSsts.js +0 -77
- package/src/components/slash-command.tsx +0 -232
- package/src/elements/framework-usage.json +0 -588
- /package/dist/{kitn-chat-provider.d.ts → kai-provider.d.ts} +0 -0
- /package/dist/{kitn-chat-provider.es.js → kai-provider.es.js} +0 -0
package/src/components/toast.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createSignal, createEffect, onCleanup, on, For, Show, createMemo,
|
|
3
3
|
} from 'solid-js';
|
|
4
|
-
import { X, Check } from 'lucide-solid';
|
|
4
|
+
import { X, Check, AlertTriangle, XCircle, Info } from 'lucide-solid';
|
|
5
5
|
import { cn } from '../utils/cn';
|
|
6
6
|
import { createPresence } from '../ui/overlay';
|
|
7
7
|
import {
|
|
8
|
-
type ToastItem, type ToastVariant, resolveDuration,
|
|
8
|
+
type ToastItem, type ToastVariant, type ToastAppearance, resolveDuration,
|
|
9
9
|
} from '../primitives/toast-store';
|
|
10
10
|
|
|
11
|
-
export type { ToastItem, ToastVariant };
|
|
11
|
+
export type { ToastItem, ToastVariant, ToastAppearance };
|
|
12
12
|
|
|
13
13
|
/** Why a toast went away — surfaced to `onDismiss` (and the facade event). */
|
|
14
14
|
export type ToastDismissReason = 'timeout' | 'close' | 'action';
|
|
@@ -27,6 +27,12 @@ export interface ToastRegionProps {
|
|
|
27
27
|
/** Stacking: 'expanded' (default, full column) | 'collapsed' (Sonner-style
|
|
28
28
|
* pile that expands on hover/focus). Attribute: stack. */
|
|
29
29
|
stack?: 'expanded' | 'collapsed';
|
|
30
|
+
/** Region-level default appearance for toasts that don't set their own. A
|
|
31
|
+
* per-toast `appearance` always wins. Defaults to `'pill'`. */
|
|
32
|
+
appearance?: ToastAppearance;
|
|
33
|
+
/** Region-level default inverse treatment for toasts that don't set their own.
|
|
34
|
+
* A per-toast `inverse` always wins. Defaults to `false`. */
|
|
35
|
+
inverse?: boolean;
|
|
30
36
|
/** When set, this region renders only the toasts scoped to that element and
|
|
31
37
|
* anchors over its bounds (top-center), following it on scroll/resize. Unset =
|
|
32
38
|
* the global, viewport-anchored region. */
|
|
@@ -60,6 +66,10 @@ interface ToastProps {
|
|
|
60
66
|
/** Hold the auto-dismiss timer (the region sets this true while the whole stack
|
|
61
67
|
* is hovered/expanded, so peers don't dismiss out from under the cursor). */
|
|
62
68
|
paused?: boolean;
|
|
69
|
+
/** Region-level default appearance, used when the item doesn't set its own. */
|
|
70
|
+
appearance?: ToastAppearance;
|
|
71
|
+
/** Region-level default inverse, used when the item doesn't set its own. */
|
|
72
|
+
inverse?: boolean;
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/**
|
|
@@ -73,6 +83,18 @@ export function Toast(props: ToastProps) {
|
|
|
73
83
|
const presence = createPresence(open);
|
|
74
84
|
const variant = () => props.item.variant ?? 'neutral';
|
|
75
85
|
const dismissible = () => props.item.dismissible !== false;
|
|
86
|
+
// Item-level value wins, then the region-level default, then the built-in default.
|
|
87
|
+
const appearance = (): ToastAppearance => props.item.appearance ?? props.appearance ?? 'pill';
|
|
88
|
+
const inverse = (): boolean => props.item.inverse ?? props.inverse ?? false;
|
|
89
|
+
|
|
90
|
+
// Surface + accent colors, swapped for the high-contrast inverse treatment so the
|
|
91
|
+
// toast pops on EITHER appearance, in light AND dark. The inverse surface is the
|
|
92
|
+
// foreground color, so action/close/muted text switch to the background color.
|
|
93
|
+
const surfaceClass = () =>
|
|
94
|
+
inverse() ? 'bg-foreground text-background border-transparent' : 'bg-popover text-popover-foreground border-border';
|
|
95
|
+
const mutedClass = () => (inverse() ? 'text-background/70' : 'text-muted-foreground');
|
|
96
|
+
const actionClass = () => (inverse() ? 'text-background hover:text-background/80' : 'text-primary hover:text-primary/80');
|
|
97
|
+
const closeClass = () => (inverse() ? 'text-background/70 hover:text-background' : 'text-muted-foreground hover:text-foreground');
|
|
76
98
|
|
|
77
99
|
let remaining = resolveDuration(props.item);
|
|
78
100
|
let startedAt = 0;
|
|
@@ -128,45 +150,107 @@ export function Toast(props: ToastProps) {
|
|
|
128
150
|
if (!keepOpen) { clear(); reason = 'action'; setOpen(false); }
|
|
129
151
|
};
|
|
130
152
|
|
|
153
|
+
// Leading variant icon — shared by both appearances. In the card it sits at the
|
|
154
|
+
// top, so nudge it down to align with the title's cap height.
|
|
155
|
+
const iconAlign = () => (appearance() === 'card' ? 'mt-0.5' : undefined);
|
|
156
|
+
const VariantIcon = () => (
|
|
157
|
+
<>
|
|
158
|
+
<Show when={variant() === 'success'}>
|
|
159
|
+
<Check class={cn('size-4 shrink-0 text-emerald-500', iconAlign())} />
|
|
160
|
+
</Show>
|
|
161
|
+
<Show when={variant() === 'warning'}>
|
|
162
|
+
<AlertTriangle class={cn('size-4 shrink-0 text-warning', iconAlign())} />
|
|
163
|
+
</Show>
|
|
164
|
+
<Show when={variant() === 'error'}>
|
|
165
|
+
<XCircle class={cn('size-4 shrink-0 text-destructive', iconAlign())} />
|
|
166
|
+
</Show>
|
|
167
|
+
<Show when={variant() === 'info'}>
|
|
168
|
+
<Info class={cn('size-4 shrink-0 text-info', iconAlign())} />
|
|
169
|
+
</Show>
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const ActionButton = (p: { class?: string }) => (
|
|
174
|
+
<Show when={props.item.action}>
|
|
175
|
+
{(action) => (
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
onClick={act}
|
|
179
|
+
class={cn(
|
|
180
|
+
appearance() === 'card'
|
|
181
|
+
? cn(
|
|
182
|
+
'inline-flex shrink-0 items-center rounded-md border px-2.5 py-1 text-xs font-medium transition-colors',
|
|
183
|
+
inverse()
|
|
184
|
+
? 'border-background/30 text-background hover:bg-background/10'
|
|
185
|
+
: 'border-border text-foreground hover:bg-accent',
|
|
186
|
+
)
|
|
187
|
+
: cn('shrink-0 rounded-md text-sm font-medium underline-offset-2 hover:underline', actionClass()),
|
|
188
|
+
p.class,
|
|
189
|
+
)}
|
|
190
|
+
>
|
|
191
|
+
{action().label}
|
|
192
|
+
</button>
|
|
193
|
+
)}
|
|
194
|
+
</Show>
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const CloseButton = () => (
|
|
198
|
+
<Show when={dismissible()}>
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
onClick={close}
|
|
202
|
+
aria-label="Dismiss"
|
|
203
|
+
class={cn(
|
|
204
|
+
'-mr-1 flex size-5 shrink-0 items-center justify-center rounded-md transition-colors',
|
|
205
|
+
closeClass(),
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
<X class="size-3.5" />
|
|
209
|
+
</button>
|
|
210
|
+
</Show>
|
|
211
|
+
);
|
|
212
|
+
|
|
131
213
|
return (
|
|
132
214
|
<Show when={presence.present()}>
|
|
133
215
|
<div
|
|
134
216
|
ref={presence.setRef}
|
|
135
217
|
role="status"
|
|
218
|
+
data-appearance={appearance()}
|
|
219
|
+
data-inverse={inverse() ? '' : undefined}
|
|
136
220
|
data-closed={presence.state() === 'closed' ? '' : undefined}
|
|
137
221
|
onPointerEnter={() => setHovered(true)}
|
|
138
222
|
onPointerLeave={() => setHovered(false)}
|
|
139
223
|
class={cn(
|
|
140
|
-
'pointer-events-auto
|
|
141
|
-
|
|
224
|
+
'pointer-events-auto border text-sm shadow-lg',
|
|
225
|
+
surfaceClass(),
|
|
142
226
|
'animate-in fade-in-0 slide-in-from-top-4',
|
|
143
227
|
'data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:slide-out-to-top-2',
|
|
228
|
+
appearance() === 'card'
|
|
229
|
+
? 'flex min-w-[24rem] items-start gap-3 rounded-xl p-4 shadow-xl'
|
|
230
|
+
: 'flex items-center gap-3 rounded-full px-4 py-2.5',
|
|
144
231
|
)}
|
|
145
232
|
>
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
</
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
>
|
|
168
|
-
<X class="size-3.5" />
|
|
169
|
-
</button>
|
|
233
|
+
<VariantIcon />
|
|
234
|
+
<Show
|
|
235
|
+
when={appearance() === 'card'}
|
|
236
|
+
fallback={
|
|
237
|
+
<>
|
|
238
|
+
<span class="min-w-0 flex-1 truncate">{props.item.message}</span>
|
|
239
|
+
<ActionButton />
|
|
240
|
+
<CloseButton />
|
|
241
|
+
</>
|
|
242
|
+
}
|
|
243
|
+
>
|
|
244
|
+
<div class="min-w-0 flex-1">
|
|
245
|
+
<div class="font-medium leading-snug">{props.item.message}</div>
|
|
246
|
+
<Show when={props.item.description}>
|
|
247
|
+
<div class={cn('mt-1 line-clamp-2 text-xs leading-snug', mutedClass())}>
|
|
248
|
+
{props.item.description}
|
|
249
|
+
</div>
|
|
250
|
+
</Show>
|
|
251
|
+
<ActionButton class="mt-2" />
|
|
252
|
+
</div>
|
|
253
|
+
<CloseButton />
|
|
170
254
|
</Show>
|
|
171
255
|
</div>
|
|
172
256
|
</Show>
|
|
@@ -333,7 +417,7 @@ export function ToastRegion(props: ToastRegionProps) {
|
|
|
333
417
|
'pointer-events-none fixed z-[100] flex flex-col gap-2',
|
|
334
418
|
anchor()
|
|
335
419
|
? ANCHOR_FLEX[position()]
|
|
336
|
-
: cn('max-w-[min(
|
|
420
|
+
: cn('max-w-[min(30rem,calc(100vw-2rem))]', POSITION_CLASSES[position()]),
|
|
337
421
|
)}
|
|
338
422
|
style={anchor() ? anchorStyle(position(), anchor()!) : undefined}
|
|
339
423
|
>
|
|
@@ -341,6 +425,8 @@ export function ToastRegion(props: ToastRegionProps) {
|
|
|
341
425
|
{(item) => (
|
|
342
426
|
<Toast
|
|
343
427
|
item={item}
|
|
428
|
+
appearance={props.appearance}
|
|
429
|
+
inverse={props.inverse}
|
|
344
430
|
onDismiss={(reason) => props.onDismiss?.(item.id, reason)}
|
|
345
431
|
onAction={(label) => props.onAction?.(item.id, label)}
|
|
346
432
|
/>
|
|
@@ -368,7 +454,7 @@ export function ToastRegion(props: ToastRegionProps) {
|
|
|
368
454
|
anchored edge. */}
|
|
369
455
|
<div
|
|
370
456
|
class="pointer-events-auto relative transition-[height] duration-200 ease-out"
|
|
371
|
-
style={{ 'min-width': '16rem', 'max-width': 'min(
|
|
457
|
+
style={{ 'min-width': '16rem', 'max-width': 'min(30rem, calc(100vw - 2rem))', height: `${stageHeight()}px` }}
|
|
372
458
|
>
|
|
373
459
|
<For each={visible()}>
|
|
374
460
|
{(item, i) => (
|
|
@@ -385,6 +471,8 @@ export function ToastRegion(props: ToastRegionProps) {
|
|
|
385
471
|
<Toast
|
|
386
472
|
item={item}
|
|
387
473
|
paused={open()}
|
|
474
|
+
appearance={props.appearance}
|
|
475
|
+
inverse={props.inverse}
|
|
388
476
|
onDismiss={(reason) => props.onDismiss?.(item.id, reason)}
|
|
389
477
|
onAction={(label) => props.onAction?.(item.id, label)}
|
|
390
478
|
/>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A tool-call part rendered by <Tool>. Pure type — kept JSX-free so it can be
|
|
2
|
+
* imported by the framework-neutral state core and the React typecheck pass. */
|
|
3
|
+
export interface ToolPart {
|
|
4
|
+
type: string;
|
|
5
|
+
state: 'input-streaming' | 'input-available' | 'output-available' | 'output-error';
|
|
6
|
+
input?: Record<string, unknown>;
|
|
7
|
+
output?: Record<string, unknown>;
|
|
8
|
+
toolCallId?: string;
|
|
9
|
+
errorText?: string;
|
|
10
|
+
}
|
package/src/components/tool.tsx
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { type JSX, splitProps, createSignal, Show, For } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
|
-
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../ui/collapsible';
|
|
3
|
+
import { Collapsible, CollapsibleTrigger, CollapsibleContent, type CollapsibleController } from '../ui/collapsible';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import { CheckCircle, ChevronDown, Loader2, Settings, XCircle } from 'lucide-solid';
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
type: string;
|
|
9
|
-
state: 'input-streaming' | 'input-available' | 'output-available' | 'output-error';
|
|
10
|
-
input?: Record<string, unknown>;
|
|
11
|
-
output?: Record<string, unknown>;
|
|
12
|
-
toolCallId?: string;
|
|
13
|
-
errorText?: string;
|
|
14
|
-
}
|
|
6
|
+
import type { ToolPart } from './tool-types';
|
|
7
|
+
export type { ToolPart } from './tool-types';
|
|
15
8
|
|
|
16
9
|
export interface ToolProps {
|
|
17
10
|
toolPart: ToolPart;
|
|
18
11
|
defaultOpen?: boolean;
|
|
12
|
+
/** Gate the disclosure trigger (programmatic control still works). */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Receive the open controller once mounted (forwarded from the Collapsible),
|
|
15
|
+
* so a parent (the kai-tool facade) can drive/observe open state. */
|
|
16
|
+
controllerRef?: (api: CollapsibleController) => void;
|
|
19
17
|
class?: string;
|
|
20
18
|
}
|
|
21
19
|
|
|
@@ -101,16 +99,24 @@ function ToolStateBadge(props: { state: ToolPart['state'] }) {
|
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
function Tool(props: ToolProps) {
|
|
104
|
-
const [local, rest] = splitProps(props, ['toolPart', 'defaultOpen', 'class']);
|
|
105
|
-
|
|
102
|
+
const [local, rest] = splitProps(props, ['toolPart', 'defaultOpen', 'disabled', 'controllerRef', 'class']);
|
|
103
|
+
// Drive the Collapsible UNCONTROLLED (seed from defaultOpen) and read its open
|
|
104
|
+
// state via the controller it hands up — that same controller is forwarded to
|
|
105
|
+
// the facade so wireDisclosure can drive show/hide/toggle.
|
|
106
|
+
const [api, setApi] = createSignal<CollapsibleController>();
|
|
107
|
+
const isOpen = () => api()?.open() ?? (local.defaultOpen ?? false);
|
|
106
108
|
|
|
107
109
|
const state = () => local.toolPart.state;
|
|
108
110
|
const input = () => local.toolPart.input;
|
|
109
111
|
const output = () => local.toolPart.output;
|
|
110
112
|
|
|
111
113
|
return (
|
|
112
|
-
<div class={cn('mt-3 overflow-hidden rounded-lg bg-
|
|
113
|
-
<Collapsible
|
|
114
|
+
<div class={cn('mt-3 overflow-hidden rounded-lg bg-surface', local.class)}>
|
|
115
|
+
<Collapsible
|
|
116
|
+
defaultOpen={local.defaultOpen}
|
|
117
|
+
disabled={local.disabled}
|
|
118
|
+
controllerRef={(a) => { setApi(a); local.controllerRef?.(a); }}
|
|
119
|
+
>
|
|
114
120
|
<CollapsibleTrigger
|
|
115
121
|
as={(triggerProps: JSX.HTMLAttributes<HTMLButtonElement>) => (
|
|
116
122
|
<Button
|
|
@@ -134,7 +140,7 @@ function Tool(props: ToolProps) {
|
|
|
134
140
|
<Show when={input() && Object.keys(input()!).length > 0}>
|
|
135
141
|
<div>
|
|
136
142
|
<h4 class="text-muted-foreground mb-2 text-sm font-medium">Input</h4>
|
|
137
|
-
<div class="rounded bg-
|
|
143
|
+
<div class="rounded bg-surface-strong p-2 font-mono text-sm">
|
|
138
144
|
<For each={Object.entries(input()!)}>
|
|
139
145
|
{([key, value]) => (
|
|
140
146
|
<div class="mb-1">
|
|
@@ -150,7 +156,7 @@ function Tool(props: ToolProps) {
|
|
|
150
156
|
<Show when={output()}>
|
|
151
157
|
<div>
|
|
152
158
|
<h4 class="text-muted-foreground mb-2 text-sm font-medium">Output</h4>
|
|
153
|
-
<div tabindex={0} class="max-h-60 overflow-auto rounded bg-
|
|
159
|
+
<div tabindex={0} class="max-h-60 overflow-auto rounded bg-surface-strong p-2 font-mono text-sm">
|
|
154
160
|
<pre class="whitespace-pre-wrap">{formatValue(output())}</pre>
|
|
155
161
|
</div>
|
|
156
162
|
</div>
|
|
@@ -1,43 +1,126 @@
|
|
|
1
|
-
import { splitProps, Show, For, createSignal } from 'solid-js';
|
|
1
|
+
import { splitProps, Show, For, createSignal, createEffect, on } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { Button } from '../ui/button';
|
|
4
4
|
import { Tooltip } from '../ui/tooltip';
|
|
5
5
|
import { useVoiceRecorder } from '../primitives/use-voice-recorder';
|
|
6
|
+
import { useSpeechRecognition } from '../primitives/use-speech-recognition';
|
|
7
|
+
|
|
8
|
+
/** Imperative handle exposed via `controllerRef` — surfaces the recorder's latent
|
|
9
|
+
* start/stop so the `<kai-voice-input>` facade can forward them as instance
|
|
10
|
+
* methods (push-to-talk). Both run the SAME getUserMedia → blob → transcription
|
|
11
|
+
* path as clicking the mic, so manual + programmatic emit identically. */
|
|
12
|
+
export interface VoiceInputController {
|
|
13
|
+
/** Begin recording programmatically (same path as clicking the mic). */
|
|
14
|
+
start(): void;
|
|
15
|
+
/** Stop the in-progress recording (produces the blob → onTranscribe). */
|
|
16
|
+
stop(): void;
|
|
17
|
+
}
|
|
6
18
|
|
|
7
19
|
export interface VoiceInputProps {
|
|
8
20
|
onTranscribe: (audio: Blob) => Promise<string>;
|
|
9
21
|
onTranscription: (text: string) => void;
|
|
10
22
|
disabled?: boolean;
|
|
11
23
|
class?: string;
|
|
24
|
+
/** Host supplied a `transcribe` callback. When true the MediaRecorder →
|
|
25
|
+
* onTranscribe path runs. When false the component prefers native
|
|
26
|
+
* SpeechRecognition (capable browsers), falling back to record-only. */
|
|
27
|
+
hasTranscribe?: boolean;
|
|
28
|
+
/** BCP-47 language tag for native recognition (e.g. `en-US`). */
|
|
29
|
+
lang?: string;
|
|
30
|
+
/** Emit live partial transcripts via `onInterim` (native path only). */
|
|
31
|
+
interim?: boolean;
|
|
32
|
+
/** Live partial transcript from native recognition (when `interim`). */
|
|
33
|
+
onInterim?: (text: string) => void;
|
|
34
|
+
/** Fires whenever recording starts or stops. Guarded against the spurious
|
|
35
|
+
* initial `false` — only true transitions emit (the facade maps this to
|
|
36
|
+
* kai-recording-change). */
|
|
37
|
+
onRecordingChange?: (recording: boolean) => void;
|
|
38
|
+
/** Receive the imperative controller once mounted. The `<kai-voice-input>`
|
|
39
|
+
* facade forwards these as element methods (start/stop). */
|
|
40
|
+
controllerRef?: (controller: VoiceInputController) => void;
|
|
12
41
|
}
|
|
13
42
|
|
|
14
43
|
export function VoiceInput(props: VoiceInputProps) {
|
|
15
44
|
const [local] = splitProps(props, ['onTranscribe', 'onTranscription', 'disabled', 'class']);
|
|
16
45
|
const { isRecording, start, stop } = useVoiceRecorder();
|
|
46
|
+
const speech = useSpeechRecognition({ interim: props.interim });
|
|
17
47
|
const [isProcessing, setIsProcessing] = createSignal(false);
|
|
18
48
|
|
|
49
|
+
// Path selection (§6): a `transcribe` callback always wins (MediaRecorder →
|
|
50
|
+
// host transcriber). Otherwise prefer native SpeechRecognition when the browser
|
|
51
|
+
// supports it. The unsupported-no-callback case falls through to recording the
|
|
52
|
+
// blob (→ onTranscribe → kai-audio-captured) with no text.
|
|
53
|
+
const useNative = () => !props.hasTranscribe && speech.isSupported;
|
|
54
|
+
|
|
19
55
|
const label = () =>
|
|
20
|
-
isProcessing() ? 'Transcribing...' :
|
|
56
|
+
isProcessing() ? 'Transcribing...' : isActive() ? 'Stop recording' : 'Voice input';
|
|
21
57
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
58
|
+
const isActive = () => (useNative() ? speech.isListening() : isRecording());
|
|
59
|
+
|
|
60
|
+
// Native path: SpeechRecognition resolves with the final transcript when it
|
|
61
|
+
// ends (click again or controller.stop()). Final text → onTranscription.
|
|
62
|
+
async function beginRecognition() {
|
|
63
|
+
try {
|
|
64
|
+
const text = await speech.start({
|
|
65
|
+
lang: props.lang,
|
|
66
|
+
onInterim: props.interim ? (t) => props.onInterim?.(t) : undefined,
|
|
67
|
+
});
|
|
68
|
+
if (text.trim()) local.onTranscription(text.trim());
|
|
69
|
+
} catch {
|
|
70
|
+
/* surfaced via speech.error; the mic returns to idle */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Begin recording, then run transcription once the blob resolves. Shared by the
|
|
75
|
+
// mic click and the imperative start() so both flows emit identically. start()
|
|
76
|
+
// resolves its Blob when the recorder stops (click again or controller.stop()).
|
|
77
|
+
async function beginRecording() {
|
|
78
|
+
try {
|
|
79
|
+
const blob = await start();
|
|
80
|
+
setIsProcessing(true);
|
|
26
81
|
try {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const text = await local.onTranscribe(blob);
|
|
31
|
-
if (text.trim()) local.onTranscription(text.trim());
|
|
32
|
-
} finally {
|
|
33
|
-
setIsProcessing(false);
|
|
34
|
-
}
|
|
35
|
-
} catch {
|
|
82
|
+
const text = await local.onTranscribe(blob);
|
|
83
|
+
if (text.trim()) local.onTranscription(text.trim());
|
|
84
|
+
} finally {
|
|
36
85
|
setIsProcessing(false);
|
|
37
86
|
}
|
|
87
|
+
} catch {
|
|
88
|
+
setIsProcessing(false);
|
|
38
89
|
}
|
|
39
90
|
}
|
|
40
91
|
|
|
92
|
+
// Drive whichever path is active (§6: start/stop must follow the live path).
|
|
93
|
+
function begin() {
|
|
94
|
+
if (useNative()) void beginRecognition();
|
|
95
|
+
else void beginRecording();
|
|
96
|
+
}
|
|
97
|
+
function end() {
|
|
98
|
+
if (useNative()) speech.stop();
|
|
99
|
+
else stop();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function handleClick() {
|
|
103
|
+
if (isActive()) end();
|
|
104
|
+
else begin();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Emit recording transitions to the host. `on(..., { defer: true })` skips the
|
|
108
|
+
// initial mount value (a spurious {recording:false}) — only real start/stop
|
|
109
|
+
// transitions fire. Drives BOTH manual (click) and programmatic (start/stop)
|
|
110
|
+
// since they all flip the active path's signal (MediaRecorder or recognition).
|
|
111
|
+
createEffect(on(isActive, (recording) => {
|
|
112
|
+
props.onRecordingChange?.(recording);
|
|
113
|
+
}, { defer: true }));
|
|
114
|
+
|
|
115
|
+
// Imperative controller (Pattern C): hand the facade a start/stop handle.
|
|
116
|
+
// start() runs the active path; stop() ends the in-progress session (resolving
|
|
117
|
+
// the active path's promise). Guard start() so a double-start while already
|
|
118
|
+
// active is a no-op (matches the click toggle).
|
|
119
|
+
props.controllerRef?.({
|
|
120
|
+
start: () => { if (!isActive()) begin(); },
|
|
121
|
+
stop: () => end(),
|
|
122
|
+
});
|
|
123
|
+
|
|
41
124
|
// Mic icon (not recording)
|
|
42
125
|
const MicIcon = () => (
|
|
43
126
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -65,7 +148,7 @@ export function VoiceInput(props: VoiceInputProps) {
|
|
|
65
148
|
return (
|
|
66
149
|
<div class={cn('relative inline-flex items-center justify-center', local.class)}>
|
|
67
150
|
{/* Animated pulse rings when recording */}
|
|
68
|
-
<Show when={
|
|
151
|
+
<Show when={isActive()}>
|
|
69
152
|
<For each={[0, 1, 2]}>
|
|
70
153
|
{(index) => (
|
|
71
154
|
<div
|
|
@@ -88,16 +171,16 @@ export function VoiceInput(props: VoiceInputProps) {
|
|
|
88
171
|
disabled={local.disabled || isProcessing()}
|
|
89
172
|
class={cn(
|
|
90
173
|
'relative z-10 rounded-full transition-all duration-300',
|
|
91
|
-
|
|
174
|
+
isActive() && 'bg-destructive text-white hover:bg-destructive/80',
|
|
92
175
|
)}
|
|
93
176
|
>
|
|
94
177
|
<Show when={isProcessing()}>
|
|
95
178
|
<Spinner />
|
|
96
179
|
</Show>
|
|
97
|
-
<Show when={!isProcessing() &&
|
|
180
|
+
<Show when={!isProcessing() && isActive()}>
|
|
98
181
|
<StopIcon />
|
|
99
182
|
</Show>
|
|
100
|
-
<Show when={!isProcessing() && !
|
|
183
|
+
<Show when={!isProcessing() && !isActive()}>
|
|
101
184
|
<MicIcon />
|
|
102
185
|
</Show>
|
|
103
186
|
</Button>
|