@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
|
@@ -20,7 +20,7 @@ export interface MessageProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
20
20
|
function Message(props: MessageProps) {
|
|
21
21
|
const [local, rest] = splitProps(props, ["children", "class"]);
|
|
22
22
|
return (
|
|
23
|
-
<div class={cn("flex items-start gap-3", local.class)} {...rest}>
|
|
23
|
+
<div part="row" class={cn("flex items-start gap-3", local.class)} {...rest}>
|
|
24
24
|
{local.children}
|
|
25
25
|
</div>
|
|
26
26
|
);
|
|
@@ -38,6 +38,7 @@ export interface MessageAvatarProps {
|
|
|
38
38
|
function MessageAvatar(props: MessageAvatarProps) {
|
|
39
39
|
return (
|
|
40
40
|
<div
|
|
41
|
+
part="avatar"
|
|
41
42
|
class={cn("h-8 w-8 shrink-0 overflow-hidden rounded-full", props.class)}
|
|
42
43
|
>
|
|
43
44
|
<Show
|
|
@@ -65,10 +66,14 @@ function MessageAvatar(props: MessageAvatarProps) {
|
|
|
65
66
|
export interface MessageContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
66
67
|
children: JSX.Element | string;
|
|
67
68
|
markdown?: boolean;
|
|
69
|
+
/** `::part` name(s) exposed on the content node. The body passes
|
|
70
|
+
* `"bubble content"` so consumers can target either the rounded bubble or the
|
|
71
|
+
* text region from outside the shadow boundary. */
|
|
72
|
+
part?: string;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
function MessageContent(props: MessageContentProps) {
|
|
71
|
-
const [local, rest] = splitProps(props, ["children", "markdown", "class"]);
|
|
76
|
+
const [local, rest] = splitProps(props, ["children", "markdown", "class", "part"]);
|
|
72
77
|
const config = useChatConfig();
|
|
73
78
|
const classNames = () =>
|
|
74
79
|
cn(
|
|
@@ -81,12 +86,12 @@ function MessageContent(props: MessageContentProps) {
|
|
|
81
86
|
<Show
|
|
82
87
|
when={local.markdown}
|
|
83
88
|
fallback={
|
|
84
|
-
<div class={classNames()} {...rest}>
|
|
89
|
+
<div part={local.part} class={classNames()} {...rest}>
|
|
85
90
|
{local.children}
|
|
86
91
|
</div>
|
|
87
92
|
}
|
|
88
93
|
>
|
|
89
|
-
<Markdown content={local.children as string} class={classNames()} />
|
|
94
|
+
<Markdown part={local.part} content={local.children as string} class={classNames()} />
|
|
90
95
|
</Show>
|
|
91
96
|
);
|
|
92
97
|
}
|
|
@@ -163,6 +168,7 @@ function feedbackVoteOf(a: ChatMessageAction | CustomAction): FeedbackVote | und
|
|
|
163
168
|
function MessageActionBar(props: MessageActionBarProps) {
|
|
164
169
|
return (
|
|
165
170
|
<MessageActions
|
|
171
|
+
part="actions"
|
|
166
172
|
class={cn(
|
|
167
173
|
'mt-1 flex gap-0',
|
|
168
174
|
props.reveal === 'hover' && 'opacity-0 transition-opacity group-hover:opacity-100',
|
|
@@ -273,6 +279,14 @@ export interface MessageBodyProps {
|
|
|
273
279
|
activeFeedback?: FeedbackVote;
|
|
274
280
|
/** When true, the copy button shows its "copied" check icon. */
|
|
275
281
|
copied?: boolean;
|
|
282
|
+
/** INJECT slot projected at the TOP of the body — above the reasoning / tools /
|
|
283
|
+
* content. A per-message header: a model-name label, a role + timestamp line.
|
|
284
|
+
* In the `<kai-message>` shadow this is `<slot name="before-body" />`. */
|
|
285
|
+
beforeBody?: JSX.Element;
|
|
286
|
+
/** INJECT slot projected at the BOTTOM of the body — below the action bar. A
|
|
287
|
+
* citation / sources row, a token-cost / latency line. In the `<kai-message>`
|
|
288
|
+
* shadow this is `<slot name="after-body" />`. */
|
|
289
|
+
afterBody?: JSX.Element;
|
|
276
290
|
}
|
|
277
291
|
|
|
278
292
|
/**
|
|
@@ -286,6 +300,8 @@ export interface MessageBodyProps {
|
|
|
286
300
|
function MessageBody(props: MessageBodyProps) {
|
|
287
301
|
return (
|
|
288
302
|
<>
|
|
303
|
+
{/* before-body (inject): a per-message header above everything else. */}
|
|
304
|
+
<Show when={props.beforeBody}>{props.beforeBody}</Show>
|
|
289
305
|
<Show when={props.reasoning}>
|
|
290
306
|
{(r) => (
|
|
291
307
|
<Reasoning class="mb-2 w-full">
|
|
@@ -303,6 +319,7 @@ function MessageBody(props: MessageBodyProps) {
|
|
|
303
319
|
</Attachments>
|
|
304
320
|
</Show>
|
|
305
321
|
<MessageContent
|
|
322
|
+
part="bubble content"
|
|
306
323
|
markdown={props.markdown}
|
|
307
324
|
class={props.isUser
|
|
308
325
|
? 'bg-muted text-primary max-w-[85%] rounded-2xl px-4 py-2'
|
|
@@ -319,6 +336,8 @@ function MessageBody(props: MessageBodyProps) {
|
|
|
319
336
|
onAction={(id) => props.onAction?.(id)}
|
|
320
337
|
/>
|
|
321
338
|
</Show>
|
|
339
|
+
{/* after-body (inject): a citation row / cost line below the action bar. */}
|
|
340
|
+
<Show when={props.afterBody}>{props.afterBody}</Show>
|
|
322
341
|
</>
|
|
323
342
|
);
|
|
324
343
|
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { splitProps, For, Show, createSignal } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
|
-
import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '../ui/dropdown';
|
|
3
|
+
import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem, type DropdownController } from '../ui/dropdown';
|
|
4
4
|
import { Button } from '../ui/button';
|
|
5
5
|
import type { ModelOption } from '../types';
|
|
6
6
|
|
|
7
|
-
export interface ModelSwitcherProps {
|
|
7
|
+
export interface ModelSwitcherProps {
|
|
8
|
+
models: ModelOption[];
|
|
9
|
+
currentModelId: string;
|
|
10
|
+
onModelChange: (modelId: string) => void;
|
|
11
|
+
class?: string;
|
|
12
|
+
/** Initial open state of the dropdown (uncontrolled seed). */
|
|
13
|
+
defaultOpen?: boolean;
|
|
14
|
+
/** Disable the trigger — click/keyboard no longer open the dropdown. */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
/** Receive the dropdown's open controller (forwarded from the inner Dropdown). */
|
|
17
|
+
controllerRef?: (api: DropdownController) => void;
|
|
18
|
+
}
|
|
8
19
|
|
|
9
20
|
const Chevron = (props: { class?: string }) => (
|
|
10
21
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class={props.class}>
|
|
@@ -25,7 +36,7 @@ function ModelRow(props: { model: ModelOption; currentModelId: string; onModelCh
|
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
export function ModelSwitcher(props: ModelSwitcherProps) {
|
|
28
|
-
const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class']);
|
|
39
|
+
const [local] = splitProps(props, ['models', 'currentModelId', 'onModelChange', 'class', 'defaultOpen', 'disabled', 'controllerRef']);
|
|
29
40
|
const currentModel = () => local.models.find((m) => m.id === local.currentModelId);
|
|
30
41
|
|
|
31
42
|
// Ungrouped models list first; grouped models collect under their group name,
|
|
@@ -44,7 +55,11 @@ export function ModelSwitcher(props: ModelSwitcherProps) {
|
|
|
44
55
|
|
|
45
56
|
return (
|
|
46
57
|
<Show when={local.models.length > 1}>
|
|
47
|
-
<Dropdown
|
|
58
|
+
<Dropdown
|
|
59
|
+
defaultOpen={local.defaultOpen}
|
|
60
|
+
disabled={local.disabled}
|
|
61
|
+
controllerRef={local.controllerRef}
|
|
62
|
+
>
|
|
48
63
|
<DropdownTrigger as={(triggerProps: any) => (
|
|
49
64
|
<Button variant="ghost" size="sm" class={cn('gap-1 text-meta text-muted-foreground', local.class)} {...triggerProps}>
|
|
50
65
|
{currentModel()?.name ?? local.currentModelId}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import { type JSX, splitProps, createSignal, createContext, useContext
|
|
1
|
+
import { type JSX, splitProps, createSignal, createContext, useContext } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { useChatConfig, textClass } from '../primitives/chat-config';
|
|
4
|
+
import { Composer, type TriggerDef, type ComposerChange } from './composer';
|
|
5
|
+
import type { ComposerDoc } from '../primitives/composer-model';
|
|
4
6
|
|
|
5
7
|
// --- Context ---
|
|
6
8
|
|
|
7
9
|
interface PromptInputContextType {
|
|
8
10
|
isLoading: boolean;
|
|
9
|
-
|
|
11
|
+
// A string (controlled text) or a ComposerDoc (a seed that pre-populates pills).
|
|
12
|
+
value: () => string | ComposerDoc;
|
|
10
13
|
setValue: (value: string) => void;
|
|
11
14
|
maxHeight: number | string;
|
|
12
15
|
onSubmit?: () => void;
|
|
13
16
|
disabled?: boolean;
|
|
14
|
-
textareaRef:
|
|
15
|
-
setTextareaRef: (el:
|
|
17
|
+
textareaRef: HTMLElement | undefined;
|
|
18
|
+
setTextareaRef: (el: HTMLElement) => void;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
const PromptInputContext = createContext<PromptInputContextType>();
|
|
@@ -27,7 +30,8 @@ function usePromptInput() {
|
|
|
27
30
|
|
|
28
31
|
export interface PromptInputProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
29
32
|
isLoading?: boolean;
|
|
30
|
-
|
|
33
|
+
/** String = controlled text; ComposerDoc = a seed that pre-populates pills. */
|
|
34
|
+
value?: string | ComposerDoc;
|
|
31
35
|
onValueChange?: (value: string) => void;
|
|
32
36
|
maxHeight?: number | string;
|
|
33
37
|
onSubmit?: () => void;
|
|
@@ -41,8 +45,8 @@ function PromptInput(props: PromptInputProps) {
|
|
|
41
45
|
'children', 'disabled', 'class', 'onClick',
|
|
42
46
|
]);
|
|
43
47
|
|
|
44
|
-
const [internalValue, setInternalValue] = createSignal(local.value ?? '');
|
|
45
|
-
let textareaRef:
|
|
48
|
+
const [internalValue, setInternalValue] = createSignal<string | ComposerDoc>(local.value ?? '');
|
|
49
|
+
let textareaRef: HTMLElement | undefined;
|
|
46
50
|
|
|
47
51
|
const handleChange = (newValue: string) => {
|
|
48
52
|
setInternalValue(newValue);
|
|
@@ -70,13 +74,14 @@ function PromptInput(props: PromptInputProps) {
|
|
|
70
74
|
}}
|
|
71
75
|
>
|
|
72
76
|
<div
|
|
77
|
+
data-prompt-input
|
|
73
78
|
onClick={handleClick}
|
|
74
79
|
class={cn(
|
|
75
80
|
// The inner textarea neutralizes its own ring (focus-visible:ring-0),
|
|
76
81
|
// so the FRAME owns the focus affordance: a blue ring whenever a
|
|
77
82
|
// control inside it is focused. Without this the composer had no
|
|
78
83
|
// visible keyboard-focus state.
|
|
79
|
-
'bg-
|
|
84
|
+
'bg-surface cursor-text rounded-xl p-2 shadow-xs',
|
|
80
85
|
'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-0',
|
|
81
86
|
local.disabled && 'cursor-not-allowed opacity-60',
|
|
82
87
|
local.class
|
|
@@ -93,83 +98,49 @@ function PromptInput(props: PromptInputProps) {
|
|
|
93
98
|
|
|
94
99
|
export interface PromptInputTextareaProps extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
95
100
|
disableAutosize?: boolean;
|
|
101
|
+
/** Rich entity triggers (`/`, `@`) forwarded to the composer. */
|
|
102
|
+
triggers?: TriggerDef[];
|
|
103
|
+
/** Default icon per entity kind (kind → image src), forwarded to the composer. */
|
|
104
|
+
kindIcons?: Record<string, string>;
|
|
105
|
+
/** Structured change (doc + entities) from the composer, on every edit. */
|
|
106
|
+
onComposerChange?: (change: ComposerChange) => void;
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
function PromptInputTextarea(props: PromptInputTextareaProps) {
|
|
99
|
-
const [local
|
|
110
|
+
const [local] = splitProps(props, ['class', 'placeholder', 'aria-label', 'triggers', 'kindIcons', 'onComposerChange']);
|
|
100
111
|
const ctx = usePromptInput();
|
|
101
112
|
const config = useChatConfig();
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
function handleRef(el: HTMLTextAreaElement) {
|
|
115
|
-
ctx.setTextareaRef(el);
|
|
116
|
-
adjustHeight(el);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
createEffect(on(
|
|
120
|
-
() => [ctx.value(), ctx.maxHeight, local.disableAutosize],
|
|
121
|
-
() => {
|
|
122
|
-
if (ctx.textareaRef && !local.disableAutosize) {
|
|
123
|
-
adjustHeight(ctx.textareaRef);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
));
|
|
127
|
-
|
|
128
|
-
function handleInput(e: InputEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
129
|
-
const el = e.currentTarget;
|
|
130
|
-
let value = el.value;
|
|
131
|
-
// Disallow leading whitespace — a prompt can't start with a space or blank
|
|
132
|
-
// line. Strip it (covers typing a space at the start AND pasting) and keep
|
|
133
|
-
// the caret in the right place.
|
|
134
|
-
if (/^\s/.test(value)) {
|
|
135
|
-
const stripped = value.replace(/^\s+/, '');
|
|
136
|
-
const removed = value.length - stripped.length;
|
|
137
|
-
const caret = Math.max(0, (el.selectionStart ?? 0) - removed);
|
|
138
|
-
el.value = stripped;
|
|
139
|
-
el.setSelectionRange(caret, caret);
|
|
140
|
-
value = stripped;
|
|
141
|
-
}
|
|
142
|
-
adjustHeight(el);
|
|
143
|
-
ctx.setValue(value);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function handleKeyDown(e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) {
|
|
147
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
148
|
-
e.preventDefault();
|
|
149
|
-
// A disabled composer is non-interactive: Enter must not submit.
|
|
150
|
-
if (!ctx.disabled) ctx.onSubmit?.();
|
|
151
|
-
}
|
|
152
|
-
if (typeof local.onKeyDown === 'function') {
|
|
153
|
-
(local.onKeyDown as (e: KeyboardEvent & { currentTarget: HTMLTextAreaElement }) => void)(e);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
114
|
+
// The editable mirrors the original <textarea>'s classes EXACTLY so swapping in
|
|
115
|
+
// the contenteditable composer is visually identical. The frame (PromptInput
|
|
116
|
+
// root) still owns radius/bg/padding/focus-ring; auto-grow is native to a
|
|
117
|
+
// contenteditable block (capped via max-height on the editable).
|
|
118
|
+
const editableClass = () =>
|
|
119
|
+
cn(
|
|
120
|
+
'text-foreground min-h-[44px] w-full bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0 overflow-y-auto whitespace-pre-wrap break-words',
|
|
121
|
+
textClass(config.proseSize()),
|
|
122
|
+
local.class,
|
|
123
|
+
);
|
|
156
124
|
|
|
157
125
|
return (
|
|
158
|
-
<
|
|
159
|
-
|
|
126
|
+
<Composer
|
|
127
|
+
bare
|
|
160
128
|
value={ctx.value()}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
class={cn(
|
|
164
|
-
// Typed text uses the normal foreground (light/dark), NOT the brand/accent
|
|
165
|
-
// color — the composer should read as plain text, not tinted.
|
|
166
|
-
'text-foreground min-h-[44px] w-full resize-none border-none bg-transparent shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
|
|
167
|
-
textClass(config.proseSize()),
|
|
168
|
-
local.class
|
|
169
|
-
)}
|
|
170
|
-
rows={1}
|
|
129
|
+
placeholder={local.placeholder as string | undefined}
|
|
130
|
+
ariaLabel={local['aria-label'] as string | undefined}
|
|
171
131
|
disabled={ctx.disabled}
|
|
172
|
-
{
|
|
132
|
+
maxHeight={ctx.maxHeight}
|
|
133
|
+
editableClass={editableClass()}
|
|
134
|
+
editableRef={(el) => ctx.setTextareaRef(el)}
|
|
135
|
+
triggers={local.triggers}
|
|
136
|
+
kindIcons={local.kindIcons}
|
|
137
|
+
// Surface the structured change (doc/entities) BEFORE the string value, so a
|
|
138
|
+
// consumer that enriches its events has the latest doc when value-change fires.
|
|
139
|
+
// A prompt can't start with whitespace — strip leading whitespace from the
|
|
140
|
+
// string value (parity with the old textarea); the controlled round-trip
|
|
141
|
+
// re-renders the editable so the stripped value shows too.
|
|
142
|
+
onChange={(c) => { local.onComposerChange?.(c); ctx.setValue(c.text.replace(/^\s+/, '')); }}
|
|
143
|
+
onSubmit={() => { if (!ctx.disabled) ctx.onSubmit?.(); }}
|
|
173
144
|
/>
|
|
174
145
|
);
|
|
175
146
|
}
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { type JSX, Show, splitProps } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { Button } from '../ui/button';
|
|
4
|
+
import { renderIcon } from '../ui/icon';
|
|
4
5
|
|
|
5
6
|
export interface PromptSuggestionProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
6
7
|
children: JSX.Element | string;
|
|
7
8
|
variant?: 'outline' | 'ghost' | 'default';
|
|
8
|
-
|
|
9
|
+
/** Row height for the `list` layout: `'md'` (default) or `'lg'` for taller
|
|
10
|
+
* rows (more vertical padding). Chips/block/highlight are unaffected. */
|
|
11
|
+
size?: 'md' | 'lg';
|
|
12
|
+
/** Optional leading icon (named icon, image URL/data-URI, or text). */
|
|
13
|
+
icon?: string;
|
|
9
14
|
highlight?: string;
|
|
10
15
|
/** Render as a full-width, left-aligned list row (the "suggested questions"
|
|
11
16
|
* idiom) instead of a rounded pill. Wraps long text. Ignored in highlight
|
|
12
17
|
* mode, which is always a list row. */
|
|
13
18
|
block?: boolean;
|
|
19
|
+
/** Render as a full-width "Ideas for you" list row: a leading icon, a
|
|
20
|
+
* left-aligned label, and a hover background. Like `block`, but keeps the
|
|
21
|
+
* leading icon. Ignored in highlight mode. */
|
|
22
|
+
list?: boolean;
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
function PromptSuggestion(props: PromptSuggestionProps) {
|
|
17
|
-
const [local, rest] = splitProps(props, ['children', 'variant', 'size', 'class', 'highlight', 'block']);
|
|
26
|
+
const [local, rest] = splitProps(props, ['children', 'variant', 'size', 'class', 'icon', 'highlight', 'block', 'list']);
|
|
27
|
+
const Icon = () => <Show when={local.icon}>{renderIcon(local.icon, { class: 'size-3.5 shrink-0' })}</Show>;
|
|
18
28
|
|
|
19
29
|
const isHighlightMode = () => local.highlight !== undefined && local.highlight.trim() !== '';
|
|
20
30
|
const content = () => typeof local.children === 'string' ? local.children : '';
|
|
@@ -24,29 +34,51 @@ function PromptSuggestion(props: PromptSuggestionProps) {
|
|
|
24
34
|
when={isHighlightMode()}
|
|
25
35
|
fallback={
|
|
26
36
|
<Show
|
|
27
|
-
when={local.
|
|
37
|
+
when={local.list}
|
|
28
38
|
fallback={
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
<Show
|
|
40
|
+
when={local.block}
|
|
41
|
+
fallback={
|
|
42
|
+
<Button
|
|
43
|
+
variant={local.variant ?? 'outline'}
|
|
44
|
+
size="lg"
|
|
45
|
+
class={cn('rounded-full', local.class)}
|
|
46
|
+
{...rest}
|
|
47
|
+
>
|
|
48
|
+
<Icon />
|
|
49
|
+
{local.children}
|
|
50
|
+
</Button>
|
|
51
|
+
}
|
|
34
52
|
>
|
|
35
|
-
|
|
36
|
-
|
|
53
|
+
<Button
|
|
54
|
+
variant={local.variant ?? 'outline'}
|
|
55
|
+
size="md"
|
|
56
|
+
class={cn(
|
|
57
|
+
'h-auto w-full cursor-pointer justify-start rounded-xl px-4 py-2.5',
|
|
58
|
+
'text-left text-sm leading-snug whitespace-normal text-pretty',
|
|
59
|
+
local.class,
|
|
60
|
+
)}
|
|
61
|
+
{...rest}
|
|
62
|
+
>
|
|
63
|
+
{local.children}
|
|
64
|
+
</Button>
|
|
65
|
+
</Show>
|
|
37
66
|
}
|
|
38
67
|
>
|
|
39
68
|
<Button
|
|
40
|
-
variant={local.variant ?? '
|
|
41
|
-
size=
|
|
69
|
+
variant={local.variant ?? 'ghost'}
|
|
70
|
+
size="md"
|
|
42
71
|
class={cn(
|
|
43
|
-
'h-auto w-full cursor-pointer justify-start rounded-xl px-
|
|
72
|
+
'group h-auto w-full cursor-pointer items-center justify-start gap-2.5 rounded-xl px-3',
|
|
73
|
+
local.size === 'lg' ? 'py-3.5' : 'py-2.5',
|
|
44
74
|
'text-left text-sm leading-snug whitespace-normal text-pretty',
|
|
75
|
+
'hover:bg-accent',
|
|
45
76
|
local.class,
|
|
46
77
|
)}
|
|
47
78
|
{...rest}
|
|
48
79
|
>
|
|
49
|
-
|
|
80
|
+
<Icon />
|
|
81
|
+
<span class="min-w-0 flex-1">{local.children}</span>
|
|
50
82
|
</Button>
|
|
51
83
|
</Show>
|
|
52
84
|
}
|
|
@@ -56,7 +88,7 @@ function PromptSuggestion(props: PromptSuggestionProps) {
|
|
|
56
88
|
fallback={
|
|
57
89
|
<Button
|
|
58
90
|
variant={local.variant ?? 'ghost'}
|
|
59
|
-
size=
|
|
91
|
+
size="sm"
|
|
60
92
|
class={cn(
|
|
61
93
|
'w-full cursor-pointer justify-start rounded-xl py-2',
|
|
62
94
|
'hover:bg-accent',
|
|
@@ -70,7 +102,7 @@ function PromptSuggestion(props: PromptSuggestionProps) {
|
|
|
70
102
|
>
|
|
71
103
|
<Button
|
|
72
104
|
variant={local.variant ?? 'ghost'}
|
|
73
|
-
size=
|
|
105
|
+
size="sm"
|
|
74
106
|
class={cn(
|
|
75
107
|
'w-full cursor-pointer justify-start gap-0 rounded-xl py-2',
|
|
76
108
|
'hover:bg-accent',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type JSX, splitProps, createSignal, createContext, useContext, createEffect, onCleanup, Show } from 'solid-js';
|
|
1
|
+
import { type JSX, type Accessor, splitProps, createSignal, createContext, useContext, createEffect, onCleanup, Show } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { ChevronDown } from 'lucide-solid';
|
|
4
4
|
import { Markdown } from './markdown';
|
|
@@ -7,8 +7,14 @@ import { observeContentHeight } from '../primitives/use-resize-observer';
|
|
|
7
7
|
interface ReasoningContextValue {
|
|
8
8
|
isOpen: () => boolean;
|
|
9
9
|
onOpenChange: (open: boolean) => void;
|
|
10
|
+
disabled: () => boolean;
|
|
10
11
|
}
|
|
11
12
|
|
|
13
|
+
/** Imperative open controller, handed to a parent (the kai-reasoning facade) via
|
|
14
|
+
* `controllerRef` so it can drive/observe open state — mirrors
|
|
15
|
+
* CollapsibleController/HoverCardController. */
|
|
16
|
+
export interface ReasoningController { open: Accessor<boolean>; setOpen: (v: boolean) => void; }
|
|
17
|
+
|
|
12
18
|
const ReasoningContext = createContext<ReasoningContextValue>();
|
|
13
19
|
|
|
14
20
|
function useReasoningContext() {
|
|
@@ -25,19 +31,30 @@ export interface ReasoningProps {
|
|
|
25
31
|
children: JSX.Element;
|
|
26
32
|
class?: string;
|
|
27
33
|
open?: boolean;
|
|
34
|
+
/** Initial open state (uncontrolled seed). */
|
|
35
|
+
defaultOpen?: boolean;
|
|
28
36
|
onOpenChange?: (open: boolean) => void;
|
|
29
37
|
isStreaming?: boolean;
|
|
38
|
+
/** Gate the trigger — programmatic control via the controller still works. */
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
/** Receive the open controller (open accessor + setOpen) once mounted. */
|
|
41
|
+
controllerRef?: (api: ReasoningController) => void;
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
function Reasoning(props: ReasoningProps) {
|
|
33
|
-
const [local] = splitProps(props, ['children', 'class', 'open', 'onOpenChange', 'isStreaming']);
|
|
34
|
-
const [internalOpen, setInternalOpen] = createSignal(false);
|
|
45
|
+
const [local] = splitProps(props, ['children', 'class', 'open', 'defaultOpen', 'onOpenChange', 'isStreaming', 'disabled', 'controllerRef']);
|
|
46
|
+
const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);
|
|
35
47
|
const [wasAutoOpened, setWasAutoOpened] = createSignal(false);
|
|
36
48
|
|
|
37
49
|
const isControlled = () => local.open !== undefined;
|
|
38
50
|
const isOpen = () => (isControlled() ? local.open! : internalOpen());
|
|
51
|
+
const disabled = () => !!local.disabled;
|
|
39
52
|
|
|
53
|
+
// The single open-mutate path, shared by the trigger and the imperative
|
|
54
|
+
// controller. In controlled mode we don't touch internal state; the consumer
|
|
55
|
+
// reflects via the `open` prop and observes via onOpenChange.
|
|
40
56
|
const handleOpenChange = (newOpen: boolean) => {
|
|
57
|
+
if (isOpen() === newOpen) return;
|
|
41
58
|
if (!isControlled()) {
|
|
42
59
|
setInternalOpen(newOpen);
|
|
43
60
|
}
|
|
@@ -56,8 +73,13 @@ function Reasoning(props: ReasoningProps) {
|
|
|
56
73
|
}
|
|
57
74
|
});
|
|
58
75
|
|
|
76
|
+
// Hand the controller up once. setOpen routes through handleOpenChange so it
|
|
77
|
+
// notifies onOpenChange and respects controlled/uncontrolled mode. Not gated
|
|
78
|
+
// by disabled — the facade's wireDisclosure applies disabled-gating itself.
|
|
79
|
+
local.controllerRef?.({ open: isOpen, setOpen: handleOpenChange });
|
|
80
|
+
|
|
59
81
|
return (
|
|
60
|
-
<ReasoningContext.Provider value={{ isOpen, onOpenChange: handleOpenChange }}>
|
|
82
|
+
<ReasoningContext.Provider value={{ isOpen, onOpenChange: handleOpenChange, disabled }}>
|
|
61
83
|
<div class={local.class}>{local.children}</div>
|
|
62
84
|
</ReasoningContext.Provider>
|
|
63
85
|
);
|
|
@@ -71,12 +93,13 @@ export interface ReasoningTriggerProps extends JSX.ButtonHTMLAttributes<HTMLButt
|
|
|
71
93
|
|
|
72
94
|
function ReasoningTrigger(props: ReasoningTriggerProps) {
|
|
73
95
|
const [local, rest] = splitProps(props, ['children', 'class']);
|
|
74
|
-
const { isOpen, onOpenChange } = useReasoningContext();
|
|
96
|
+
const { isOpen, onOpenChange, disabled } = useReasoningContext();
|
|
75
97
|
|
|
76
98
|
return (
|
|
77
99
|
<button
|
|
78
100
|
class={cn('flex cursor-pointer items-center gap-2 text-meta', local.class)}
|
|
79
|
-
|
|
101
|
+
disabled={disabled() || undefined}
|
|
102
|
+
onClick={() => { if (!disabled()) onOpenChange(!isOpen()); }}
|
|
80
103
|
{...rest}
|
|
81
104
|
>
|
|
82
105
|
<span class="text-primary">{local.children}</span>
|
|
@@ -41,6 +41,18 @@ export { normalizeCandidates, buildSelection, isAnyStreaming } from './response-
|
|
|
41
41
|
|
|
42
42
|
export type CompareLayout = 'auto' | 'columns' | 'tabs';
|
|
43
43
|
|
|
44
|
+
/** Imperative handle exposed via `controllerRef` — surfaces the compare's latent
|
|
45
|
+
* capabilities (commit a pick by candidate id, focus the roving tab stop) so the
|
|
46
|
+
* `<kai-compare>` facade can forward them as instance methods (Pattern C). */
|
|
47
|
+
export interface ResponseCompareController {
|
|
48
|
+
/** Commit a pick by candidate id — same path as the "Pick this" button: emits
|
|
49
|
+
* onSelect + optimistically collapses (single-shot; inert while streaming or
|
|
50
|
+
* already resolved). No-op for an unknown id. */
|
|
51
|
+
select(candidateId: string): void;
|
|
52
|
+
/** Focus the current roving tab stop (the focused candidate's "Pick this" radio). */
|
|
53
|
+
focus(options?: FocusOptions): void;
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
57
|
// Resolution controller — a sibling of `useCardResolution` for `CompareSelection`,
|
|
46
58
|
// which has no `kind` field (so it can't satisfy `R extends CardResolution`). Same
|
|
@@ -88,6 +100,9 @@ export interface ResponseCompareProps {
|
|
|
88
100
|
* component is ≥640px wide, tabs when narrower (e.g. on a phone). */
|
|
89
101
|
layout?: CompareLayout;
|
|
90
102
|
class?: string;
|
|
103
|
+
/** Receive the imperative controller once mounted. The `<kai-compare>` facade
|
|
104
|
+
* forwards these as element methods (select/focus). */
|
|
105
|
+
controllerRef?: (controller: ResponseCompareController) => void;
|
|
91
106
|
/** Fired when the user commits a pick. */
|
|
92
107
|
onSelect?: (sel: CompareSelection) => void;
|
|
93
108
|
/** Fired once both candidates have settled (stopped streaming) and a valid
|
|
@@ -119,6 +134,7 @@ export function ResponseCompare(props: ResponseCompareProps): JSX.Element {
|
|
|
119
134
|
'selection',
|
|
120
135
|
'layout',
|
|
121
136
|
'class',
|
|
137
|
+
'controllerRef',
|
|
122
138
|
'onSelect',
|
|
123
139
|
'onReady',
|
|
124
140
|
'onError',
|
|
@@ -178,11 +194,24 @@ export function ResponseCompare(props: ResponseCompareProps): JSX.Element {
|
|
|
178
194
|
res.setLocal(sel);
|
|
179
195
|
};
|
|
180
196
|
|
|
181
|
-
const focusColumn = (index: number): void => {
|
|
197
|
+
const focusColumn = (index: number, options?: FocusOptions): void => {
|
|
182
198
|
const cols = groupRef?.querySelectorAll<HTMLElement>('[role="radio"]');
|
|
183
|
-
cols?.[index]?.focus();
|
|
199
|
+
cols?.[index]?.focus(options);
|
|
184
200
|
};
|
|
185
201
|
|
|
202
|
+
// Imperative controller (Pattern C): hand the facade a handle over the latent
|
|
203
|
+
// pick + roving-focus capabilities. `pick`/`focusColumn` are already wired
|
|
204
|
+
// internally — this just surfaces them. `select` looks the candidate up by id
|
|
205
|
+
// (the public surface is id-based; `pick` is candidate-based internally) and
|
|
206
|
+
// is a no-op for an unknown id, while streaming, or once resolved (via pick()).
|
|
207
|
+
local.controllerRef?.({
|
|
208
|
+
select: (candidateId: string) => {
|
|
209
|
+
const cand = pair()?.find((c) => c.id === candidateId);
|
|
210
|
+
if (cand) pick(cand);
|
|
211
|
+
},
|
|
212
|
+
focus: (options?: FocusOptions) => focusColumn(focusIndex(), options),
|
|
213
|
+
});
|
|
214
|
+
|
|
186
215
|
// Two-item roving tabindex (A↔B). Arrows toggle the focused column; Enter/Space
|
|
187
216
|
// picks it. No-op once resolved.
|
|
188
217
|
const onGroupKeyDown = (e: KeyboardEvent): void => {
|