@kitn.ai/ui 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -6
- package/bin/mcp.js +16 -3
- package/bin/route.js +23 -0
- package/bin/route.test.js +58 -0
- package/dist/{elements/chunks/Icon--Y8vsFal.js → Icon-DObhsRtm.js} +1 -1
- package/dist/action-icons-CcgNN2oT.js +1 -0
- package/dist/arrow-down-CjRU3JgV.js +1 -0
- package/dist/arrow-left-Bnpb1UEW.js +1 -0
- package/dist/artifact-Bwt7OgcR.js +1 -0
- package/dist/assets/dev-5XzXYR21.js +91 -0
- package/dist/attachments-Qr4kg-2b.js +1 -0
- package/dist/{elements/chunks/audio-visualizer-D7lB6mvR.js → audio-visualizer-DToYdfSc.js} +1 -1
- package/dist/badge-CdEu_jn2.js +1 -0
- package/dist/bash-InADTalH.js +1 -6
- package/dist/bell-DQAAOpDz.js +1 -0
- package/dist/{elements/chunks/button-flUbeufF.js → button-DkuOnGLy.js} +1 -1
- package/dist/card-renderer-Jx70m6_l.js +1 -0
- package/dist/card-routing-0YidER_3.js +1 -0
- package/dist/check-njkVdZu2.js +1 -0
- package/dist/checkbox-D7iRB3nL.js +1 -0
- package/dist/checkbox-group-B2_rpoPM.js +1 -0
- package/dist/chevron-down-B72s6PXR.js +1 -0
- package/dist/chevron-right-BatuHzUm.js +1 -0
- package/dist/choice-card-m0BYqqOK.js +1 -0
- package/dist/circle-check-B62pODB3.js +1 -0
- package/dist/circle-x-BPG0iPst.js +1 -0
- package/dist/circle-zx0ByzY9.js +1 -0
- package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
- package/dist/code-block-ClMLKb7C.js +1 -0
- package/dist/{elements/chunks/collapsible-BzbXpPzr.js → collapsible-B4DlWicL.js} +1 -1
- package/dist/components/attachment-types.d.ts +7 -0
- package/dist/components/audio-visualizer/fit-scale.d.ts +1 -1
- package/dist/components/card-renderer.d.ts +13 -0
- package/dist/components/card.d.ts +4 -0
- package/dist/components/chat-thread.d.ts +135 -2
- package/dist/components/conversation-item.d.ts +35 -7
- package/dist/components/conversation-list.d.ts +17 -5
- package/dist/components/conversation-panel.d.ts +29 -0
- package/dist/components/form-widgets.d.ts +67 -2
- package/dist/components/form.d.ts +48 -1
- package/dist/components/home-panel.d.ts +34 -0
- package/dist/components/message.d.ts +25 -3
- package/dist/components/reasoning.d.ts +6 -0
- package/dist/components/scroll-button.d.ts +11 -0
- package/dist/components/thread.d.ts +5 -0
- package/dist/components/tool-types.d.ts +9 -0
- package/dist/components/widget-tab-bar.d.ts +16 -0
- package/dist/composer-sQ0HcvyG.js +106 -0
- package/dist/confirm-card-rQWDQsnC.js +1 -0
- package/dist/construct-cli.es.js +1337 -0
- package/dist/{elements/chunks/context-VLIL3sJ-.js → context-0kg4ZX1J.js} +1 -1
- package/dist/controllable-CnGpMF6r.js +1 -0
- package/dist/conversation-item-BxwQLaFJ.js +1 -0
- package/dist/conversation-list-BCfGN1JU.js +1 -0
- package/dist/copy-n9u9rqpi.js +1 -0
- package/dist/core-AYMC6_lb.js +12 -5874
- package/dist/{create-tween-BFT6c-Y8.js → create-tween-C-q-JCM-.js} +1 -1
- package/dist/{create-tween-CJNlzoaO.js → create-tween-CRrJXxBd.js} +1 -1
- package/dist/{create-tween-CQED4T0g.js → create-tween-DZ3RRDHD.js} +1 -1
- package/dist/{elements/chunks/create-tween-c4Z8TDYz.js → create-tween-uPkArjIG.js} +2 -2
- package/dist/css-M7EaDHN_.js +1 -6
- package/dist/custom-elements.json +1433 -202
- package/dist/default-input-CaMrJkgH.js +1 -0
- package/dist/define-By-uL588.js +1 -0
- package/dist/define.d.ts +4 -0
- package/dist/define.js +510 -0
- package/dist/define.server.d.ts +5 -0
- package/dist/define.server.js +506 -0
- package/dist/{elements/chunks/disclosure-CcNbyLy0.js → disclosure-WUImBbDv.js} +1 -1
- package/dist/{elements/chunks/dropdown-BgWTthUx.js → dropdown-wZVeJOMd.js} +1 -1
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/autoloader.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- 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-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkbox-group.d.ts +1 -0
- package/dist/elements/checkbox-group.js +1 -0
- package/dist/elements/checkbox.d.ts +1 -0
- package/dist/elements/checkbox.js +1 -0
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-item.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/define-entry.d.ts +12 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.js +102 -8
- package/dist/elements/dropdown.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-data-types.d.ts +37 -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.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- 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.js +1 -1
- 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.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-grid.d.ts +1 -0
- package/dist/elements/pane-grid.js +1 -0
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/radio-group.d.ts +1 -0
- package/dist/elements/radio-group.js +1 -0
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +2 -2
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/select.d.ts +1 -0
- package/dist/elements/select.js +1 -0
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slider.d.ts +1 -0
- package/dist/elements/slider.js +1 -0
- package/dist/elements/slots.d.ts +1 -1
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- 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/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +380 -26
- package/dist/{elements/chunks/ellipsis-7gU33mL2.js → ellipsis-CbOpJnU3.js} +1 -1
- package/dist/embed-CrfjkbUG.js +1 -0
- package/dist/engine-javascript-vq0WuIJl.js +14 -2516
- package/dist/{elements/chunks/external-link-CSOEsNlx.js → external-link-ByXAwBBQ.js} +1 -1
- package/dist/file-text-DMib2qwt.js +1 -0
- package/dist/{elements/chunks/file-tree-zvB2i7P9.js → file-tree-CJqM2aP7.js} +1 -1
- package/dist/folder-_BAb929C.js +1 -0
- package/dist/form-DSZgZQZk.js +1 -0
- package/dist/github-dark-dimmed-DUshB20C.js +1 -4
- package/dist/github-light-JYsPkUQd.js +1 -4
- package/dist/hover-card-BQbul9eq.js +1 -0
- package/dist/html-CPZ3oZQ7.js +1 -10
- package/dist/icon-CdcfRhYI.js +1 -0
- package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +7474 -5857
- package/dist/index.server.js +5965 -4762
- package/dist/info-DsFjGuI_.js +1 -0
- package/dist/input-DKbmkMos.js +1 -0
- package/dist/javascript-C25yR2R2.js +1 -6
- package/dist/json-DxJze_jm.js +1 -6
- package/dist/kai.es.js +1 -1
- package/dist/kbd-PvdGhX4s.js +1 -0
- package/dist/{elements/chunks/link-preview-OQ9IEq0f.js → link-preview-DbM1866N.js} +1 -1
- package/dist/link-zF8PzWfF.js +1 -0
- package/dist/loader-BM4CEz4G.js +1 -0
- package/dist/{elements/chunks/markdown-DgBFaGAz.js → markdown-LMvmAyBk.js} +7 -7
- package/dist/mcp.es.js +1361 -81
- package/dist/message-BSsgvHvf.js +1 -0
- package/dist/message-BYZECyek.js +1 -0
- package/dist/message-circle-DruwObfX.js +1 -0
- package/dist/message-square-BG6q6mlm.js +1 -0
- package/dist/{elements/chunks/minimize-2-DPIUKTrT.js → minimize-2-BQRoBV1V.js} +1 -1
- package/dist/{elements/chunks/model-switcher-DsT8tFB0.js → model-switcher-CMpCOYcB.js} +1 -1
- package/dist/{elements/chunks/overlay-DuiwdXjm.js → overlay-Cb-MkyDa.js} +1 -1
- package/dist/panel-right-i8warFxT.js +1 -0
- package/dist/paperclip-ByY3UVO6.js +1 -0
- package/dist/play-BvKKq3fX.js +1 -0
- package/dist/primitives/card-data-types.d.ts +49 -0
- package/dist/primitives/card-routing.d.ts +1 -33
- package/dist/primitives/card-validate.d.ts +2 -2
- package/dist/primitives/chat-config.d.ts +29 -2
- package/dist/primitives/conversation-store.d.ts +54 -0
- package/dist/primitives/field-mask.d.ts +96 -0
- package/dist/primitives/field-semantics.d.ts +36 -0
- package/dist/primitives/input-mask.d.ts +46 -0
- package/dist/primitives/url-scheme-policy.d.ts +34 -0
- package/dist/primitives/use-resize-observer.d.ts +2 -2
- package/dist/progress-bar-jVAHvMUf.js +1 -0
- package/dist/prompt-suggestion-CD-qtNgC.js +1 -0
- package/dist/radio-BsoBMhkF.js +1 -0
- package/dist/react/index.d.ts +586 -19
- package/dist/react.js +223 -187
- package/dist/reasoning-4kBPtlTx.js +1 -0
- package/dist/register-impl-B1hLkrI8.js +424 -0
- package/dist/{elements/chunks/resizable-D181b4fM.js → resizable-uPmY9VY-.js} +1 -1
- package/dist/rotate-ccw-B2xn93py.js +1 -0
- package/dist/rotate-cw-CMVLoCyH.js +1 -0
- package/dist/schemas/form.schema.json +21 -1
- package/dist/schemas/tool-defs.d.ts +11 -1
- package/dist/schemas.js +81 -81
- package/dist/scroll-area-BqLUo8ZZ.js +1 -0
- package/dist/scroll-button-DslXiJyL.js +1 -0
- package/dist/select-2cLRtPGT.js +1 -0
- package/dist/{elements/chunks/separator-CeJHQqLW.js → separator-BWQtNJcM.js} +1 -1
- package/dist/{elements/chunks/settings-CqaHgJF9.js → settings-BBR63jLg.js} +1 -1
- package/dist/{elements/chunks/settings-group-CjqjLCOn.js → settings-group-B2ciy-Iw.js} +1 -1
- package/dist/{elements/chunks/skeleton-D8akBnKS.js → skeleton-CkHI-HgC.js} +1 -1
- package/dist/slider-DDR9U-i0.js +1 -0
- package/dist/{solid-BtdqNZI7.js → solid-BzCFdL25.js} +9771 -7594
- package/dist/{solid-CnavwBye.js → solid-DruVOXnf.js} +7893 -6183
- package/dist/solid.d.ts +16 -2
- package/dist/solid.js +256 -243
- package/dist/solid.server.js +256 -243
- package/dist/source-GEnFF528.js +1 -0
- package/dist/star-CilHq8GP.js +1 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/mock.d.ts +32 -2
- package/dist/state.js +133 -108
- package/dist/store-Ciyk6eZG.js +1 -0
- package/dist/svelte--5p79yCD.js +1 -15
- package/dist/switch-KkeQIbuH.js +1 -0
- package/dist/tasks-card-BrN4bggJ.js +1 -0
- package/dist/text-shimmer-Dcj1MHuq.js +1 -0
- package/dist/textarea-Dpvbngbg.js +1 -0
- package/dist/theme.tokens.css +110 -13
- package/dist/{elements/chunks/thumbs-up-DX2Ijk3H.js → thumbs-up-CsT9Wzb9.js} +1 -1
- package/dist/toast-store-tgK6MuMv.js +1 -0
- package/dist/{elements/chunks/tool-DKQ2D1DP.js → tool-DzUjIBT8.js} +1 -1
- package/dist/{elements/chunks/tooltip-B0G99y8w.js → tooltip-Bkkg_4t0.js} +1 -1
- package/dist/trash-2-jBKxjGGQ.js +1 -0
- package/dist/{elements/chunks/triangle-alert-F9cyUEyy.js → triangle-alert-DlQ68ppf.js} +1 -1
- package/dist/tsx-B8rCNbgL.js +1 -6
- package/dist/types.d.ts +48 -3
- package/dist/typescript-RycA9KXf.js +1 -6
- package/dist/ui/checkbox-group.d.ts +83 -0
- package/dist/ui/checkbox.d.ts +40 -0
- package/dist/ui/dock.d.ts +51 -0
- package/dist/ui/icon.d.ts +3 -0
- package/dist/ui/input.d.ts +48 -2
- package/dist/ui/radio.d.ts +85 -0
- package/dist/ui/select.d.ts +71 -0
- package/dist/ui/slider.d.ts +65 -0
- package/dist/ui/switch.d.ts +9 -2
- package/dist/upload-TcAHXv9W.js +1 -0
- package/dist/url-scheme-policy-DR5DgvkM.js +1 -0
- package/dist/use-card-resolution-DaZqAdWf.js +1 -0
- package/dist/{elements/chunks/variant-aurora-qt6Zct8M.js → variant-aurora-BhLuYxJR.js} +1 -1
- package/dist/{variant-aurora-B8II3-I1.js → variant-aurora-CNmfHhcx.js} +2 -2
- package/dist/{variant-aurora-CMNWOSJR.js → variant-aurora-D6GsdTPA.js} +2 -2
- package/dist/{variant-aurora-CcuDMWwQ.js → variant-aurora-DbW_OWL2.js} +1 -1
- package/dist/{elements/chunks/variant-custom-tJp22_BQ.js → variant-custom-BRvaNHSi.js} +1 -1
- package/dist/{variant-custom-L16JLLZy.js → variant-custom-BvYHR62b.js} +1 -1
- package/dist/{variant-custom-BdvYC7af.js → variant-custom-DMTrEO5w.js} +2 -2
- package/dist/{variant-custom-C714mqKl.js → variant-custom-DV2x254s.js} +2 -2
- package/dist/{variant-wave-ecwykeyL.js → variant-wave-BNMGrDfj.js} +2 -2
- package/dist/{variant-wave-2S4La2Ka.js → variant-wave-BpDd8t3Z.js} +1 -1
- package/dist/{variant-wave-D7ruiA1W.js → variant-wave-CyBueXj9.js} +2 -2
- package/dist/{elements/chunks/variant-wave-CaQ5ti39.js → variant-wave-sp-qwsdI.js} +1 -1
- package/dist/vue-BmIZj4XD.js +1 -33
- package/dist/x-Z5UntkIJ.js +1 -0
- package/frameworks/react/index.tsx +210 -22
- package/llms-full.txt +1184 -22
- package/llms.txt +6 -3
- package/package.json +17 -4
- package/src/agent-tooling/README.md +192 -0
- package/src/agent-tooling/catalog/invariants.ts +3 -3
- package/src/agent-tooling/catalog/surfaces.ts +72 -0
- package/src/agent-tooling/construct/cli-entry.ts +8 -0
- package/src/agent-tooling/construct/cli.ts +147 -0
- package/src/agent-tooling/construct/codegen.ts +1635 -0
- package/src/agent-tooling/construct/construct.v1.schema.json +319 -0
- package/src/agent-tooling/construct/dev.ts +148 -0
- package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
- package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +32 -0
- package/src/agent-tooling/construct/schema.ts +452 -0
- package/src/agent-tooling/mcp/server.ts +2 -1
- package/src/agent-tooling/mcp/tools/construct.ts +130 -0
- package/src/agent-tooling/mcp/tools/debug.ts +126 -4
- package/src/agent-tooling/mcp/tools/reference.ts +104 -3
- package/src/agent-tooling/recipes/composed-thread.ts +714 -0
- package/src/agent-tooling/recipes/index.ts +22 -0
- package/src/agent-tooling/recipes/types.ts +32 -0
- package/src/agent-tooling/registry.ts +5 -0
- package/src/components/attachment-types.ts +7 -0
- package/src/components/audio-visualizer/fit-scale.ts +1 -1
- package/src/components/card-renderer.tsx +39 -8
- package/src/components/card.tsx +10 -2
- package/src/components/chat-scope-picker.tsx +2 -2
- package/src/components/chat-thread.tsx +602 -43
- package/src/components/choice-card.tsx +102 -37
- package/src/components/coachmark.tsx +1 -1
- package/src/components/code-block.tsx +2 -2
- package/src/components/composer.tsx +89 -15
- package/src/components/confirm-card.tsx +20 -4
- package/src/components/conversation-item.tsx +56 -9
- package/src/components/conversation-list.tsx +25 -10
- package/src/components/conversation-panel.tsx +118 -0
- package/src/components/empty.tsx +14 -3
- package/src/components/file-tree.tsx +5 -1
- package/src/components/form-widgets.tsx +257 -195
- package/src/components/form.tsx +297 -9
- package/src/components/home-panel.tsx +159 -0
- package/src/components/loader.tsx +12 -5
- package/src/components/message-skills.tsx +1 -1
- package/src/components/message.tsx +71 -11
- package/src/components/prompt-input.tsx +7 -1
- package/src/components/prompt-suggestion.tsx +4 -1
- package/src/components/reasoning.tsx +30 -3
- package/src/components/response-compare.tsx +2 -2
- package/src/components/scroll-button.tsx +56 -4
- package/src/components/source.tsx +6 -1
- package/src/components/tasks-card.tsx +58 -16
- package/src/components/thread.tsx +10 -1
- package/src/components/toast.tsx +1 -1
- package/src/components/tool-types.ts +9 -0
- package/src/components/widget-tab-bar.tsx +73 -0
- package/src/elements/attachments.tsx +3 -1
- package/src/elements/chat-workspace.tsx +1 -1
- package/src/elements/chat.tsx +115 -5
- package/src/elements/checkbox-group.tsx +164 -0
- package/src/elements/checkbox.tsx +134 -0
- package/src/elements/compiled.css +1 -1
- package/src/elements/composer.tsx +12 -1
- package/src/elements/conversation-item.tsx +62 -22
- package/src/elements/conversation-list.tsx +2 -2
- package/src/elements/define-entry.ts +12 -0
- package/src/elements/dock.tsx +11 -0
- package/src/elements/element-data-types.ts +40 -0
- package/src/elements/element-manifest.json +24 -0
- package/src/elements/element-meta.json +767 -34
- package/src/elements/element-nonscalar.json +17 -0
- package/src/elements/element-types.d.ts +380 -26
- package/src/elements/feedback-bar.tsx +5 -0
- package/src/elements/icon-names.json +29 -0
- package/src/elements/input.tsx +312 -23
- package/src/elements/message.tsx +2 -0
- package/src/elements/pane-grid.tsx +111 -0
- package/src/elements/prompt-input.tsx +8 -2
- package/src/elements/radio-group.tsx +129 -0
- package/src/elements/register-impl.ts +6 -0
- package/src/elements/scroll-button.tsx +54 -6
- package/src/elements/select.tsx +165 -0
- package/src/elements/slider.tsx +171 -0
- package/src/elements/slots.ts +1 -1
- package/src/elements/styles.css +89 -3
- package/src/elements/thread.tsx +2 -0
- package/src/elements/toast.tsx +14 -1
- package/src/index.ts +5 -2
- package/src/primitives/card-data-types.ts +49 -0
- package/src/primitives/card-host.tsx +4 -2
- package/src/primitives/card-routing.ts +7 -56
- package/src/primitives/card-schemas/form.schema.json +21 -1
- package/src/primitives/card-validate.ts +10 -2
- package/src/primitives/chat-config.tsx +33 -6
- package/src/primitives/conversation-store.ts +248 -0
- package/src/primitives/field-mask.ts +256 -0
- package/src/primitives/field-semantics.ts +115 -0
- package/src/primitives/input-mask.ts +853 -0
- package/src/primitives/toast-store.ts +66 -4
- package/src/primitives/url-scheme-policy.ts +70 -0
- package/src/primitives/use-resize-observer.ts +2 -2
- package/src/schemas/tool-defs.ts +12 -2
- package/src/solid.ts +20 -2
- package/src/state/index.ts +1 -1
- package/src/state/mock.ts +92 -6
- package/src/types.ts +45 -3
- package/src/ui/agent-card.tsx +2 -2
- package/src/ui/avatar.tsx +1 -1
- package/src/ui/checkbox-group.tsx +153 -0
- package/src/ui/checkbox.tsx +63 -0
- package/src/ui/dock.tsx +192 -7
- package/src/ui/icon.tsx +53 -0
- package/src/ui/input.tsx +249 -9
- package/src/ui/kbd.tsx +1 -1
- package/src/ui/nav.tsx +1 -1
- package/src/ui/pane-group.tsx +2 -2
- package/src/ui/radio.tsx +150 -0
- package/src/ui/select.tsx +168 -0
- package/src/ui/slider.tsx +178 -0
- package/src/ui/switch.tsx +49 -18
- package/src/utils/cn.ts +11 -5
- package/theme.css +112 -13
- package/dist/elements/chunks/action-icons-D9u3OzYW.js +0 -1
- package/dist/elements/chunks/arrow-left-CVh1TeN7.js +0 -1
- package/dist/elements/chunks/artifact-BkYcfIlZ.js +0 -1
- package/dist/elements/chunks/attachments-DwfNlubq.js +0 -1
- package/dist/elements/chunks/badge-Q0-Cmwzz.js +0 -1
- package/dist/elements/chunks/bash-InADTalH.js +0 -1
- package/dist/elements/chunks/card-renderer-3Dyl-FpN.js +0 -1
- package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
- package/dist/elements/chunks/check-B5kBPKeo.js +0 -1
- package/dist/elements/chunks/chevron-down-DZ1GS39h.js +0 -1
- package/dist/elements/chunks/chevron-right-CvP7OUd2.js +0 -1
- package/dist/elements/chunks/choice-card-D4QUIOWZ.js +0 -1
- package/dist/elements/chunks/circle-CDf9g6MQ.js +0 -1
- package/dist/elements/chunks/circle-check-CA47Nqbz.js +0 -1
- package/dist/elements/chunks/circle-x-CJ_HVbC8.js +0 -1
- package/dist/elements/chunks/code-block-BdLiTitd.js +0 -1
- package/dist/elements/chunks/composer-Byve-n8K.js +0 -69
- package/dist/elements/chunks/confirm-card-B0flE3MN.js +0 -1
- package/dist/elements/chunks/controllable-DTwTIlwp.js +0 -1
- package/dist/elements/chunks/conversation-item-BDztezOG.js +0 -1
- package/dist/elements/chunks/copy-Nf-kqxXH.js +0 -1
- package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
- package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
- package/dist/elements/chunks/default-input-C2iLFq0b.js +0 -1
- package/dist/elements/chunks/define-wjHR1MkU.js +0 -1
- package/dist/elements/chunks/download-C4V7tBdb.js +0 -1
- package/dist/elements/chunks/embed-D3KewsIJ.js +0 -1
- package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
- package/dist/elements/chunks/file-text-D1G8khle.js +0 -1
- package/dist/elements/chunks/folder-WW2LGLid.js +0 -1
- package/dist/elements/chunks/form-CQirdC4W.js +0 -1
- package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
- package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
- package/dist/elements/chunks/hover-card-CJJkfHTK.js +0 -1
- package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
- package/dist/elements/chunks/icon-Cfgmf4fa.js +0 -1
- package/dist/elements/chunks/info-DX3YMOt6.js +0 -1
- package/dist/elements/chunks/input-BtW9U6Sq.js +0 -1
- package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
- package/dist/elements/chunks/json-DxJze_jm.js +0 -1
- package/dist/elements/chunks/kbd-LGXwuT_V.js +0 -1
- package/dist/elements/chunks/link-2My69BqT.js +0 -1
- package/dist/elements/chunks/loader-iOxc_Tu-.js +0 -1
- package/dist/elements/chunks/message-VMZohrXx.js +0 -1
- package/dist/elements/chunks/message-circle-CpXMM-_n.js +0 -1
- package/dist/elements/chunks/message-square-DxcWtETQ.js +0 -1
- package/dist/elements/chunks/message-tZmV2A3p.js +0 -1
- package/dist/elements/chunks/paperclip-CRZVp4z_.js +0 -1
- package/dist/elements/chunks/progress-bar-DNy66e_E.js +0 -1
- package/dist/elements/chunks/prompt-suggestion-Bn64Nl7L.js +0 -1
- package/dist/elements/chunks/reasoning-Emn9N4-T.js +0 -1
- package/dist/elements/chunks/rotate-cw-CPWe37D9.js +0 -1
- package/dist/elements/chunks/scroll-area-ClZkZxIk.js +0 -1
- package/dist/elements/chunks/scroll-button-DPE-t3x_.js +0 -1
- package/dist/elements/chunks/share-CyfnNEqp.js +0 -1
- package/dist/elements/chunks/source-UWCvmVak.js +0 -1
- package/dist/elements/chunks/star-DoHFAikQ.js +0 -1
- package/dist/elements/chunks/store-Cjq0qwYN.js +0 -1
- package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
- package/dist/elements/chunks/tasks-card-CmZ5xh6v.js +0 -1
- package/dist/elements/chunks/text-shimmer-DYn09zrz.js +0 -1
- package/dist/elements/chunks/textarea-skiGHURM.js +0 -1
- package/dist/elements/chunks/toast-store-CFnQpThX.js +0 -1
- package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
- package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
- package/dist/elements/chunks/use-card-resolution-DoA2f_EU.js +0 -1
- package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
- package/dist/elements/chunks/x-B6UnOiLw.js +0 -1
- package/dist/llms/llms-full.txt +0 -2949
- package/dist/llms/llms.txt +0 -156
- package/dist/register-impl-DpE7icIb.js +0 -293
- package/dist/ui/stat.d.ts +0 -16
- package/src/ui/stat.tsx +0 -41
- /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
- /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
- /package/dist/{elements/chunks/slots-DaNjX0rc.js → slots-DaNjX0rc.js} +0 -0
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from 'solid-js';
|
|
15
15
|
import { cn } from '../utils/cn';
|
|
16
16
|
import { Button } from '../ui/button';
|
|
17
|
+
import { Radio } from '../ui/radio';
|
|
17
18
|
import { HoverCard } from '../ui/hover-card';
|
|
18
19
|
import { Card } from './card';
|
|
19
20
|
import { DismissedStub } from './dismissed-stub';
|
|
@@ -21,7 +22,7 @@ import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primi
|
|
|
21
22
|
import { useCardResolution } from './use-card-resolution';
|
|
22
23
|
import { emitCardEvent } from '../primitives/card-routing';
|
|
23
24
|
import { useCardHost } from '../primitives/card-host';
|
|
24
|
-
import { Check
|
|
25
|
+
import { Check } from 'lucide-solid';
|
|
25
26
|
|
|
26
27
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
27
28
|
// Types (choice.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
|
|
@@ -430,6 +431,10 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
430
431
|
const groupLabel = (): string => local.heading ?? local.data?.prompt ?? 'Choose an option';
|
|
431
432
|
const promptId = `kai-choice-prompt-${uid}`;
|
|
432
433
|
const otherInputId = `kai-choice-other-${uid}`;
|
|
434
|
+
// The shared `name` is what makes the rows ONE native control: mutual exclusion,
|
|
435
|
+
// form participation and "2 of 4" from a screen reader all come from it. Scoped to
|
|
436
|
+
// this card's uid so two ChoiceCards on a page never de-select each other.
|
|
437
|
+
const radioName = `kai-choice-${uid}`;
|
|
433
438
|
|
|
434
439
|
return (
|
|
435
440
|
<Show when={valid()} fallback={<Card heading={local.heading} errorMessage={errorMessage()} />}>
|
|
@@ -443,14 +448,18 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
443
448
|
when={!res.isDeferred()}
|
|
444
449
|
fallback={<DismissedStub type={CHOICE_CARD_TYPE} title={local.heading} onReopen={onReopen} />}
|
|
445
450
|
>
|
|
446
|
-
|
|
451
|
+
{/* `prompt` is the card's supporting sentence — the same role `form`'s
|
|
452
|
+
`description` plays — so it goes through the shell's `description`
|
|
453
|
+
slot rather than being re-implemented in the body. That is what gives
|
|
454
|
+
it the muted colour: at full `text-foreground` it was the same colour
|
|
455
|
+
as the heading above it, which flattened the hierarchy. `descriptionId`
|
|
456
|
+
keeps the radiogroup's `aria-describedby` pointing at it. */}
|
|
457
|
+
<Card
|
|
458
|
+
heading={local.heading}
|
|
459
|
+
description={local.data?.prompt}
|
|
460
|
+
descriptionId={promptId}
|
|
461
|
+
>
|
|
447
462
|
<div class={cn('flex flex-col gap-3', local.class)}>
|
|
448
|
-
<Show when={local.data?.prompt}>
|
|
449
|
-
<p id={promptId} class="text-sm text-foreground">
|
|
450
|
-
{local.data?.prompt}
|
|
451
|
-
</p>
|
|
452
|
-
</Show>
|
|
453
|
-
|
|
454
463
|
<Show
|
|
455
464
|
when={!res.isResolved()}
|
|
456
465
|
fallback={<ResolvedChoice choice={resolvedChoice()!} optimistic={res.isOptimistic()} />}
|
|
@@ -462,7 +471,7 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
462
471
|
aria-describedby={local.data?.prompt ? promptId : undefined}
|
|
463
472
|
aria-disabled={isDisabled() ? 'true' : undefined}
|
|
464
473
|
class={cn(
|
|
465
|
-
'divide-y divide-border overflow-hidden rounded-lg border border-
|
|
474
|
+
'divide-y divide-border overflow-hidden rounded-lg border border-border',
|
|
466
475
|
isDisabled() && 'cursor-not-allowed opacity-60',
|
|
467
476
|
)}
|
|
468
477
|
onKeyDown={onGroupKeyDown}
|
|
@@ -477,8 +486,10 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
477
486
|
return (
|
|
478
487
|
<ListRow
|
|
479
488
|
opt={opt}
|
|
489
|
+
name={radioName}
|
|
480
490
|
checked={checked()}
|
|
481
491
|
tabStop={tabStop()}
|
|
492
|
+
groupDisabled={isDisabled()}
|
|
482
493
|
descId={descId}
|
|
483
494
|
hasDesc={hasDesc()}
|
|
484
495
|
onPick={() => {
|
|
@@ -518,14 +529,23 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
518
529
|
|
|
519
530
|
<div class="flex w-full flex-wrap items-center justify-between gap-2">
|
|
520
531
|
<Show when={local.data?.dismissible === true}>
|
|
532
|
+
{/* The contract `dismiss` verb — a footer ACTION (it emits
|
|
533
|
+
`{kind:'dismiss'}` and collapses the card to a re-openable
|
|
534
|
+
stub), not the chrome close in `Card`'s own `dismissible`
|
|
535
|
+
prop, which the contract cards deliberately never set.
|
|
536
|
+
Labelled rather than a bare ghost ✕: every other control in
|
|
537
|
+
this row says what it does, and an unlabelled 28px glyph
|
|
538
|
+
300px from the primary button reads as stray chrome. Same
|
|
539
|
+
shape in all four cards — `form` already looked like this.
|
|
540
|
+
`aria-label` is kept (redundant with the text) so the
|
|
541
|
+
element tests that select on it keep working. */}
|
|
521
542
|
<Button
|
|
522
543
|
type="button"
|
|
523
544
|
variant="ghost"
|
|
524
|
-
size="icon-sm"
|
|
525
545
|
aria-label="Dismiss"
|
|
526
546
|
onClick={onDismiss}
|
|
527
547
|
>
|
|
528
|
-
|
|
548
|
+
Dismiss
|
|
529
549
|
</Button>
|
|
530
550
|
</Show>
|
|
531
551
|
<Button type="button" class="ml-auto" disabled={!canSubmit()} onClick={submit}>
|
|
@@ -542,14 +562,19 @@ export function ChoiceCard(props: ChoiceCardProps): JSX.Element {
|
|
|
542
562
|
}
|
|
543
563
|
|
|
544
564
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
545
|
-
// Internal list-row presentation: a
|
|
546
|
-
// selectable-list
|
|
565
|
+
// Internal list-row presentation: a `<label>` row wrapping a REAL
|
|
566
|
+
// `<input type="radio">` (the `Radio` primitive), with the kit's selectable-list
|
|
567
|
+
// styling.
|
|
547
568
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
548
569
|
|
|
549
570
|
interface RowProps {
|
|
550
571
|
opt: ChoiceOption;
|
|
572
|
+
/** The shared form-control name every radio in this card's group carries. */
|
|
573
|
+
name: string;
|
|
551
574
|
checked: boolean;
|
|
552
575
|
tabStop: boolean;
|
|
576
|
+
/** The whole group is frozen (`disabled` prop), not just this row. */
|
|
577
|
+
groupDisabled: boolean;
|
|
553
578
|
descId: string;
|
|
554
579
|
hasDesc: boolean;
|
|
555
580
|
onPick: () => void;
|
|
@@ -558,7 +583,7 @@ interface RowProps {
|
|
|
558
583
|
|
|
559
584
|
function RecommendedPill(): JSX.Element {
|
|
560
585
|
return (
|
|
561
|
-
<span class="inline-flex items-center rounded-full bg-[var(--color-primary)] px-1.5 py-0.5 text-
|
|
586
|
+
<span class="inline-flex items-center rounded-full bg-[var(--color-primary)] px-1.5 py-0.5 text-micro font-medium uppercase leading-none tracking-wide text-[var(--color-primary-foreground,white)]">
|
|
562
587
|
Recommended
|
|
563
588
|
</span>
|
|
564
589
|
);
|
|
@@ -587,25 +612,76 @@ function IconBadge(props: { name: string }): JSX.Element {
|
|
|
587
612
|
);
|
|
588
613
|
}
|
|
589
614
|
|
|
615
|
+
/**
|
|
616
|
+
* One row of the radiogroup.
|
|
617
|
+
*
|
|
618
|
+
* The control is a REAL `<input type="radio">` (the `Radio` primitive over
|
|
619
|
+
* `.kai-radio`), not the `aria-hidden` ring-shaped `<span>` this row used to draw.
|
|
620
|
+
* That is what buys form participation — a `name`, a `value`, and a real entry in a
|
|
621
|
+
* native `FormData` — which a `<div role="radio">` can never have, and it removes the
|
|
622
|
+
* fourth hand-rolled radio from the kit.
|
|
623
|
+
*
|
|
624
|
+
* WHY `role="radio"` IS ON THE INPUT AND NOT ON THE ROW. `choice-card.tsx`'s own
|
|
625
|
+
* keyboard handler and `response-compare.tsx` both drive `[role="radio"]`, an
|
|
626
|
+
* ATTRIBUTE selector that a native radio's implicit role does not match, so the role
|
|
627
|
+
* has to be written somewhere. Putting it on the `<label>` wrapper (which the plan
|
|
628
|
+
* suggested) would nest a radio inside a radio and announce the row twice; putting it
|
|
629
|
+
* on the input states the role the input already has. `aria-checked` / `aria-disabled`
|
|
630
|
+
* are likewise redundant with `checked` / `disabled` and kept in lockstep with them —
|
|
631
|
+
* they are the state the element tests read, and a redundant ARIA attribute that never
|
|
632
|
+
* disagrees with the DOM is harmless where a second radio in the a11y tree is not.
|
|
633
|
+
*
|
|
634
|
+
* The row's `<label>` is what makes a click anywhere on it select the option, which is
|
|
635
|
+
* the browser's own behaviour rather than the `onClick` re-implementation it replaces.
|
|
636
|
+
* The `onClick` is kept as well: it is the path the existing tests fire, and both paths
|
|
637
|
+
* land on the same idempotent `select()`.
|
|
638
|
+
*/
|
|
590
639
|
function ListRow(props: RowProps): JSX.Element {
|
|
640
|
+
// The row is inert when the option itself is disabled OR the whole group is frozen.
|
|
641
|
+
// The group case has to reach the native `disabled` attribute: an enabled input
|
|
642
|
+
// would visibly check itself on click while `select()` refuses the state change, and
|
|
643
|
+
// the DOM would then be lying about the selection.
|
|
644
|
+
const inert = (): boolean => props.opt.disabled === true || props.groupDisabled;
|
|
645
|
+
const pick = (): void => {
|
|
646
|
+
if (inert()) return;
|
|
647
|
+
props.onPick();
|
|
648
|
+
};
|
|
591
649
|
return (
|
|
592
|
-
<
|
|
593
|
-
|
|
594
|
-
aria-checked={props.checked}
|
|
595
|
-
aria-disabled={props.opt.disabled ? 'true' : undefined}
|
|
596
|
-
aria-describedby={props.hasDesc ? props.descId : undefined}
|
|
597
|
-
data-option-id={props.opt.id}
|
|
598
|
-
tabindex={props.opt.disabled ? -1 : props.tabStop ? 0 : -1}
|
|
599
|
-
onClick={() => !props.opt.disabled && props.onPick()}
|
|
600
|
-
onFocus={props.onFocus}
|
|
650
|
+
<label
|
|
651
|
+
onClick={pick}
|
|
601
652
|
class={cn(
|
|
602
|
-
'flex items-center gap-3 px-3 py-2.5 text-sm
|
|
653
|
+
'flex items-center gap-3 px-3 py-2.5 text-sm transition-colors',
|
|
654
|
+
// The focus ring stays on the ROW and stays inset — the same 2px inset ring the
|
|
655
|
+
// focusable `div` painted. Focus now lands on the input inside it, so the
|
|
656
|
+
// selector moves from `focus-visible:` to `has-[:focus-visible]:`; the input's
|
|
657
|
+
// own outline is suppressed below so there is one ring, not two. That is the
|
|
658
|
+
// pattern src/elements/styles.css sanctions at its `:focus-visible` rule.
|
|
659
|
+
'has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-inset has-[:focus-visible]:ring-ring',
|
|
603
660
|
props.opt.disabled
|
|
604
661
|
? 'cursor-not-allowed opacity-60 text-foreground'
|
|
605
662
|
: 'cursor-pointer hover:bg-muted/50',
|
|
606
663
|
props.checked ? 'bg-accent font-medium text-accent-foreground' : 'text-foreground',
|
|
607
664
|
)}
|
|
608
665
|
>
|
|
666
|
+
{/* FIRST in the row, ahead of any media. It used to sit AFTER the thumbnail/icon
|
|
667
|
+
badge, so a list where only some options carried media had a ragged control
|
|
668
|
+
column (measured 98px vs 50px in the `WithOther` story). The control column is
|
|
669
|
+
the one thing every row shares, so it leads. */}
|
|
670
|
+
<Radio
|
|
671
|
+
role="radio"
|
|
672
|
+
name={props.name}
|
|
673
|
+
value={props.opt.id}
|
|
674
|
+
checked={props.checked}
|
|
675
|
+
disabled={inert()}
|
|
676
|
+
aria-checked={props.checked}
|
|
677
|
+
aria-disabled={props.opt.disabled ? 'true' : undefined}
|
|
678
|
+
aria-describedby={props.hasDesc ? props.descId : undefined}
|
|
679
|
+
data-option-id={props.opt.id}
|
|
680
|
+
tabindex={props.opt.disabled ? -1 : props.tabStop ? 0 : -1}
|
|
681
|
+
class="focus-visible:outline-none"
|
|
682
|
+
onChange={pick}
|
|
683
|
+
onFocus={props.onFocus}
|
|
684
|
+
/>
|
|
609
685
|
<Show when={props.opt.media?.image}>
|
|
610
686
|
<HoverCard
|
|
611
687
|
openDelay={150}
|
|
@@ -623,17 +699,6 @@ function ListRow(props: RowProps): JSX.Element {
|
|
|
623
699
|
<Show when={!props.opt.media?.image && props.opt.media?.icon}>
|
|
624
700
|
<IconBadge name={props.opt.media!.icon!} />
|
|
625
701
|
</Show>
|
|
626
|
-
<span
|
|
627
|
-
aria-hidden="true"
|
|
628
|
-
class={cn(
|
|
629
|
-
'flex h-[1.125rem] w-[1.125rem] shrink-0 items-center justify-center rounded-full border-[1.5px] transition-colors',
|
|
630
|
-
props.checked ? 'border-[var(--color-primary)]' : 'border-input bg-background',
|
|
631
|
-
)}
|
|
632
|
-
>
|
|
633
|
-
<Show when={props.checked}>
|
|
634
|
-
<span class="h-2 w-2 rounded-full bg-[var(--color-primary)]" />
|
|
635
|
-
</Show>
|
|
636
|
-
</span>
|
|
637
702
|
<span class="flex min-w-0 flex-col gap-0.5">
|
|
638
703
|
<span class="flex items-center gap-2">
|
|
639
704
|
<span class="truncate">{props.opt.label}</span>
|
|
@@ -652,7 +717,7 @@ function ListRow(props: RowProps): JSX.Element {
|
|
|
652
717
|
{props.opt.meta}
|
|
653
718
|
</span>
|
|
654
719
|
</Show>
|
|
655
|
-
</
|
|
720
|
+
</label>
|
|
656
721
|
);
|
|
657
722
|
}
|
|
658
723
|
|
|
@@ -663,7 +728,7 @@ function ResolvedChoice(props: {
|
|
|
663
728
|
const c = props.choice;
|
|
664
729
|
return (
|
|
665
730
|
<div
|
|
666
|
-
class="flex items-center gap-3 rounded-lg border border-
|
|
731
|
+
class="flex items-center gap-3 rounded-lg border border-border bg-accent px-3 py-2.5 text-sm font-medium text-accent-foreground"
|
|
667
732
|
role={props.optimistic ? 'status' : undefined}
|
|
668
733
|
>
|
|
669
734
|
<Check size={16} aria-hidden="true" />
|
|
@@ -228,7 +228,7 @@ export function Coachmark(props: CoachmarkProps) {
|
|
|
228
228
|
<Show when={props.badge}>
|
|
229
229
|
<span
|
|
230
230
|
part="badge"
|
|
231
|
-
class={cn('inline-flex items-center rounded-full px-1.5 py-0.5 text-
|
|
231
|
+
class={cn('inline-flex items-center rounded-full px-1.5 py-0.5 text-micro font-medium uppercase leading-none tracking-wide', BADGE_CLS)}
|
|
232
232
|
>
|
|
233
233
|
{props.badge}
|
|
234
234
|
</span>
|
|
@@ -122,8 +122,8 @@ function CodeBlockCode(props: CodeBlockCodeProps) {
|
|
|
122
122
|
|
|
123
123
|
const codeTextSize = () => {
|
|
124
124
|
switch (config.proseSize()) {
|
|
125
|
-
case 'xs': return 'text-
|
|
126
|
-
case 'sm': return 'text-
|
|
125
|
+
case 'xs': return 'text-caption';
|
|
126
|
+
case 'sm': return 'text-compact';
|
|
127
127
|
case 'base': return 'text-sm';
|
|
128
128
|
case 'lg': return 'text-base';
|
|
129
129
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
For,
|
|
12
12
|
} from 'solid-js';
|
|
13
13
|
import { cn } from '../utils/cn';
|
|
14
|
+
import { useChatConfig, textClass } from '../primitives/chat-config';
|
|
14
15
|
import {
|
|
15
16
|
type ComposerDoc,
|
|
16
17
|
type EntityRef,
|
|
@@ -242,6 +243,7 @@ function setCaretToOffset(root: HTMLElement, offset: number): void {
|
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
export function Composer(props: ComposerProps): JSX.Element {
|
|
246
|
+
const config = useChatConfig();
|
|
245
247
|
let editable!: HTMLDivElement;
|
|
246
248
|
// A SIGNAL (not a plain `let`): usePosition's floating ref must be reactive so
|
|
247
249
|
// Floating UI recomputes once the menu mounts. A non-reactive ref left the menu
|
|
@@ -799,23 +801,78 @@ export function Composer(props: ComposerProps): JSX.Element {
|
|
|
799
801
|
// This is covered by updateTriggerState() in handleInput.
|
|
800
802
|
|
|
801
803
|
const maxH = () => props.maxHeight ?? 240;
|
|
802
|
-
//
|
|
803
|
-
//
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
//
|
|
804
|
+
// This element is `contenteditable` with `role="textbox"` — a real tab stop,
|
|
805
|
+
// and the primary one in the kit — so it must never be left with NO focus
|
|
806
|
+
// affordance. WHICH element draws it depends on who owns the frame:
|
|
807
|
+
//
|
|
808
|
+
// - Standalone (the `else` branch below renders the rounded shell): the FRAME
|
|
809
|
+
// owns the ring via `focus-within:ring-*`, exactly like `PromptInput`, so
|
|
810
|
+
// the editable neutralizes its own. The editable is a square, full-bleed
|
|
811
|
+
// box inside a `rounded-xl` shell, so an outline on IT painted a sharp
|
|
812
|
+
// rectangle hugging the text region inside the rounded border — the bug.
|
|
813
|
+
// - Bare: the host owns radius/bg/padding and may draw nothing at all
|
|
814
|
+
// (`PromptInput` passes its own `editableClass` and rings its frame), so
|
|
815
|
+
// the editable keeps `kai-focus-inset` — a ring inside the border box,
|
|
816
|
+
// since an outset one would be clipped by a rounded host shell.
|
|
817
|
+
//
|
|
818
|
+
// A consumer passing `editableClass` still replaces this wholesale — including
|
|
819
|
+
// the `text-start` pin below, so a replacement string owes its own (`PromptInput`,
|
|
820
|
+
// the one in-repo caller, carries it).
|
|
809
821
|
const editableCls = () =>
|
|
810
822
|
props.editableClass ??
|
|
811
|
-
|
|
823
|
+
cn(
|
|
824
|
+
// `text-start` is a PIN, not a style choice: `text-align` inherits, so any
|
|
825
|
+
// centered ancestor (`Empty`'s root did exactly this) reached in and centered
|
|
826
|
+
// the placeholder AND the typed text. An input control's text alignment is a
|
|
827
|
+
// fact about the control, so it states it rather than inheriting it. LOGICAL
|
|
828
|
+
// (`start`), not `text-left` — in RTL the correct edge is the right one, and
|
|
829
|
+
// pinning the physical value would be a worse bug than the one it fixes.
|
|
830
|
+
'text-foreground min-h-[44px] w-full text-start overflow-y-auto whitespace-pre-wrap break-words',
|
|
831
|
+
// Size from the SAME token every other text surface reads (`ChatConfig`'s
|
|
832
|
+
// proseSize, default `sm` = the 14px the rest of the kit sets explicitly) —
|
|
833
|
+
// `Markdown`, `MessageContent` and `PromptInputTextarea` all go through
|
|
834
|
+
// `textClass`. Without it the editable set no size at all and just inherited
|
|
835
|
+
// whatever the shadow root's base was (16px in a stock page), so the composer
|
|
836
|
+
// read a step larger than every component beside it. Going through the token
|
|
837
|
+
// also means `proseSize` moves the composer like it moves `PromptInput`,
|
|
838
|
+
// rather than leaving one input deaf to it.
|
|
839
|
+
textClass(config.proseSize()),
|
|
840
|
+
props.bare
|
|
841
|
+
? 'kai-focus-inset'
|
|
842
|
+
: 'outline-none focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
|
|
843
|
+
);
|
|
812
844
|
|
|
813
845
|
const inner = (
|
|
814
846
|
<>
|
|
815
847
|
{/* Static style for CSS Custom Highlight API decoration.
|
|
816
848
|
No-op in browsers that don't support ::highlight(); the selector
|
|
817
849
|
is simply unrecognized and dropped. */}
|
|
818
|
-
<style>{
|
|
850
|
+
<style>{`
|
|
851
|
+
/* MARKED text, not selected text. Reads --color-highlight (overridable via
|
|
852
|
+
--kai-color-highlight), a warm <mark> yellow. It used to be a 22% tint of
|
|
853
|
+
--color-primary with a #6366f1 indigo fallback — but the kit's
|
|
854
|
+
--color-primary is a NEUTRAL (near-black in light, near-white in dark),
|
|
855
|
+
so the indigo the fallback implied never appeared and the highlight
|
|
856
|
+
rendered plain grey in both themes.
|
|
857
|
+
|
|
858
|
+
NO \`prefers-color-scheme\` here, deliberately — the same trap that put
|
|
859
|
+
the pill hues below at 2.33:1. The OS scheme and the kit's RESOLVED
|
|
860
|
+
theme are different questions, and \`theme="light"\` on a dark-OS machine
|
|
861
|
+
is an ordinary configuration; a scheme-keyed value would paint the dark
|
|
862
|
+
amber on the light field. The pills answer this by scoping to \`.dark\`,
|
|
863
|
+
which is NOT available here: \`::highlight()\` names a document-registered
|
|
864
|
+
highlight, so there is no wrapper to hang a descendant selector on.
|
|
865
|
+
|
|
866
|
+
It does not need one. \`--color-highlight\` reaches this pseudo through
|
|
867
|
+
highlight inheritance from the ORIGINATING element — the text inside the
|
|
868
|
+
\`.dark\` wrapper — so the token already answers the resolved theme,
|
|
869
|
+
verified by rendering the dark amber under \`theme="dark"\` with only the
|
|
870
|
+
LIGHT literal below as a fallback. The literal is therefore reachable
|
|
871
|
+
only when no kit stylesheet is loaded at all, and it is deliberately the
|
|
872
|
+
light one: an unstyled page is a white page, and guessing from the OS is
|
|
873
|
+
exactly the failure above. */
|
|
874
|
+
::highlight(${highlightName}) { background-color: var(--color-highlight, hsl(45 96% 78%)); }
|
|
875
|
+
`}</style>
|
|
819
876
|
{/* Atomic entity pill styling. Self-contained (currentColor-based) so it
|
|
820
877
|
renders correctly without depending on a Tailwind rebuild for the
|
|
821
878
|
custom .kai-composer-pill class. */}
|
|
@@ -846,12 +903,24 @@ export function Composer(props: ComposerProps): JSX.Element {
|
|
|
846
903
|
.kai-composer-pill[data-kind="agent"] { color: var(--kai-pill-agent, #7c3aed); }
|
|
847
904
|
/* Brighter hues on dark so they don't muddy against a dark field. The
|
|
848
905
|
consumer's --kai-pill-* override still wins (it's the var value); only
|
|
849
|
-
the FALLBACK shifts per scheme.
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
906
|
+
the FALLBACK shifts per scheme.
|
|
907
|
+
|
|
908
|
+
Keyed on the kit's RESOLVED theme (the \`.dark\` wrapper an element
|
|
909
|
+
facade puts inside its shadow root, and the \`.dark\` class the docs /
|
|
910
|
+
Storybook put on <html>) — NOT \`prefers-color-scheme\`. Those are
|
|
911
|
+
different questions: \`theme="light"\` on a machine whose OS is dark is
|
|
912
|
+
an ordinary, common configuration, and the media query answered the OS
|
|
913
|
+
while the surface under the pill answered the attribute. That painted
|
|
914
|
+
the DARK hue (#6ea8fe) on the LIGHT field: 2.33:1, under both the
|
|
915
|
+
4.5:1 text floor (SC 1.4.3) and even 3:1. Matched pairs looked fine,
|
|
916
|
+
which is why it survived — never scheme-match a colour whose backdrop
|
|
917
|
+
is chosen by the theme attribute. */
|
|
918
|
+
.dark .kai-composer-pill[data-kind="skill"] { color: var(--kai-pill-skill, #6ea8fe); }
|
|
919
|
+
.dark .kai-composer-pill[data-kind="agent"] { color: var(--kai-pill-agent, #c4a7fc); }
|
|
920
|
+
/* Dimmer than the label, but the sigil is what makes a pill read as
|
|
921
|
+
\`/skill\` vs \`@agent\`, so it is a meaningful glyph and owes 3:1
|
|
922
|
+
(SC 1.4.11). At 0.6 the light-mode composite was 2.52:1. */
|
|
923
|
+
.kai-composer-pill-sigil { opacity: 0.8; font-weight: 600; }
|
|
855
924
|
/* Plugins (and any other/unknown kind): the richer CHIP — background +
|
|
856
925
|
icon — so a composite bundle reads differently at a glance. */
|
|
857
926
|
.kai-composer-pill:not([data-kind="skill"]):not([data-kind="agent"]) {
|
|
@@ -991,7 +1060,12 @@ export function Composer(props: ComposerProps): JSX.Element {
|
|
|
991
1060
|
) : (
|
|
992
1061
|
<div
|
|
993
1062
|
class={cn(
|
|
1063
|
+
// Same frame-owns-the-ring pattern as `PromptInput`: the editable inside
|
|
1064
|
+
// neutralizes its own outline (see `editableCls`), so this rounded shell
|
|
1065
|
+
// draws the focus affordance and the ring follows `rounded-xl` instead of
|
|
1066
|
+
// painting a square box around the inner text region.
|
|
994
1067
|
'kai-composer relative rounded-xl bg-surface p-2',
|
|
1068
|
+
'focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-0',
|
|
995
1069
|
props.disabled && 'cursor-not-allowed opacity-60',
|
|
996
1070
|
)}
|
|
997
1071
|
>
|
|
@@ -18,7 +18,7 @@ import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primi
|
|
|
18
18
|
import { useCardResolution } from './use-card-resolution';
|
|
19
19
|
import { emitCardEvent } from '../primitives/card-routing';
|
|
20
20
|
import { useCardHost } from '../primitives/card-host';
|
|
21
|
-
import { AlertTriangle, Check
|
|
21
|
+
import { AlertTriangle, Check } from 'lucide-solid';
|
|
22
22
|
|
|
23
23
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
24
24
|
// Types (confirm.schema.json) — AUTHORED IN ../primitives/card-data-types.ts.
|
|
@@ -316,14 +316,23 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
|
|
|
316
316
|
>
|
|
317
317
|
<div class="flex w-full flex-wrap items-center justify-between gap-2">
|
|
318
318
|
<Show when={local.data?.dismissible === true}>
|
|
319
|
+
{/* The contract `dismiss` verb — a footer ACTION (it emits
|
|
320
|
+
`{kind:'dismiss'}` and collapses the card to a re-openable
|
|
321
|
+
stub), not the chrome close in `Card`'s own `dismissible`
|
|
322
|
+
prop, which the contract cards deliberately never set.
|
|
323
|
+
Labelled rather than a bare ghost ✕: every other control in
|
|
324
|
+
this row says what it does, and an unlabelled 28px glyph
|
|
325
|
+
300px from the primary button reads as stray chrome. Same
|
|
326
|
+
shape in all four cards — `form` already looked like this.
|
|
327
|
+
`aria-label` is kept (redundant with the text) so the
|
|
328
|
+
element tests that select on it keep working. */}
|
|
319
329
|
<Button
|
|
320
330
|
type="button"
|
|
321
331
|
variant="ghost"
|
|
322
|
-
size="icon-sm"
|
|
323
332
|
aria-label="Dismiss"
|
|
324
333
|
onClick={onDismiss}
|
|
325
334
|
>
|
|
326
|
-
|
|
335
|
+
Dismiss
|
|
327
336
|
</Button>
|
|
328
337
|
</Show>
|
|
329
338
|
<div ref={actionsRef} class="ml-auto flex flex-wrap items-center gap-2">
|
|
@@ -375,7 +384,14 @@ export function ConfirmCard(props: ConfirmCardProps): JSX.Element {
|
|
|
375
384
|
<p class="text-sm font-semibold text-foreground">{local.data?.heading}</p>
|
|
376
385
|
</Show>
|
|
377
386
|
<Show when={local.data?.body}>
|
|
378
|
-
|
|
387
|
+
{/* The supporting sentence sits directly under the heading it
|
|
388
|
+
supports, muted — the same contrast step the shell header makes
|
|
389
|
+
between `heading` and `description`. It stays in the BODY (not
|
|
390
|
+
the shell's `description` slot, where `form`'s and `choice`'s
|
|
391
|
+
live) because `data.heading` is a documented IN-BODY heading
|
|
392
|
+
that this sentence has to stay beneath, and because this div is
|
|
393
|
+
the Enter-to-confirm tab stop. */}
|
|
394
|
+
<p class="text-sm text-muted-foreground">{local.data?.body}</p>
|
|
379
395
|
</Show>
|
|
380
396
|
</div>
|
|
381
397
|
</Card>
|
|
@@ -31,9 +31,32 @@ export function relativeTimeShort(iso?: string, now: number = Date.now()): strin
|
|
|
31
31
|
return `${Math.floor(hours / 24)}d ago`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Whether a conversation should show an unread indicator (owner round,
|
|
36
|
+
* 2026-08-26). `lastReadAt`'s own doc (`types.ts`) has the full contract;
|
|
37
|
+
* this is the one place that reads it, so both list surfaces (this file's
|
|
38
|
+
* `ConversationItem` and the widget-box `ConversationPanel`) derive it
|
|
39
|
+
* identically rather than each restating the comparison.
|
|
40
|
+
*
|
|
41
|
+
* Absent `lastReadAt` reads as NOT unread — the decide-loudly default for a
|
|
42
|
+
* store that never implements `ConversationStore.markRead` at all (every
|
|
43
|
+
* summary it returns leaves the field undefined forever, so this always
|
|
44
|
+
* returns `false` for it) rather than guessing "probably unread" from a
|
|
45
|
+
* signal the store never actually provided. Defensive `Date.parse`, same
|
|
46
|
+
* pattern as `relativeTimeShort` above: an unparsable date reads as not
|
|
47
|
+
* unread rather than throwing.
|
|
48
|
+
*/
|
|
49
|
+
export function isConversationUnread(conv: Pick<ConversationSummary, 'updatedAt' | 'lastReadAt'>): boolean {
|
|
50
|
+
if (!conv.lastReadAt) return false;
|
|
51
|
+
const updated = Date.parse(conv.updatedAt);
|
|
52
|
+
const read = Date.parse(conv.lastReadAt);
|
|
53
|
+
if (Number.isNaN(updated) || Number.isNaN(read)) return false;
|
|
54
|
+
return updated > read;
|
|
55
|
+
}
|
|
56
|
+
|
|
34
57
|
/**
|
|
35
58
|
* The slotted-item shape rendered by `<kai-conversation-item>` — the composed
|
|
36
|
-
* row of the consumer-owned loop
|
|
59
|
+
* row of the consumer-owned loop. Distinct from the
|
|
37
60
|
* data-mode `ConversationItem` above, which batteries mode keeps rendering
|
|
38
61
|
* unchanged: this one takes REGIONS, not a `ConversationSummary`.
|
|
39
62
|
*
|
|
@@ -41,16 +64,19 @@ export function relativeTimeShort(iso?: string, now: number = Date.now()): strin
|
|
|
41
64
|
* title), plus `leading`, `meta`, and `menu`. The `menu` region takes the
|
|
42
65
|
* consumer's OWN popover (rename / fork / archive live there); the component
|
|
43
66
|
* provides only the region plus focus and ARIA plumbing, never a declarative
|
|
44
|
-
* actions prop. Activation
|
|
45
|
-
* (`createConversationItemsController` in
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
67
|
+
* actions prop. Activation has two modes: inside `<kai-conversations>` it
|
|
68
|
+
* lives in the CONTAINER (`createConversationItemsController` in
|
|
69
|
+
* conversation-list.tsx — this row renders no handler and the
|
|
70
|
+
* `data-kai-item-menu` marker on the menu region is what the container's
|
|
71
|
+
* activation guard keys off so a click in the consumer's menu never also
|
|
72
|
+
* selects the row); STANDALONE, `onActivate` makes the row body
|
|
73
|
+
* its own tabbable button-role control — click / Enter / Space — with the menu
|
|
74
|
+
* still outside the control as the body's sibling.
|
|
49
75
|
*
|
|
50
76
|
* ARIA contract for direct Solid use: the row renders `role="listitem"` holding
|
|
51
77
|
* a `role="button"` body (`aria-current` marks the active row, the same dialect
|
|
52
78
|
* as the batteries-mode row above) with the menu as the body's tabbable SIBLING
|
|
53
|
-
* (
|
|
79
|
+
* (axe `nested-interactive` bans focusable descendants of
|
|
54
80
|
* a control, and `aria-required-children` outlaws non-option content anywhere
|
|
55
81
|
* inside a listbox, which is why the vocabulary is not listbox/option). Render
|
|
56
82
|
* your loop's rows inside a `role="list"` ancestor; element mode has no such
|
|
@@ -80,13 +106,22 @@ export interface SlottedConversationItemProps {
|
|
|
80
106
|
* Solid consumers rendering the component directly leave it off and the row
|
|
81
107
|
* itself is the group. */
|
|
82
108
|
hostSemantics?: boolean;
|
|
109
|
+
/** STANDALONE activation: when set, the row body is itself the
|
|
110
|
+
* activation control — `tabindex="0"` on the `role="button"` body, and
|
|
111
|
+
* click / Enter / Space call this. The menu region never triggers it (it is
|
|
112
|
+
* the body's sibling, outside the control). Inside `<kai-conversations>`
|
|
113
|
+
* leave it UNSET: the container's controller owns activation (its delegated
|
|
114
|
+
* click/keydown → `kai-conversation-select`) plus roving tabindex, and a
|
|
115
|
+
* handler here would double-fire. The `kai-conversation-item` facade passes
|
|
116
|
+
* it only when the item is standalone. */
|
|
117
|
+
onActivate?: () => void;
|
|
83
118
|
class?: string;
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
export function SlottedConversationItem(props: SlottedConversationItemProps) {
|
|
87
|
-
const [local] = splitProps(props, ['conversationId', 'active', 'compact', 'leading', 'meta', 'menu', 'children', 'hostSemantics', 'class']);
|
|
122
|
+
const [local] = splitProps(props, ['conversationId', 'active', 'compact', 'leading', 'meta', 'menu', 'children', 'hostSemantics', 'onActivate', 'class']);
|
|
88
123
|
return (
|
|
89
|
-
// The sibling restructure
|
|
124
|
+
// The sibling restructure: axe nested-interactive
|
|
90
125
|
// bans focusable descendants of an activation control, so the control role
|
|
91
126
|
// sits on the row BODY and the consumer's menu is its tabbable SIBLING
|
|
92
127
|
// (the nav.tsx TrailingActions precedent). The vocabulary is
|
|
@@ -112,6 +147,18 @@ export function SlottedConversationItem(props: SlottedConversationItemProps) {
|
|
|
112
147
|
role="button"
|
|
113
148
|
data-kai-item-body
|
|
114
149
|
aria-current={local.active ? 'true' : 'false'}
|
|
150
|
+
// Standalone activation only: with `onActivate` unset —
|
|
151
|
+
// the inside-a-container case — no tabindex and no handlers render, so
|
|
152
|
+
// the container's delegated activation stays the single path.
|
|
153
|
+
tabindex={local.onActivate ? 0 : undefined}
|
|
154
|
+
onClick={() => local.onActivate?.()}
|
|
155
|
+
onKeyDown={(e: KeyboardEvent) => {
|
|
156
|
+
if (!local.onActivate) return;
|
|
157
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
158
|
+
e.preventDefault(); // Space must not scroll the page
|
|
159
|
+
local.onActivate();
|
|
160
|
+
}
|
|
161
|
+
}}
|
|
115
162
|
class="flex min-w-0 flex-1 cursor-pointer items-center gap-2.5 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
116
163
|
>
|
|
117
164
|
<Show when={local.leading}>
|