@kitn.ai/ui 0.17.0 → 0.18.1
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-C2LTALdM.js +1 -0
- package/dist/elements/chunks/action-icons-Dtl8Ox5D.js +1 -0
- package/dist/elements/chunks/arrow-left-GbBceGBE.js +1 -0
- package/dist/elements/chunks/attachments-DLJcWCDV.js +1 -0
- package/dist/elements/chunks/badge-BTNZTODL.js +1 -0
- package/dist/elements/chunks/button-DBDZebGr.js +1 -0
- package/dist/elements/chunks/card-YfCyI4fH.js +1 -0
- package/dist/elements/chunks/chat-thread-Dz2qbzvI.js +1 -0
- package/dist/elements/chunks/check-fhmRksq_.js +1 -0
- package/dist/elements/chunks/chevron-down-DcRg1aRU.js +1 -0
- package/dist/elements/chunks/chevron-right-ChzBcNJ-.js +1 -0
- package/dist/elements/chunks/circle-B4fTe9v6.js +1 -0
- package/dist/elements/chunks/circle-check-CJV-3Rem.js +1 -0
- package/dist/elements/chunks/circle-x-pDtzlghQ.js +1 -0
- package/dist/elements/chunks/cn-CU7UAWzk.js +1 -0
- package/dist/elements/chunks/code-block-DZ6mz6vs.js +1 -0
- package/dist/elements/chunks/collapsible-CWUAShMC.js +1 -0
- package/dist/elements/chunks/composer-BZyPU6gD.js +65 -0
- package/dist/elements/chunks/context-Cx6eQA_G.js +1 -0
- package/dist/elements/chunks/conversation-list-C4xTQSpT.js +1 -0
- package/dist/elements/chunks/default-input-BZcwyKcP.js +1 -0
- package/dist/elements/chunks/define-CNSKFZCd.js +1 -0
- package/dist/elements/chunks/disclosure-BcBs--Jf.js +1 -0
- package/dist/elements/chunks/download-BDQwcFav.js +1 -0
- package/dist/elements/chunks/dropdown-wgs8xCJZ.js +1 -0
- package/dist/elements/chunks/ellipsis-CUiEJ6R1.js +1 -0
- package/dist/elements/chunks/external-link-DJUmC7g2.js +1 -0
- package/dist/elements/chunks/file-text-DOYfYvrh.js +1 -0
- package/dist/elements/chunks/file-tree-DQzNQrbr.js +1 -0
- package/dist/elements/chunks/folder-DBDUBANz.js +1 -0
- package/dist/elements/chunks/hover-card-BQGdGAQ3.js +1 -0
- package/dist/elements/chunks/icon-CAyRqVrg.js +1 -0
- package/dist/elements/chunks/{index-CTBEJeo6.js → index-BCRhi84a.js} +1 -1
- package/dist/elements/chunks/info-DBQl-m6e.js +1 -0
- package/dist/elements/chunks/input-DZD67EvW.js +1 -0
- package/dist/elements/chunks/kbd-DbyVrlnU.js +1 -0
- package/dist/elements/chunks/link-vko7AttA.js +1 -0
- package/dist/elements/chunks/loader-Cg6rO8tQ.js +1 -0
- package/dist/elements/chunks/{markdown-CMTGx9r8.js → markdown-n3Xbj3PR.js} +9 -9
- package/dist/elements/chunks/message-CgKytiSB.js +1 -0
- package/dist/elements/chunks/message-feedback-Ds3-US0e.js +1 -0
- package/dist/elements/chunks/message-square-MmRkvc-M.js +1 -0
- package/dist/elements/chunks/minimize-2-3kCdE0q3.js +1 -0
- package/dist/elements/chunks/model-switcher-BMtXGwLU.js +1 -0
- package/dist/elements/chunks/overlay-B1-MBI6h.js +1 -0
- package/dist/elements/chunks/paperclip-DDEfmkHc.js +1 -0
- package/dist/elements/chunks/progress-bar-Dwbiyv3s.js +1 -0
- package/dist/elements/chunks/prompt-suggestion-9lvgFcEa.js +1 -0
- package/dist/elements/chunks/reasoning-DzHWsdAJ.js +1 -0
- package/dist/elements/chunks/resizable-Bq_eHxUm.js +1 -0
- package/dist/elements/chunks/rotate-cw-jpisOdOV.js +1 -0
- package/dist/elements/chunks/scroll-area-g5Ze3K6f.js +1 -0
- package/dist/elements/chunks/separator-Bmjc5XyX.js +1 -0
- package/dist/elements/chunks/settings-Cz1bMJP9.js +1 -0
- package/dist/elements/chunks/settings-group-9imY3kRA.js +1 -0
- package/dist/elements/chunks/share-BdenF0wQ.js +1 -0
- package/dist/elements/chunks/skeleton-Pjc5jiaY.js +1 -0
- package/dist/elements/chunks/slots-DkZ-Cmha.js +1 -0
- package/dist/elements/chunks/star-DY6z1UUH.js +1 -0
- package/dist/elements/chunks/store-th4VyJXM.js +1 -0
- package/dist/elements/chunks/text-shimmer-CEi-tlXr.js +1 -0
- package/dist/elements/chunks/textarea-CboigarR.js +1 -0
- package/dist/elements/chunks/thumbs-up-BR33KLfA.js +1 -0
- package/dist/elements/chunks/toast-store-BEMda0fv.js +1 -0
- package/dist/elements/chunks/tool-TMued3WO.js +1 -0
- package/dist/elements/chunks/tooltip-Cft_Rxiw.js +1 -0
- package/dist/elements/chunks/triangle-alert-B07fX3Q8.js +1 -0
- package/dist/elements/chunks/use-card-resolution-M36zVciB.js +1 -0
- package/dist/elements/chunks/video-MKp3LmNT.js +1 -0
- package/dist/elements/chunks/x-Ckwicf6g.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 +8255 -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-cKscku5m.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 +77 -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 +31 -10
- package/src/agent-tooling/mcp/manifest.ts +14 -0
- package/src/agent-tooling/mcp/tools/debug.ts +11 -7
- 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 +249 -0
- package/src/components/component-meta.json +699 -110
- 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 +341 -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 +2 -2
- 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 +87 -7
- 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 +218 -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 +117 -0
- package/src/ui/pane-group.tsx +291 -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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineWebComponent } from './define';
|
|
2
|
-
import { VoiceInput } from '../components/voice-input';
|
|
2
|
+
import { VoiceInput, type VoiceInputController } from '../components/voice-input';
|
|
3
3
|
|
|
4
4
|
interface Props extends Record<string, unknown> {
|
|
5
5
|
/**
|
|
@@ -10,32 +10,82 @@ interface Props extends Record<string, unknown> {
|
|
|
10
10
|
transcribe?: (audio: Blob) => Promise<string>;
|
|
11
11
|
/** Disable the mic button (non-interactive). */
|
|
12
12
|
disabled?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* BCP-47 language tag for the native `SpeechRecognition` path (e.g. `en-US`).
|
|
15
|
+
* Attribute: `recognition-lang` (the plain `lang` attribute is reserved by
|
|
16
|
+
* `HTMLElement` and can't be a custom-element property). No effect when
|
|
17
|
+
* `transcribe` is set or the browser lacks SpeechRecognition.
|
|
18
|
+
*/
|
|
19
|
+
recognitionLang?: string;
|
|
20
|
+
/** Emit live partial transcripts (`kai-transcript-interim`) during native
|
|
21
|
+
* recognition. Attribute: `interim`. No-op on the transcribe/fallback paths. */
|
|
22
|
+
interim?: boolean;
|
|
13
23
|
}
|
|
14
24
|
|
|
15
25
|
/** Events fired by `<kai-voice-input>`. */
|
|
16
26
|
interface Events {
|
|
17
27
|
/** Raw audio captured (before transcription) — for hosts that prefer to
|
|
18
|
-
* handle transcription themselves instead of via the `transcribe` property.
|
|
28
|
+
* handle transcription themselves instead of via the `transcribe` property.
|
|
29
|
+
* Also the unsupported-fallback signal: no `transcribe`, no SpeechRecognition,
|
|
30
|
+
* so only the blob is produced (no text). */
|
|
19
31
|
'kai-audio-captured': { blob: Blob };
|
|
20
|
-
/**
|
|
32
|
+
/** Final transcript — the `transcribe` property resolved, OR native
|
|
33
|
+
* `SpeechRecognition` produced final text (no `transcribe` set). */
|
|
21
34
|
'kai-transcription': { text: string };
|
|
35
|
+
/** Live partial transcript during native recognition (only when `interim` is
|
|
36
|
+
* set). Fires repeatedly before the final `kai-transcription`. */
|
|
37
|
+
'kai-transcript-interim': { text: string };
|
|
38
|
+
/** Recording started or stopped — lets the host drive its own UI (waveform,
|
|
39
|
+
* push-to-talk indicator) in sync with the mic. Fires on real transitions
|
|
40
|
+
* only (manual click and programmatic start()/stop()), never on mount. */
|
|
41
|
+
'kai-recording-change': { recording: boolean };
|
|
22
42
|
}
|
|
23
43
|
|
|
24
44
|
/**
|
|
25
|
-
* `<kai-voice-input>` — a mic button that records and transcribes.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
45
|
+
* `<kai-voice-input>` — a mic button that records and transcribes. Works
|
|
46
|
+
* natively by default: with no `transcribe` callback it uses the browser's
|
|
47
|
+
* `SpeechRecognition` (Chrome/Safari; no Firefox — and cloud-based in Chrome).
|
|
48
|
+
* Set `el.transcribe` to route audio through your own async transcriber instead.
|
|
49
|
+
* Where neither is available it records the blob and emits `kai-audio-captured`
|
|
50
|
+
* with no text. Also emits `kai-transcription` (final text) and, with `interim`,
|
|
51
|
+
* `kai-transcript-interim` (live partials).
|
|
28
52
|
*/
|
|
29
53
|
defineWebComponent<Props, Events>('kai-voice-input', {
|
|
30
54
|
transcribe: undefined,
|
|
31
55
|
disabled: false,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
recognitionLang: undefined,
|
|
57
|
+
interim: false,
|
|
58
|
+
}, (props, { dispatch, flag, expose }) => {
|
|
59
|
+
// Pattern C: the VoiceInput component owns useVoiceRecorder; it hands up a
|
|
60
|
+
// start/stop controller and an onRecordingChange callback. The facade captures
|
|
61
|
+
// the controller and exposes delegating methods (start/stop both run the same
|
|
62
|
+
// record→transcribe path as the mic click, so manual + programmatic emit the
|
|
63
|
+
// same kai-audio-captured/kai-transcription/kai-recording-change events).
|
|
64
|
+
let controller: VoiceInputController | undefined;
|
|
65
|
+
expose({
|
|
66
|
+
/** Begin recording programmatically (e.g. push-to-talk bound to a global
|
|
67
|
+
* key). Runs the same getUserMedia path as clicking the mic; no-ops if
|
|
68
|
+
* already recording. */
|
|
69
|
+
start: () => controller?.start(),
|
|
70
|
+
/** Stop the in-progress recording, producing the blob (→ kai-audio-captured)
|
|
71
|
+
* and running transcription. Pairs with start() for push-to-talk. */
|
|
72
|
+
stop: () => controller?.stop(),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<VoiceInput
|
|
77
|
+
disabled={flag('disabled')}
|
|
78
|
+
hasTranscribe={typeof props.transcribe === 'function'}
|
|
79
|
+
lang={props.recognitionLang}
|
|
80
|
+
interim={flag('interim')}
|
|
81
|
+
onTranscribe={async (blob) => {
|
|
82
|
+
dispatch('kai-audio-captured', { blob });
|
|
83
|
+
return props.transcribe ? props.transcribe(blob) : '';
|
|
84
|
+
}}
|
|
85
|
+
onTranscription={(text) => dispatch('kai-transcription', { text })}
|
|
86
|
+
onInterim={(text) => dispatch('kai-transcript-interim', { text })}
|
|
87
|
+
onRecordingChange={(recording) => dispatch('kai-recording-change', { recording })}
|
|
88
|
+
controllerRef={(c) => (controller = c)}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineWebComponent } from './define';
|
|
2
|
+
import { VoiceOutput, type VoiceOutputController } from '../components/voice-output';
|
|
3
|
+
|
|
4
|
+
interface Props extends Record<string, unknown> {
|
|
5
|
+
/** The utterance to read aloud. */
|
|
6
|
+
text?: string;
|
|
7
|
+
/** Speak automatically when `text` is set/changed. */
|
|
8
|
+
autoplay?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* TTS model seam the host supplies — given text, returns an audio `Blob` to
|
|
11
|
+
* play. This is a **function-valued property** (`el.synthesize = async text =>
|
|
12
|
+
* blob`); when set, the native `speechSynthesis` path is bypassed. Mirrors
|
|
13
|
+
* `<kai-voice-input>`'s `transcribe`. A value-returning callback can't be
|
|
14
|
+
* modelled as a fire-and-forget event, hence a property.
|
|
15
|
+
*/
|
|
16
|
+
synthesize?: (text: string) => Promise<Blob>;
|
|
17
|
+
/** Disable the button (non-interactive). */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Events fired by `<kai-voice-output>`. */
|
|
22
|
+
interface Events {
|
|
23
|
+
/** Playback started or stopped — drive your own UI in sync. Fires on real
|
|
24
|
+
* transitions only (manual click and programmatic speak()/stop()), never on mount. */
|
|
25
|
+
'kai-speaking-change': { speaking: boolean };
|
|
26
|
+
/** The model path (`synthesize`) resolved audio — the raw `Blob` before playback. */
|
|
27
|
+
'kai-synthesized': { blob: Blob };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* `<kai-voice-output>` — a speaker button that reads `text` aloud. Native by
|
|
32
|
+
* default (`speechSynthesis`); set `el.synthesize` to route through your TTS
|
|
33
|
+
* model instead. The output sibling of `<kai-voice-input>`. Emits
|
|
34
|
+
* `kai-speaking-change` and (model path) `kai-synthesized`.
|
|
35
|
+
*/
|
|
36
|
+
defineWebComponent<Props, Events>('kai-voice-output', {
|
|
37
|
+
text: '',
|
|
38
|
+
autoplay: false,
|
|
39
|
+
synthesize: undefined,
|
|
40
|
+
disabled: false,
|
|
41
|
+
}, (props, { dispatch, flag, expose }) => {
|
|
42
|
+
// Pattern C: the VoiceOutput component owns playback; it hands up a
|
|
43
|
+
// speak/pause/resume/stop controller. The facade captures it and exposes
|
|
44
|
+
// delegating methods (manual + programmatic run the same path, so both emit
|
|
45
|
+
// the same kai-speaking-change / kai-synthesized events).
|
|
46
|
+
let controller: VoiceOutputController | undefined;
|
|
47
|
+
expose({
|
|
48
|
+
/** Speak the current `text` (native, or via `synthesize` if set). */
|
|
49
|
+
speak: () => controller?.speak(),
|
|
50
|
+
/** Pause playback (resumable). */
|
|
51
|
+
pause: () => controller?.pause(),
|
|
52
|
+
/** Resume paused playback. */
|
|
53
|
+
resume: () => controller?.resume(),
|
|
54
|
+
/** Stop playback and reset. */
|
|
55
|
+
stop: () => controller?.stop(),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<VoiceOutput
|
|
60
|
+
text={(props.text as string | undefined) ?? ''}
|
|
61
|
+
autoplay={flag('autoplay')}
|
|
62
|
+
disabled={flag('disabled')}
|
|
63
|
+
onSynthesize={props.synthesize ? (text) => props.synthesize!(text) : undefined}
|
|
64
|
+
onSpeakingChange={(speaking) => dispatch('kai-speaking-change', { speaking })}
|
|
65
|
+
onSynthesized={(blob) => dispatch('kai-synthesized', { blob })}
|
|
66
|
+
controllerRef={(c) => (controller = c)}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { ModelOption, SearchFilters, ConversationScope, ConversationSummary
|
|
|
5
5
|
export { cn } from './utils/cn';
|
|
6
6
|
|
|
7
7
|
// Layer 1: Headless Primitives
|
|
8
|
+
export { createKaiChat } from './primitives/create-kai-chat';
|
|
9
|
+
export type { CreateKaiChatOptions, KaiChatStore } from './primitives/create-kai-chat';
|
|
8
10
|
export { useAutoResize } from './primitives/use-auto-resize';
|
|
9
11
|
export { useStickToBottom } from './primitives/use-stick-to-bottom';
|
|
10
12
|
export { useTextStream } from './primitives/use-text-stream';
|
|
@@ -204,8 +206,6 @@ export {
|
|
|
204
206
|
PromptInput, PromptInputTextarea, PromptInputActions, PromptInputAction,
|
|
205
207
|
usePromptInput,
|
|
206
208
|
} from './components/prompt-input';
|
|
207
|
-
export { SlashCommand } from './components/slash-command';
|
|
208
|
-
export type { SlashCommandItem, SlashCommandProps } from './components/slash-command';
|
|
209
209
|
export { ResponseStream } from './components/response-stream';
|
|
210
210
|
export { Markdown } from './components/markdown';
|
|
211
211
|
export { CodeBlock, CodeBlockCode, CodeBlockGroup } from './components/code-block';
|
|
@@ -281,3 +281,6 @@ export type {
|
|
|
281
281
|
AttachmentInfoProps, AttachmentRemoveProps, AttachmentEmptyProps,
|
|
282
282
|
AttachmentHoverCardProps, AttachmentHoverCardTriggerProps, AttachmentHoverCardContentProps,
|
|
283
283
|
} from './components/attachments';
|
|
284
|
+
|
|
285
|
+
// Chat message types — public API for consumers who need to type their own message arrays.
|
|
286
|
+
export type { ChatMessage, ChatMessageAction, CustomAction, AvatarData, FeedbackVote } from './elements/chat-types';
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"properties": {
|
|
9
9
|
"mode": {
|
|
10
10
|
"type": "string",
|
|
11
|
-
"enum": ["select"],
|
|
11
|
+
"enum": ["select", "progress"],
|
|
12
12
|
"default": "select",
|
|
13
|
-
"description": "
|
|
13
|
+
"description": "'select' (default) = toggle + a confirm button that emits `submit`. 'progress' = an onboarding-checklist look (header `done / total` count, circular indicators, per-item title/description, no confirm button, where checking a row IS the action). Both share the selection model.",
|
|
14
14
|
"x-kai-mode": true
|
|
15
15
|
},
|
|
16
16
|
"heading": {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface EntityRef {
|
|
2
|
+
kind: string;
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
icon?: string;
|
|
6
|
+
promptText?: string;
|
|
7
|
+
data?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export type Segment =
|
|
10
|
+
| { type: 'text'; text: string }
|
|
11
|
+
| { type: 'entity'; entity: EntityRef };
|
|
12
|
+
export type ComposerDoc = Segment[];
|
|
13
|
+
|
|
14
|
+
export function normalizeValue(value: string | ComposerDoc | null | undefined): ComposerDoc {
|
|
15
|
+
if (value == null) return [];
|
|
16
|
+
if (typeof value === 'string') return value.length ? [{ type: 'text', text: value }] : [];
|
|
17
|
+
const out: ComposerDoc = [];
|
|
18
|
+
for (const seg of value) {
|
|
19
|
+
if (seg.type === 'text') {
|
|
20
|
+
if (!seg.text) continue;
|
|
21
|
+
const last = out[out.length - 1];
|
|
22
|
+
// Replace (don't mutate) the prior segment so every object in the returned
|
|
23
|
+
// doc is freshly built and never an aliased/held reference.
|
|
24
|
+
if (last && last.type === 'text') out[out.length - 1] = { type: 'text', text: last.text + seg.text };
|
|
25
|
+
else out.push({ type: 'text', text: seg.text });
|
|
26
|
+
} else {
|
|
27
|
+
out.push({ type: 'entity', entity: seg.entity });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function serializeToText(
|
|
34
|
+
doc: ComposerDoc,
|
|
35
|
+
opts?: { entity?: (e: EntityRef) => string },
|
|
36
|
+
): string {
|
|
37
|
+
let out = '';
|
|
38
|
+
for (const seg of doc) {
|
|
39
|
+
if (seg.type === 'text') out += seg.text;
|
|
40
|
+
else out += opts?.entity ? opts.entity(seg.entity) : (seg.entity.promptText ?? seg.entity.label);
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
export function entitiesOf(doc: ComposerDoc): EntityRef[] {
|
|
45
|
+
return doc.filter((s): s is Extract<Segment, { type: 'entity' }> => s.type === 'entity').map((s) => s.entity);
|
|
46
|
+
}
|
|
47
|
+
export function docIsEmpty(doc: ComposerDoc): boolean {
|
|
48
|
+
return doc.every((s) => s.type === 'text' && s.text.length === 0);
|
|
49
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface ActiveTrigger { char: string; query: string; start: number; }
|
|
2
|
+
|
|
3
|
+
export function detectTrigger(text: string, caret: number, chars: string[]): ActiveTrigger | null {
|
|
4
|
+
// Caret is a position within `text` (0..text.length). Out-of-range = no trigger.
|
|
5
|
+
if (caret < 0 || caret > text.length) return null;
|
|
6
|
+
for (let i = caret - 1; i >= 0; i--) {
|
|
7
|
+
const c = text[i];
|
|
8
|
+
if (/\s/.test(c)) return null; // hit whitespace before any trigger char
|
|
9
|
+
if (chars.includes(c)) {
|
|
10
|
+
const boundaryOk = i === 0 || /\s/.test(text[i - 1]);
|
|
11
|
+
if (!boundaryOk) return null;
|
|
12
|
+
return { char: c, query: text.slice(i + 1, caret), start: i };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Wraps detectTrigger with a list of trigger definitions.
|
|
20
|
+
* Returns { def, query, start } when a trigger is active, else null.
|
|
21
|
+
* Accepts generic defs so it does not import from composer.tsx (no cycle).
|
|
22
|
+
*/
|
|
23
|
+
export function activeTriggerFor<T extends { char: string; kind: string }>(
|
|
24
|
+
text: string,
|
|
25
|
+
caret: number,
|
|
26
|
+
defs: T[],
|
|
27
|
+
): { def: T; query: string; start: number } | null {
|
|
28
|
+
if (!defs.length) return null;
|
|
29
|
+
const chars = defs.map((d) => d.char);
|
|
30
|
+
const hit = detectTrigger(text, caret, chars);
|
|
31
|
+
if (!hit) return null;
|
|
32
|
+
const def = defs.find((d) => d.char === hit.char);
|
|
33
|
+
if (!def) return null;
|
|
34
|
+
return { def, query: hit.query, start: hit.start };
|
|
35
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/primitives/create-kai-chat.ts
|
|
2
|
+
import { createSignal } from 'solid-js';
|
|
3
|
+
import type { ChatMessage } from '../elements/chat-types';
|
|
4
|
+
import type { AttachmentData } from '../components/attachments';
|
|
5
|
+
import {
|
|
6
|
+
appendMessage, updateMessage, removeMessage, addSuggestion, removeSuggestion,
|
|
7
|
+
createAssistantStream, onStreamSettled, type AssistantStream, type SetMessages,
|
|
8
|
+
} from '../state';
|
|
9
|
+
|
|
10
|
+
export interface CreateKaiChatOptions {
|
|
11
|
+
/** Seed messages, read once at creation and copied. Later changes are ignored — drive updates through the returned ops. */
|
|
12
|
+
initialMessages?: ChatMessage[];
|
|
13
|
+
/** Seed suggestions, read once at creation and copied. Later changes are ignored. */
|
|
14
|
+
initialSuggestions?: string[];
|
|
15
|
+
onSubmit?: (detail: { value: string; attachments: AttachmentData[] }) => void | Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Solid store: consumer-owned chat state + the same ergonomic ops as `useKaiChat`. */
|
|
19
|
+
export interface KaiChatStore {
|
|
20
|
+
messages: () => ChatMessage[];
|
|
21
|
+
setMessages: SetMessages;
|
|
22
|
+
suggestions: () => string[];
|
|
23
|
+
loading: () => boolean;
|
|
24
|
+
append: (msg: ChatMessage) => void;
|
|
25
|
+
update: (id: string, patch: Partial<ChatMessage> | ((m: ChatMessage) => ChatMessage)) => void;
|
|
26
|
+
remove: (id: string) => void;
|
|
27
|
+
addSuggestion: (s: string) => void;
|
|
28
|
+
removeSuggestion: (s: string) => void;
|
|
29
|
+
clearSuggestions: () => void;
|
|
30
|
+
streamAssistant: (init?: Partial<ChatMessage>) => AssistantStream;
|
|
31
|
+
handleSubmit: (event: CustomEvent<{ value: string; attachments: AttachmentData[] }>) => void;
|
|
32
|
+
/** Spread onto `<kai-chat {...chat.bind} />` (reactive getters). Wire submit via `on:kai-submit={chat.handleSubmit}`. */
|
|
33
|
+
bind: { readonly messages: ChatMessage[]; readonly loading: boolean; readonly suggestions: string[] };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createKaiChat(options: CreateKaiChatOptions = {}): KaiChatStore {
|
|
37
|
+
// Copy the seed arrays so a caller mutating the array it passed in can't reach our state.
|
|
38
|
+
const [messages, setMessagesSignal] = createSignal<ChatMessage[]>([...(options.initialMessages ?? [])]);
|
|
39
|
+
const [suggestions, setSuggestions] = createSignal<string[]>([...(options.initialSuggestions ?? [])]);
|
|
40
|
+
const [loading, setLoading] = createSignal(false);
|
|
41
|
+
|
|
42
|
+
const setMessages: SetMessages = (updater) => setMessagesSignal((prev) => updater(prev));
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
messages,
|
|
46
|
+
setMessages,
|
|
47
|
+
suggestions,
|
|
48
|
+
loading,
|
|
49
|
+
append: (msg) => setMessages((prev) => appendMessage(prev, msg)),
|
|
50
|
+
update: (id, patch) => setMessages((prev) => updateMessage(prev, id, patch)),
|
|
51
|
+
remove: (id) => setMessages((prev) => removeMessage(prev, id)),
|
|
52
|
+
addSuggestion: (s) => setSuggestions((prev) => addSuggestion(prev, s)),
|
|
53
|
+
removeSuggestion: (s) => setSuggestions((prev) => removeSuggestion(prev, s)),
|
|
54
|
+
clearSuggestions: () => setSuggestions([]),
|
|
55
|
+
streamAssistant: (init) => {
|
|
56
|
+
setLoading(true);
|
|
57
|
+
return onStreamSettled(createAssistantStream(setMessages, init), () => setLoading(false));
|
|
58
|
+
},
|
|
59
|
+
handleSubmit: (event) => { void options.onSubmit?.(event.detail); },
|
|
60
|
+
bind: {
|
|
61
|
+
get messages() { return messages(); },
|
|
62
|
+
get loading() { return loading(); },
|
|
63
|
+
get suggestions() { return suggestions(); },
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -16,12 +16,20 @@ import { createRoot, createSignal } from 'solid-js';
|
|
|
16
16
|
import { createStore, produce } from 'solid-js/store';
|
|
17
17
|
import type { ToastPosition } from '../components/toast';
|
|
18
18
|
|
|
19
|
-
export type ToastVariant = 'neutral' | 'success';
|
|
19
|
+
export type ToastVariant = 'neutral' | 'success' | 'warning' | 'error' | 'info';
|
|
20
|
+
/** Visual treatment: `'pill'` (default, compact single-line) or `'card'` (richer
|
|
21
|
+
* rounded card with an optional description line). */
|
|
22
|
+
export type ToastAppearance = 'pill' | 'card';
|
|
20
23
|
|
|
21
24
|
export interface ToastConfig {
|
|
22
25
|
stack?: 'expanded' | 'collapsed';
|
|
23
26
|
position?: ToastPosition;
|
|
24
27
|
max?: number;
|
|
28
|
+
/** Default appearance for imperatively-raised toasts. Defaults to `'pill'`. */
|
|
29
|
+
appearance?: ToastAppearance;
|
|
30
|
+
/** Default high-contrast inverse treatment for imperatively-raised toasts.
|
|
31
|
+
* Defaults to `false`. */
|
|
32
|
+
inverse?: boolean;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
// Module-level config the imperative singleton regions inherit. Defaults match
|
|
@@ -33,6 +41,11 @@ function applyConfig(el: HTMLElement): void {
|
|
|
33
41
|
if (toastConfig.stack) el.setAttribute('stack', toastConfig.stack);
|
|
34
42
|
if (toastConfig.position) el.setAttribute('position', toastConfig.position);
|
|
35
43
|
if (toastConfig.max !== undefined) el.setAttribute('max', String(toastConfig.max));
|
|
44
|
+
if (toastConfig.appearance) el.setAttribute('appearance', toastConfig.appearance);
|
|
45
|
+
if (toastConfig.inverse !== undefined) {
|
|
46
|
+
if (toastConfig.inverse) el.setAttribute('inverse', '');
|
|
47
|
+
else el.removeAttribute('inverse');
|
|
48
|
+
}
|
|
36
49
|
}
|
|
37
50
|
|
|
38
51
|
/**
|
|
@@ -59,6 +72,15 @@ export interface ToastItem {
|
|
|
59
72
|
id: string;
|
|
60
73
|
message: string;
|
|
61
74
|
variant?: ToastVariant;
|
|
75
|
+
/** Visual treatment: `'pill'` (default, compact single-line) or `'card'` (richer
|
|
76
|
+
* rounded card with an optional description line). */
|
|
77
|
+
appearance?: ToastAppearance;
|
|
78
|
+
/** High-contrast inverse surface — works on either appearance, popping in light
|
|
79
|
+
* AND dark. Defaults to `false`. */
|
|
80
|
+
inverse?: boolean;
|
|
81
|
+
/** Secondary line shown below the message in the `'card'` appearance. The
|
|
82
|
+
* `'pill'` appearance ignores it. */
|
|
83
|
+
description?: string;
|
|
62
84
|
action?: ToastAction;
|
|
63
85
|
/** Auto-dismiss delay in ms. `0` = sticky (never auto-dismisses). When an
|
|
64
86
|
* `action` is present the effective floor is 4000ms so it stays long enough
|
|
@@ -77,6 +99,13 @@ export interface ToastItem {
|
|
|
77
99
|
export interface ToastOptions {
|
|
78
100
|
id?: string;
|
|
79
101
|
variant?: ToastVariant;
|
|
102
|
+
/** Visual treatment: `'pill'` (default) or `'card'`. Falls back to the value set
|
|
103
|
+
* via `configureToasts`, then `'pill'`. */
|
|
104
|
+
appearance?: ToastAppearance;
|
|
105
|
+
/** High-contrast inverse surface. Falls back to `configureToasts`, then `false`. */
|
|
106
|
+
inverse?: boolean;
|
|
107
|
+
/** Secondary line shown below the message in the `'card'` appearance. */
|
|
108
|
+
description?: string;
|
|
80
109
|
action?: ToastAction;
|
|
81
110
|
duration?: number;
|
|
82
111
|
dismissible?: boolean;
|
|
@@ -193,6 +222,9 @@ function raise(message: string, opts: ToastOptions | undefined, variant: ToastVa
|
|
|
193
222
|
id,
|
|
194
223
|
message,
|
|
195
224
|
variant: opts?.variant ?? variant,
|
|
225
|
+
appearance: opts?.appearance ?? toastConfig.appearance ?? 'pill',
|
|
226
|
+
inverse: opts?.inverse ?? toastConfig.inverse ?? false,
|
|
227
|
+
description: opts?.description,
|
|
196
228
|
action: opts?.action,
|
|
197
229
|
duration: opts?.duration,
|
|
198
230
|
dismissible: opts?.dismissible,
|
|
@@ -204,8 +236,14 @@ function raise(message: string, opts: ToastOptions | undefined, variant: ToastVa
|
|
|
204
236
|
|
|
205
237
|
export interface ToastFn {
|
|
206
238
|
(message: string, opts?: ToastOptions): ToastHandle;
|
|
207
|
-
/** Raise a success (
|
|
239
|
+
/** Raise a success (green check) toast. */
|
|
208
240
|
success: (message: string, opts?: ToastOptions) => ToastHandle;
|
|
241
|
+
/** Raise a warning (amber) toast — e.g. an agent needs your input. */
|
|
242
|
+
warning: (message: string, opts?: ToastOptions) => ToastHandle;
|
|
243
|
+
/** Raise an error (destructive/red) toast — e.g. an agent failed. */
|
|
244
|
+
error: (message: string, opts?: ToastOptions) => ToastHandle;
|
|
245
|
+
/** Raise an info (blue) toast. */
|
|
246
|
+
info: (message: string, opts?: ToastOptions) => ToastHandle;
|
|
209
247
|
/** Dismiss a toast by id. */
|
|
210
248
|
dismiss: (id: string) => void;
|
|
211
249
|
/** Dismiss every active toast. */
|
|
@@ -226,6 +264,9 @@ export const toast: ToastFn = Object.assign(
|
|
|
226
264
|
(message: string, opts?: ToastOptions) => raise(message, opts, 'neutral'),
|
|
227
265
|
{
|
|
228
266
|
success: (message: string, opts?: ToastOptions) => raise(message, opts, 'success'),
|
|
267
|
+
warning: (message: string, opts?: ToastOptions) => raise(message, opts, 'warning'),
|
|
268
|
+
error: (message: string, opts?: ToastOptions) => raise(message, opts, 'error'),
|
|
269
|
+
info: (message: string, opts?: ToastOptions) => raise(message, opts, 'info'),
|
|
229
270
|
dismiss,
|
|
230
271
|
clear: () => setToasts([]),
|
|
231
272
|
},
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { createSignal, onCleanup } from 'solid-js';
|
|
2
|
+
|
|
3
|
+
// Minimal Web Speech API surface. The DOM lib doesn't ship these types reliably
|
|
4
|
+
// across TS versions, so we declare only what we touch. `webkitSpeechRecognition`
|
|
5
|
+
// is the Chrome/Safari-prefixed constructor; the unprefixed one is rare.
|
|
6
|
+
interface SpeechRecognitionAlternative {
|
|
7
|
+
transcript: string;
|
|
8
|
+
}
|
|
9
|
+
interface SpeechRecognitionResult {
|
|
10
|
+
readonly isFinal: boolean;
|
|
11
|
+
readonly length: number;
|
|
12
|
+
[index: number]: SpeechRecognitionAlternative;
|
|
13
|
+
}
|
|
14
|
+
interface SpeechRecognitionResultList {
|
|
15
|
+
readonly length: number;
|
|
16
|
+
[index: number]: SpeechRecognitionResult;
|
|
17
|
+
}
|
|
18
|
+
interface SpeechRecognitionEventLike {
|
|
19
|
+
readonly resultIndex: number;
|
|
20
|
+
readonly results: SpeechRecognitionResultList;
|
|
21
|
+
}
|
|
22
|
+
interface SpeechRecognitionLike {
|
|
23
|
+
lang: string;
|
|
24
|
+
continuous: boolean;
|
|
25
|
+
interimResults: boolean;
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
abort(): void;
|
|
29
|
+
onresult: ((event: SpeechRecognitionEventLike) => void) | null;
|
|
30
|
+
onerror: ((event: { error?: string }) => void) | null;
|
|
31
|
+
onend: (() => void) | null;
|
|
32
|
+
}
|
|
33
|
+
type SpeechRecognitionCtor = new () => SpeechRecognitionLike;
|
|
34
|
+
|
|
35
|
+
function getCtor(): SpeechRecognitionCtor | undefined {
|
|
36
|
+
if (typeof window === 'undefined') return undefined;
|
|
37
|
+
const w = window as unknown as {
|
|
38
|
+
SpeechRecognition?: SpeechRecognitionCtor;
|
|
39
|
+
webkitSpeechRecognition?: SpeechRecognitionCtor;
|
|
40
|
+
};
|
|
41
|
+
return w.SpeechRecognition ?? w.webkitSpeechRecognition;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface UseSpeechRecognitionOptions {
|
|
45
|
+
/** Emit live, non-final partials via the `onInterim` callback passed to start(). */
|
|
46
|
+
interim?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface SpeechRecognitionStartOptions {
|
|
50
|
+
/** BCP-47 language tag, e.g. `en-US`. */
|
|
51
|
+
lang?: string;
|
|
52
|
+
/** Called with each interim (non-final) transcript when `interim` is enabled. */
|
|
53
|
+
onInterim?: (text: string) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Wrap the browser's Web Speech `SpeechRecognition` (Chrome/Safari; no Firefox).
|
|
58
|
+
* Mirrors `useVoiceRecorder`'s shape: an `isListening` signal, an `error` signal,
|
|
59
|
+
* and `start`/`stop` controls. `start()` resolves with the final transcript when
|
|
60
|
+
* recognition ends; `stop()` ends the in-progress session (resolving start()).
|
|
61
|
+
*
|
|
62
|
+
* Caveat (documented at the element): in Chrome this is cloud-based — audio is
|
|
63
|
+
* sent to Google — so it is "native to the browser," not on-device/private.
|
|
64
|
+
*/
|
|
65
|
+
export function useSpeechRecognition(options: UseSpeechRecognitionOptions = {}) {
|
|
66
|
+
const [isListening, setIsListening] = createSignal(false);
|
|
67
|
+
const [error, setError] = createSignal<string | null>(null);
|
|
68
|
+
|
|
69
|
+
const isSupported = getCtor() !== undefined;
|
|
70
|
+
|
|
71
|
+
let recognition: SpeechRecognitionLike | undefined;
|
|
72
|
+
let resolveText: ((text: string) => void) | undefined;
|
|
73
|
+
let finalText = '';
|
|
74
|
+
|
|
75
|
+
function start(opts: SpeechRecognitionStartOptions = {}): Promise<string> {
|
|
76
|
+
setError(null);
|
|
77
|
+
finalText = '';
|
|
78
|
+
const Ctor = getCtor();
|
|
79
|
+
if (!Ctor) {
|
|
80
|
+
const message = 'SpeechRecognition is not supported in this browser';
|
|
81
|
+
setError(message);
|
|
82
|
+
return Promise.reject(new Error(message));
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
recognition = new Ctor();
|
|
86
|
+
if (opts.lang) recognition.lang = opts.lang;
|
|
87
|
+
recognition.interimResults = options.interim === true;
|
|
88
|
+
recognition.continuous = false;
|
|
89
|
+
|
|
90
|
+
recognition.onresult = (event) => {
|
|
91
|
+
let interim = '';
|
|
92
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
93
|
+
const result = event.results[i];
|
|
94
|
+
const transcript = result[0]?.transcript ?? '';
|
|
95
|
+
if (result.isFinal) {
|
|
96
|
+
finalText += transcript;
|
|
97
|
+
} else {
|
|
98
|
+
interim += transcript;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (interim && opts.onInterim) opts.onInterim(interim);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
recognition.onerror = (event) => {
|
|
105
|
+
setError(event.error ?? 'Speech recognition error');
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
recognition.onend = () => {
|
|
109
|
+
setIsListening(false);
|
|
110
|
+
resolveText?.(finalText);
|
|
111
|
+
recognition = undefined;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
recognition.start();
|
|
115
|
+
setIsListening(true);
|
|
116
|
+
return new Promise<string>((resolve) => { resolveText = resolve; });
|
|
117
|
+
} catch (err) {
|
|
118
|
+
setError(err instanceof Error ? err.message : 'Speech recognition failed to start');
|
|
119
|
+
setIsListening(false);
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function stop() {
|
|
125
|
+
if (recognition && isListening()) {
|
|
126
|
+
recognition.stop();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
onCleanup(() => {
|
|
131
|
+
if (recognition) recognition.abort();
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return { isSupported, isListening, error, start, stop };
|
|
135
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Framework-neutral state core for @kitn.ai/ui. Pure functions over ChatMessage[]
|
|
2
|
+
// + a typed streaming handle. No React/Solid runtime — drives a consumer setter.
|
|
3
|
+
export { appendMessage, upsertMessage, updateMessage, removeMessage, appendContent } from './messages';
|
|
4
|
+
export { addSuggestion, removeSuggestion } from './suggestions';
|
|
5
|
+
export { createAssistantStream, onStreamSettled } from './stream';
|
|
6
|
+
export type { SetMessages, AssistantStream } from './stream';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ChatMessage } from '../elements/chat-types';
|
|
2
|
+
|
|
3
|
+
/** Append a message; returns a new array. */
|
|
4
|
+
export function appendMessage(messages: ChatMessage[], msg: ChatMessage): ChatMessage[] {
|
|
5
|
+
return [...messages, msg];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Replace a same-id message, or append when absent. */
|
|
9
|
+
export function upsertMessage(messages: ChatMessage[], msg: ChatMessage): ChatMessage[] {
|
|
10
|
+
const i = messages.findIndex((x) => x.id === msg.id);
|
|
11
|
+
if (i === -1) return [...messages, msg];
|
|
12
|
+
const next = messages.slice();
|
|
13
|
+
next[i] = msg;
|
|
14
|
+
return next;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Patch the matched message (object patch or updater). Untouched items keep their reference. */
|
|
18
|
+
export function updateMessage(
|
|
19
|
+
messages: ChatMessage[],
|
|
20
|
+
id: string,
|
|
21
|
+
patch: Partial<ChatMessage> | ((m: ChatMessage) => ChatMessage),
|
|
22
|
+
): ChatMessage[] {
|
|
23
|
+
return messages.map((x) =>
|
|
24
|
+
x.id === id ? (typeof patch === 'function' ? patch(x) : { ...x, ...patch }) : x,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Remove the matched message. */
|
|
29
|
+
export function removeMessage(messages: ChatMessage[], id: string): ChatMessage[] {
|
|
30
|
+
return messages.filter((x) => x.id !== id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Streaming primitive: concatenate `delta` onto the matched message's content. */
|
|
34
|
+
export function appendContent(messages: ChatMessage[], id: string, delta: string): ChatMessage[] {
|
|
35
|
+
return messages.map((x) => (x.id === id ? { ...x, content: x.content + delta } : x));
|
|
36
|
+
}
|