@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
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
// element, so it carries its own shadow root + the shared kit stylesheet — it's
|
|
10
10
|
// viewport-positioned AND fully kit-styled, never a raw div.
|
|
11
11
|
//
|
|
12
|
+
// ADOPTS, else creates: `ensureMounted` first
|
|
13
|
+
// looks for a connected `<kai-toast-region>` already in the document and binds
|
|
14
|
+
// the store to it, so an app that placed its own region and also calls
|
|
15
|
+
// `toast()` gets ONE region, not two overlapping ones. It creates a region only
|
|
16
|
+
// when none exists. With two or more candidate regions the choice is genuinely
|
|
17
|
+
// ambiguous — the first in document order wins, and a one-time console.warn
|
|
18
|
+
// says so (decide loudly).
|
|
19
|
+
//
|
|
12
20
|
// SSR-safe: every DOM touch is guarded by `typeof document`. On the server,
|
|
13
21
|
// raising a toast is an inert no-op (the store updates, nothing mounts).
|
|
14
22
|
|
|
@@ -156,21 +164,75 @@ function resolveDuration(item: Pick<ToastItem, 'duration' | 'action'>): number {
|
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
/**
|
|
159
|
-
* Lazily
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
167
|
+
* Lazily resolve the single `<kai-toast-region>` host for a target and bind the
|
|
168
|
+
* store to its `toasts` property. Idempotent + SSR-safe. The element upgrade
|
|
169
|
+
* (its `defineWebComponent` registration) happens when the elements bundle
|
|
170
|
+
* loads; setting the property before/after upgrade both work because
|
|
163
171
|
* `customElement` reflects late-set properties.
|
|
172
|
+
*
|
|
173
|
+
* ADOPT-IF-PRESENT: a connected
|
|
174
|
+
* `<kai-toast-region>` already in the document (same `target`, not already
|
|
175
|
+
* claimed for another target) is adopted — the store binds to IT, and no
|
|
176
|
+
* second region mounts. Adoption respects the region's authored attributes
|
|
177
|
+
* (position/stack/…): config from `configureToasts()` is not stamped onto an
|
|
178
|
+
* adopted region here, though an explicit `configureToasts()` call still
|
|
179
|
+
* updates every region, adopted included. Binding the store REPLACES a
|
|
180
|
+
* `toasts` array the app set as data — after the first `toast()` call the
|
|
181
|
+
* imperative store owns the adopted region's list. Only when no candidate
|
|
182
|
+
* exists is a fresh region created on `document.body`. Two or more candidates
|
|
183
|
+
* for the same target are genuinely ambiguous: the first in document order
|
|
184
|
+
* wins, with a one-time console.warn (decide loudly).
|
|
185
|
+
*
|
|
186
|
+
* If an adopted (or created) region later leaves the DOM, the cache entry is
|
|
187
|
+
* dropped and the next call resolves fresh — adopt again if a region exists,
|
|
188
|
+
* else create — so a removed region never becomes a dead cache entry that
|
|
189
|
+
* swallows toasts.
|
|
164
190
|
*/
|
|
165
191
|
// One region per distinct target (the `null` key = the global / viewport region).
|
|
166
192
|
// Every region binds the SAME store proxy and filters to its own target, so a
|
|
167
193
|
// toast routes to the region for its `target`.
|
|
168
194
|
const regions = new Map<HTMLElement | null, HTMLElement>();
|
|
169
195
|
|
|
196
|
+
// Decide-loudly, once: ambiguity between 2+ candidate regions is warned about a
|
|
197
|
+
// single time per page, not on every toast.
|
|
198
|
+
let warnedAmbiguousRegions = false;
|
|
199
|
+
|
|
200
|
+
/** Read a region element's `target` property (undefined pre-upgrade → null =
|
|
201
|
+
* the global region). */
|
|
202
|
+
function regionTarget(el: HTMLElement): HTMLElement | null {
|
|
203
|
+
return (el as unknown as { target?: HTMLElement }).target ?? null;
|
|
204
|
+
}
|
|
205
|
+
|
|
170
206
|
export function ensureMounted(target: HTMLElement | null = null): HTMLElement | undefined {
|
|
171
207
|
if (typeof document === 'undefined') return undefined;
|
|
172
208
|
const cached = regions.get(target);
|
|
173
209
|
if (cached && cached.isConnected) return cached;
|
|
210
|
+
// The region this key resolved to left the DOM — drop the dead entry so it
|
|
211
|
+
// can never swallow toasts, and resolve fresh below.
|
|
212
|
+
if (cached) regions.delete(target);
|
|
213
|
+
|
|
214
|
+
// Adopt a region the app already placed. Regions this store already
|
|
215
|
+
// claimed for OTHER targets are not candidates.
|
|
216
|
+
const claimed = new Set(regions.values());
|
|
217
|
+
const candidates = Array.from(
|
|
218
|
+
document.querySelectorAll<HTMLElement>('kai-toast-region'),
|
|
219
|
+
).filter((el) => el.isConnected && !claimed.has(el) && regionTarget(el) === target);
|
|
220
|
+
if (candidates.length > 1 && !warnedAmbiguousRegions) {
|
|
221
|
+
warnedAmbiguousRegions = true;
|
|
222
|
+
console.warn(
|
|
223
|
+
`[kitn] toast(): ${candidates.length} candidate <kai-toast-region> elements are in the ` +
|
|
224
|
+
'document; adopting the first in document order. Keep one region per target so the ' +
|
|
225
|
+
'imperative toast() API has an unambiguous home.',
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const adopted = candidates[0];
|
|
229
|
+
if (adopted) {
|
|
230
|
+
(adopted as unknown as { toasts: ToastItem[] }).toasts = toasts as ToastItem[];
|
|
231
|
+
regions.set(target, adopted);
|
|
232
|
+
setMounted(true);
|
|
233
|
+
return adopted;
|
|
234
|
+
}
|
|
235
|
+
|
|
174
236
|
const el = document.createElement('kai-toast-region') as HTMLElement;
|
|
175
237
|
// Append FIRST so the element upgrades, THEN bind. Setting a property before
|
|
176
238
|
// upgrade loses it: component-register resets every prop in the constructor on
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/primitives/url-scheme-policy.ts
|
|
2
|
+
// The kit's one URL-scheme allow/deny policy, split out of card-routing.ts so it
|
|
3
|
+
// has NO DOM dependency (`URL` is a global in both the browser and Node — the
|
|
4
|
+
// rest of card-routing.ts uses HTMLElement/window/CustomEvent, which aren't).
|
|
5
|
+
// That split matters beyond tidiness: this schema.ts (agent-tooling/construct)
|
|
6
|
+
// runs under tsconfig.mcp.json's Node-only, no-DOM-lib pass (it's imported
|
|
7
|
+
// transitively via mcp/tools/construct.ts), and importing anything from
|
|
8
|
+
// card-routing.ts there drags its HTMLElement/window/Document/CustomEvent
|
|
9
|
+
// references into that compile and breaks it — even though only isSafeUrl is
|
|
10
|
+
// used. card-routing.ts re-exports everything here so its own public surface
|
|
11
|
+
// (isSafeUrl/isScriptUrl, used by markdown.tsx/artifact.tsx/source.tsx) is
|
|
12
|
+
// unchanged; this is a location split, not a second policy.
|
|
13
|
+
|
|
14
|
+
const SAFE_SCHEMES = ['http:', 'https:', 'mailto:'];
|
|
15
|
+
const SCRIPT_SCHEMES = ['javascript:', 'vbscript:'];
|
|
16
|
+
|
|
17
|
+
/** The scheme the WHATWG parser reads out of `url`, resolved against a base so a
|
|
18
|
+
* relative input inherits `http:`. `undefined` when it will not parse at all.
|
|
19
|
+
*
|
|
20
|
+
* ONE parser for the two questions below, deliberately: the parsing is where
|
|
21
|
+
* the subtlety lives (it strips embedded tabs/newlines and trims surrounding
|
|
22
|
+
* whitespace before reading the scheme, so `java\nscript:` and
|
|
23
|
+
* ` javascript:... ` are both read as `javascript:` -- which a regex over the
|
|
24
|
+
* raw string would miss). Two predicates over one parse cannot drift on that;
|
|
25
|
+
* two hand-rolled parsers would. */
|
|
26
|
+
function schemeOf(url: string): string | undefined {
|
|
27
|
+
try { return new URL(url, 'http://_invalid_base').protocol; } catch { return undefined; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** True when `url` is safe to put in an href / hand to `window.open` / an `<img src>`.
|
|
31
|
+
*
|
|
32
|
+
* Exported so the MARKDOWN renderer, the ARTIFACT viewer and the construct
|
|
33
|
+
* format's `widget.launcherIcon` reuse this exact guard rather than growing a
|
|
34
|
+
* second scheme list that can drift from this one.
|
|
35
|
+
*
|
|
36
|
+
* Resolving against a base is deliberate and is what makes it correct for
|
|
37
|
+
* markdown: a relative or fragment link (`/docs`, `#section`, `./rel`) is
|
|
38
|
+
* ordinary markdown, and resolving it inherits the base's `http:` so it
|
|
39
|
+
* passes, while `javascript:`/`data:`/`vbscript:` keep their own protocol and
|
|
40
|
+
* fail. It is correct for the artifact viewer for the same reason: a file with
|
|
41
|
+
* no `src` to resolve against yields a bare relative path (`docs/report.pdf`),
|
|
42
|
+
* which is a legitimate artifact address. Contrast `isRenderableLink` in
|
|
43
|
+
* `primitives/link-preview.ts`, which takes NO base and so demands an absolute
|
|
44
|
+
* http(s) URL: that is the right guard for a model-supplied citation -- a
|
|
45
|
+
* reference to a page on the public web -- this one for markdown body links,
|
|
46
|
+
* artifact file addresses, and a construct's launcher icon. */
|
|
47
|
+
export function isSafeUrl(url: string): boolean {
|
|
48
|
+
const scheme = schemeOf(url);
|
|
49
|
+
return scheme !== undefined && SAFE_SCHEMES.includes(scheme);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** True when navigating to `url` would EXECUTE it as script in the initiating
|
|
53
|
+
* document's origin.
|
|
54
|
+
*
|
|
55
|
+
* Strictly narrower than `!isSafeUrl(url)`, and not a competing policy: it
|
|
56
|
+
* answers a different question, for the one sink where the allowlist is the
|
|
57
|
+
* wrong tool. An `<iframe src>` legitimately takes `data:` and `blob:` (a
|
|
58
|
+
* `data:` blob artifact is a documented `<kai-artifact>` use -- it is what
|
|
59
|
+
* `displayUrl` exists for) and both get an OPAQUE origin in every modern
|
|
60
|
+
* browser, so neither can reach the host page. `javascript:`/`vbscript:` are
|
|
61
|
+
* the schemes that run in the EMBEDDER's origin, and they are the whole risk:
|
|
62
|
+
* the default sandbox (no `allow-same-origin`) already makes the browser refuse
|
|
63
|
+
* them, but a consumer who sets `allow-same-origin` turns a model-supplied
|
|
64
|
+
* `src` into host-origin script execution. The artifact viewer used to suggest
|
|
65
|
+
* that setting for "an artifact you trust"; it no longer does, and this guard
|
|
66
|
+
* is what protects the consumers who took the old advice. */
|
|
67
|
+
export function isScriptUrl(url: string): boolean {
|
|
68
|
+
const scheme = schemeOf(url);
|
|
69
|
+
return scheme !== undefined && SCRIPT_SCHEMES.includes(scheme);
|
|
70
|
+
}
|
|
@@ -14,8 +14,8 @@ export function observeContentHeight(el: Element, onHeight: (height: number) =>
|
|
|
14
14
|
/** Observe an element's resizes but report the FULL rendered document height (not the
|
|
15
15
|
* observed element's own content-box) — `document.body.scrollHeight`.
|
|
16
16
|
*
|
|
17
|
-
* Used by the remote-card provider runtime to size the host iframe
|
|
18
|
-
*
|
|
17
|
+
* Used by the remote-card provider runtime to size the host iframe:
|
|
18
|
+
* `observeContentHeight` reports the ResizeObserver content-box of
|
|
19
19
|
* the observed root, which drops that root's OWN padding/border and any ancestor
|
|
20
20
|
* (e.g. `<body>`) padding above it — the host then clips the bottom of the card by
|
|
21
21
|
* exactly those lost pixels. `scrollHeight` is the full padding-box height as actually
|
package/src/schemas/tool-defs.ts
CHANGED
|
@@ -132,7 +132,7 @@ export interface CardToolOptions<P extends ToolProvider = ToolProvider> {
|
|
|
132
132
|
*/
|
|
133
133
|
readonly strict?: boolean;
|
|
134
134
|
/**
|
|
135
|
-
* Narrow the DERIVED tool schema per card type, after projection
|
|
135
|
+
* Narrow the DERIVED tool schema per card type, after projection.
|
|
136
136
|
*
|
|
137
137
|
* A card's authored schema is deliberately permissive — `files` is optional on an
|
|
138
138
|
* artifact, its items need only a `path` — because the card CONTRACT must accept
|
|
@@ -147,6 +147,16 @@ export interface CardToolOptions<P extends ToolProvider = ToolProvider> {
|
|
|
147
147
|
* unknown path or a required name the node has no property for is a TypeError
|
|
148
148
|
* naming the card, the path and the field, because a tool that can never be
|
|
149
149
|
* satisfied must not ship quietly.
|
|
150
|
+
*
|
|
151
|
+
* IT CANNOT REACH A FORM CARD'S FIELDS, and that is a known limitation rather
|
|
152
|
+
* than an oversight. The form
|
|
153
|
+
* card's payload IS a JSON Schema, so `form.schema.json` describes `properties`
|
|
154
|
+
* as a map of field definitions and there is no node at `properties.ticketId`
|
|
155
|
+
* for a rule to land on — `require: { form: [{ path: 'properties.ticketId' }] }`
|
|
156
|
+
* raises the TypeError above, correctly. So an app cannot pin
|
|
157
|
+
* `x-kai-format: 'custom'` onto one field of a form the model authors. The
|
|
158
|
+
* enum on that hint is what makes a small model pick a valid token; an app that
|
|
159
|
+
* needs a guarantee checks the envelope when it arrives.
|
|
150
160
|
*/
|
|
151
161
|
readonly require?: Readonly<Record<string, readonly CardRequireRule[]>>;
|
|
152
162
|
}
|
|
@@ -442,7 +452,7 @@ function relaxRootCombinators(parameters: Record<string, unknown>, cardType: str
|
|
|
442
452
|
}
|
|
443
453
|
|
|
444
454
|
/**
|
|
445
|
-
* Apply one card's `require` rules to its PROJECTED parameters
|
|
455
|
+
* Apply one card's `require` rules to its PROJECTED parameters.
|
|
446
456
|
*
|
|
447
457
|
* Runs after `project()` and `relaxRootCombinators()`, on the copy the projection
|
|
448
458
|
* built — never on the authored schema, which `registry.validate()` keeps reading.
|
package/src/solid.ts
CHANGED
|
@@ -53,8 +53,18 @@ export { Input } from './ui/input';
|
|
|
53
53
|
export type { InputProps } from './ui/input';
|
|
54
54
|
export { Kbd } from './ui/kbd';
|
|
55
55
|
export type { KbdProps, KbdPlatform } from './ui/kbd';
|
|
56
|
+
export { Checkbox } from './ui/checkbox';
|
|
57
|
+
export type { CheckboxProps } from './ui/checkbox';
|
|
58
|
+
export { Radio, RadioGroup } from './ui/radio';
|
|
59
|
+
export type { RadioProps, RadioGroupProps, RadioOption } from './ui/radio';
|
|
60
|
+
export { CheckboxGroup } from './ui/checkbox-group';
|
|
61
|
+
export type { CheckboxGroupProps, CheckboxOption } from './ui/checkbox-group';
|
|
56
62
|
export { Switch } from './ui/switch';
|
|
57
63
|
export type { SwitchProps } from './ui/switch';
|
|
64
|
+
export { Slider } from './ui/slider';
|
|
65
|
+
export type { SliderProps } from './ui/slider';
|
|
66
|
+
export { Select } from './ui/select';
|
|
67
|
+
export type { SelectProps, SelectOption } from './ui/select';
|
|
58
68
|
export { Tabs } from './ui/tabs';
|
|
59
69
|
export type { TabsProps, TabsVariant, KaiTabItem } from './ui/tabs';
|
|
60
70
|
export { Segmented } from './ui/segmented';
|
|
@@ -69,8 +79,8 @@ export { EditableLabel } from './ui/editable-label';
|
|
|
69
79
|
export type { EditableLabelProps } from './ui/editable-label';
|
|
70
80
|
export { Dialog } from './ui/dialog';
|
|
71
81
|
export type { DialogProps, DialogController } from './ui/dialog';
|
|
72
|
-
export { Dock, DockLauncherGlyph, DockCloseGlyph } from './ui/dock';
|
|
73
|
-
export type { DockProps, DockController, DockPosition, DockFocusOnOpen } from './ui/dock';
|
|
82
|
+
export { Dock, DockLauncherGlyph, DockCloseGlyph, DockLauncherImage } from './ui/dock';
|
|
83
|
+
export type { DockProps, DockController, DockPosition, DockFocusOnOpen, DockLauncherImageProps } from './ui/dock';
|
|
74
84
|
export { Popover } from './ui/popover';
|
|
75
85
|
export type { PopoverProps, PopoverController } from './ui/popover';
|
|
76
86
|
export { Nav } from './ui/nav';
|
|
@@ -136,6 +146,14 @@ export { ChatThread } from './components/chat-thread';
|
|
|
136
146
|
export type { ChatThreadProps, ChatThreadController, ChatThreadContextUsage } from './components/chat-thread';
|
|
137
147
|
export { Screen } from './components/screen';
|
|
138
148
|
export type { ScreenProps, ScreenController } from './components/screen';
|
|
149
|
+
// HomePanel / WidgetTabBar — the widget home screen (Intercom-pattern).
|
|
150
|
+
// Wired into ChatThread itself behind the `home` prop (and from there into
|
|
151
|
+
// `<kai-chat>`'s own `home`/`onHomeLink`); exported here too for a Solid
|
|
152
|
+
// consumer composing either piece directly.
|
|
153
|
+
export { HomePanel } from './components/home-panel';
|
|
154
|
+
export type { HomePanelProps } from './components/home-panel';
|
|
155
|
+
export { WidgetTabBar } from './components/widget-tab-bar';
|
|
156
|
+
export type { WidgetTabBarProps } from './components/widget-tab-bar';
|
|
139
157
|
export { Coachmark } from './components/coachmark';
|
|
140
158
|
export type { CoachmarkProps, CoachmarkController } from './components/coachmark';
|
|
141
159
|
export { Composer } from './components/composer';
|
package/src/state/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ export type { ParsedThread, DroppedStored, SaveScheduler, SaveSchedulerOptions }
|
|
|
23
23
|
// so the no-backend preview runs through the SAME parser a real provider does —
|
|
24
24
|
// and is deliberately impossible to mistake for one. See ./mock.
|
|
25
25
|
export { createMockResponder, DEFAULT_MOCK_REPLIES, MOCK_BANNER, MOCK_MARKER, MOCK_MARKER_KEY, MOCK_MODEL_ID } from './mock';
|
|
26
|
-
export type { MockResponder, MockResponderOptions } from './mock';
|
|
26
|
+
export type { MockReply, MockResponder, MockResponderOptions, MockToolCall, MockTurn } from './mock';
|
|
27
27
|
|
|
28
28
|
// The content-model types every signature above mentions. Without these a
|
|
29
29
|
// consumer importing only from '@kitn.ai/ui/state' cannot annotate the very
|
package/src/state/mock.ts
CHANGED
|
@@ -96,9 +96,42 @@ export const DEFAULT_MOCK_REPLIES: readonly string[] = [
|
|
|
96
96
|
'Mock again. Every frame I send is tagged `_kai_mock` and my usage reports zero tokens, so nothing here can be mistaken for a real turn.',
|
|
97
97
|
];
|
|
98
98
|
|
|
99
|
+
/** One scripted tool call for a mock turn. Framed exactly the way the OpenAI
|
|
100
|
+
* chat-completions wire frames a real one — an announce fragment carrying
|
|
101
|
+
* `id`/`function.name`, then the argument JSON streamed in fragments — so the
|
|
102
|
+
* kit's own reader (`readOpenAIStream`) reassembles it through the same path a
|
|
103
|
+
* real provider's call takes. */
|
|
104
|
+
export interface MockToolCall {
|
|
105
|
+
/** The tool name, e.g. `'get_weather'` or a card tool like `'kai_confirm'`. */
|
|
106
|
+
name: string;
|
|
107
|
+
/** The call's arguments. Serialized with `JSON.stringify` and streamed as
|
|
108
|
+
* fragments, like a real provider. Defaults to `{}`. */
|
|
109
|
+
arguments?: unknown;
|
|
110
|
+
/** Explicit tool-call id. Defaults to `call_kai-mock-<turn>-<n>`, which keeps
|
|
111
|
+
* the mock's naming tell (see tell 3): no provider issues ids in that shape. */
|
|
112
|
+
id?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A scripted mock turn: optional text, then optional tool calls. A turn with
|
|
116
|
+
* tool calls finishes `finish_reason: 'tool_calls'`, exactly as a real
|
|
117
|
+
* tool-calling turn does; a turn without them finishes `'stop'`. */
|
|
118
|
+
export interface MockTurn {
|
|
119
|
+
/** Text streamed (token by token) before the tool calls. */
|
|
120
|
+
text?: string;
|
|
121
|
+
/** Tool calls announced this turn, in order. */
|
|
122
|
+
toolCalls?: readonly MockToolCall[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A canned reply: plain text, or a scripted turn. A string is exactly
|
|
126
|
+
* `{ text }` — the pre-tool-call API unchanged. */
|
|
127
|
+
export type MockReply = string | MockTurn;
|
|
128
|
+
|
|
99
129
|
export interface MockResponderOptions {
|
|
100
|
-
/** Canned replies, cycled one per turn.
|
|
101
|
-
|
|
130
|
+
/** Canned replies, cycled one per turn. Plain strings stream as text; a
|
|
131
|
+
* `MockTurn` can also script tool calls (`{ text, toolCalls }`), which is
|
|
132
|
+
* what lets the zero-config mock exercise the kit's tool/card path without
|
|
133
|
+
* hand-rolled SSE framing. Defaults to `DEFAULT_MOCK_REPLIES`. */
|
|
134
|
+
replies?: readonly MockReply[];
|
|
102
135
|
/** Delay between chunks, in ms. Defaults to 24 — fast enough to feel alive,
|
|
103
136
|
* slow enough that the streaming is visible. `0` streams as fast as the
|
|
104
137
|
* event loop allows, which is what tests want. */
|
|
@@ -144,6 +177,27 @@ function frame(id: string, body: Record<string, unknown>): string {
|
|
|
144
177
|
const delay = (ms: number): Promise<void> =>
|
|
145
178
|
ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
146
179
|
|
|
180
|
+
/** Normalize a canned reply: a plain string is exactly `{ text }`. */
|
|
181
|
+
const asTurn = (reply: MockReply): MockTurn => (typeof reply === 'string' ? { text: reply } : reply);
|
|
182
|
+
|
|
183
|
+
/** How many characters of argument JSON ride in each tool-call fragment. Small
|
|
184
|
+
* enough that any realistic argument object spans several frames, so the
|
|
185
|
+
* reader's fragment-reassembly path — the one a real provider exercises — is
|
|
186
|
+
* exercised here too, rather than a single-frame shortcut it would never see
|
|
187
|
+
* in production. */
|
|
188
|
+
const TOOL_ARG_FRAGMENT_CHARS = 16;
|
|
189
|
+
|
|
190
|
+
/** Split argument JSON into the fragments a provider streams. Never empty: an
|
|
191
|
+
* empty-object call still sends its `'{}'` so the reader has arguments to
|
|
192
|
+
* parse. */
|
|
193
|
+
function argumentFragments(json: string): string[] {
|
|
194
|
+
const out: string[] = [];
|
|
195
|
+
for (let i = 0; i < json.length; i += TOOL_ARG_FRAGMENT_CHARS) {
|
|
196
|
+
out.push(json.slice(i, i + TOOL_ARG_FRAGMENT_CHARS));
|
|
197
|
+
}
|
|
198
|
+
return out.length > 0 ? out : [''];
|
|
199
|
+
}
|
|
200
|
+
|
|
147
201
|
/**
|
|
148
202
|
* Build a mock responder.
|
|
149
203
|
*
|
|
@@ -170,8 +224,9 @@ export function createMockResponder(options: MockResponderOptions = {}): MockRes
|
|
|
170
224
|
let announced = false;
|
|
171
225
|
|
|
172
226
|
return function respond(_prompt?: string): AsyncIterable<string> {
|
|
173
|
-
const
|
|
227
|
+
const scripted = asTurn(pool[turn % pool.length]);
|
|
174
228
|
const id = `kai-mock-${turn + 1}`;
|
|
229
|
+
const turnNumber = turn + 1;
|
|
175
230
|
turn += 1;
|
|
176
231
|
|
|
177
232
|
return {
|
|
@@ -193,15 +248,46 @@ export function createMockResponder(options: MockResponderOptions = {}): MockRes
|
|
|
193
248
|
// exercises the reader's empty-delta path.
|
|
194
249
|
yield frame(id, { choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }] });
|
|
195
250
|
|
|
196
|
-
for (const token of tokenize(
|
|
251
|
+
for (const token of tokenize(scripted.text ?? '', chunkSize)) {
|
|
197
252
|
await delay(delayMs);
|
|
198
253
|
yield frame(id, { choices: [{ index: 0, delta: { content: token }, finish_reason: null }] });
|
|
199
254
|
}
|
|
200
255
|
|
|
256
|
+
// Scripted tool calls, framed the way the OpenAI wire frames real ones:
|
|
257
|
+
// one announce fragment per call (`index`/`id`/`function.name` + empty
|
|
258
|
+
// arguments), then the argument JSON in fragments. The kit's reader
|
|
259
|
+
// reassembles them through the exact path a real provider's call takes.
|
|
260
|
+
const toolCalls = scripted.toolCalls ?? [];
|
|
261
|
+
for (let index = 0; index < toolCalls.length; index += 1) {
|
|
262
|
+
const call = toolCalls[index];
|
|
263
|
+
// Tell 3's shape, extended to call ids: no provider issues these.
|
|
264
|
+
const callId = call.id ?? `call_${MOCK_MODEL_ID}-${turnNumber}-${index + 1}`;
|
|
265
|
+
await delay(delayMs);
|
|
266
|
+
yield frame(id, {
|
|
267
|
+
choices: [{
|
|
268
|
+
index: 0,
|
|
269
|
+
delta: { tool_calls: [{ index, id: callId, function: { name: call.name, arguments: '' } }] },
|
|
270
|
+
finish_reason: null,
|
|
271
|
+
}],
|
|
272
|
+
});
|
|
273
|
+
for (const fragment of argumentFragments(JSON.stringify(call.arguments ?? {}))) {
|
|
274
|
+
await delay(delayMs);
|
|
275
|
+
yield frame(id, {
|
|
276
|
+
choices: [{
|
|
277
|
+
index: 0,
|
|
278
|
+
delta: { tool_calls: [{ index, function: { arguments: fragment } }] },
|
|
279
|
+
finish_reason: null,
|
|
280
|
+
}],
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
201
285
|
// Tell 4: zero usage. A real turn that produced this much text cannot
|
|
202
|
-
// report zero completion tokens.
|
|
286
|
+
// report zero completion tokens. A turn that announced tool calls ends
|
|
287
|
+
// `tool_calls`, exactly as a real one does, so the consumer's tool loop
|
|
288
|
+
// branches the same way it would against a provider.
|
|
203
289
|
yield frame(id, {
|
|
204
|
-
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
|
290
|
+
choices: [{ index: 0, delta: {}, finish_reason: toolCalls.length > 0 ? 'tool_calls' : 'stop' }],
|
|
205
291
|
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
|
|
206
292
|
});
|
|
207
293
|
yield 'data: [DONE]\n\n';
|
package/src/types.ts
CHANGED
|
@@ -37,10 +37,29 @@ export interface ConversationSummary {
|
|
|
37
37
|
/** Fallback for the auto relative time when updatedAt is absent. */
|
|
38
38
|
lastMessageAt?: string;
|
|
39
39
|
updatedAt: string;
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
/** Trailing text describing the row's content, read differently by the two
|
|
41
|
+
* built-in list surfaces (both optional, same field — no widened shape):
|
|
42
|
+
* the desktop `ConversationList`/`ConversationItem` renders it right-aligned
|
|
43
|
+
* as a count/status/"days ago" and auto-derives a short relative time from
|
|
44
|
+
* `updatedAt` (fallback `lastMessageAt`) when it's absent; the widget-box
|
|
45
|
+
* `ConversationPanel` list view (owner rework, 2026-08-26) renders it as the
|
|
46
|
+
* one-line last-message preview under the title instead — that view always
|
|
47
|
+
* computes its own right-aligned relative time from `updatedAt` separately,
|
|
48
|
+
* since a box that size has no room for a third line. `localStorageStore`
|
|
49
|
+
* writes it as the ~80-char truncated last-message preview on every save
|
|
50
|
+
* (`primitives/conversation-store.ts`). */
|
|
43
51
|
trailing?: string;
|
|
52
|
+
/** ISO timestamp of when this conversation was last SEEN by the visitor
|
|
53
|
+
* (widened additively, 2026-08-26 — unread indicators; no existing field
|
|
54
|
+
* fit, since `trailing`/`lastMessageAt`/`updatedAt` are all content-timing
|
|
55
|
+
* facts, not viewing-state). Unread = `updatedAt` is later than this.
|
|
56
|
+
* Written by `ConversationStore.markRead` (see that doc for exactly when
|
|
57
|
+
* and for the decide-loudly default when a store never implements it —
|
|
58
|
+
* an absent `lastReadAt` reads as "not unread," never as "definitely
|
|
59
|
+
* unread," so a store that doesn't support the concept at all simply
|
|
60
|
+
* never shows an indicator rather than guessing). Round-tripped through
|
|
61
|
+
* `list()`/`save()`; never author this by hand. */
|
|
62
|
+
lastReadAt?: string;
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
export interface ConversationGroup {
|
|
@@ -51,3 +70,26 @@ export interface ConversationGroup {
|
|
|
51
70
|
sortOrder: number;
|
|
52
71
|
createdAt: string;
|
|
53
72
|
}
|
|
73
|
+
|
|
74
|
+
/** One row in a `HomeConfig.links` list — a "docs" / "talk to sales" style
|
|
75
|
+
* entry. `href` is optional: with it, the row renders as a link (subject to
|
|
76
|
+
* `isSafeUrl`); without it, the row is a button that emits itself via
|
|
77
|
+
* `HomePanelProps.onLink`. `icon` is either a `renderIcon` name or a safe
|
|
78
|
+
* URL, same resolution `renderIcon` already does for every other icon prop
|
|
79
|
+
* in the kit. */
|
|
80
|
+
export interface HomeLinkEntry {
|
|
81
|
+
label: string;
|
|
82
|
+
href?: string;
|
|
83
|
+
description?: string;
|
|
84
|
+
icon?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The widget home screen's JSON-shaped config (Intercom-pattern home tab).
|
|
88
|
+
* Consumed by `ChatThread`'s `home` prop, which wires it into `HomePanel`/
|
|
89
|
+
* `WidgetTabBar` and, from there, into `<kai-chat>`'s own `home` property. */
|
|
90
|
+
export interface HomeConfig {
|
|
91
|
+
greeting?: { title?: string; subtitle?: string };
|
|
92
|
+
recentConversation?: boolean;
|
|
93
|
+
newConversation?: { label?: string };
|
|
94
|
+
links?: HomeLinkEntry[];
|
|
95
|
+
}
|
package/src/ui/agent-card.tsx
CHANGED
|
@@ -156,7 +156,7 @@ export function AgentCard(props: AgentCardProps) {
|
|
|
156
156
|
/>
|
|
157
157
|
</span>
|
|
158
158
|
<Show when={local.status.label}>
|
|
159
|
-
<span class={cn('text-
|
|
159
|
+
<span class={cn('text-caption font-medium leading-none', TONE_TEXT[tone()])}>
|
|
160
160
|
{local.status.label}
|
|
161
161
|
</span>
|
|
162
162
|
</Show>
|
|
@@ -185,7 +185,7 @@ export function AgentCard(props: AgentCardProps) {
|
|
|
185
185
|
|
|
186
186
|
<span class="flex shrink-0 items-center gap-2">
|
|
187
187
|
<Show when={local.needsAttention}>
|
|
188
|
-
<span class="inline-flex items-center gap-1 rounded-full bg-tool-amber/15 px-2 py-0.5 text-
|
|
188
|
+
<span class="inline-flex items-center gap-1 rounded-full bg-tool-amber/15 px-2 py-0.5 text-caption font-medium leading-none text-tool-amber">
|
|
189
189
|
<Bell class="size-3" aria-hidden="true" />
|
|
190
190
|
Needs you
|
|
191
191
|
</span>
|
package/src/ui/avatar.tsx
CHANGED
|
@@ -8,7 +8,7 @@ export interface AvatarProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
size?: 'sm' | 'md' | 'lg';
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const sizeClasses = { sm: 'h-6 w-6 text-
|
|
11
|
+
const sizeClasses = { sm: 'h-6 w-6 text-micro', md: 'h-8 w-8 text-xs', lg: 'h-10 w-10 text-sm' };
|
|
12
12
|
|
|
13
13
|
export function Avatar(props: AvatarProps) {
|
|
14
14
|
const [local, rest] = splitProps(props, ['src', 'alt', 'fallback', 'size', 'class']);
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { type JSX, For, Show, splitProps } from 'solid-js';
|
|
2
|
+
import { cn } from '../utils/cn';
|
|
3
|
+
import { Checkbox } from './checkbox';
|
|
4
|
+
|
|
5
|
+
/** One choice in a {@link CheckboxGroup}. */
|
|
6
|
+
export interface CheckboxOption<T = string> {
|
|
7
|
+
/** The value this row selects. Compared to the group's `value` by identity. */
|
|
8
|
+
value: T;
|
|
9
|
+
/** The row's visible label. */
|
|
10
|
+
label: JSX.Element;
|
|
11
|
+
/** Optional second line under the label. */
|
|
12
|
+
description?: JSX.Element;
|
|
13
|
+
/** Disable this row alone (the group's `disabled` disables all of them). */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CheckboxGroupProps<T = string>
|
|
18
|
+
extends Omit<JSX.HTMLAttributes<HTMLDivElement>, 'onChange' | 'children'> {
|
|
19
|
+
/**
|
|
20
|
+
* The choices, in display order. Rendered in full: the kit never truncates,
|
|
21
|
+
* re-orders or de-duplicates a consumer's option list.
|
|
22
|
+
*/
|
|
23
|
+
options: readonly CheckboxOption<T>[];
|
|
24
|
+
/** The selected values. Each option is checked when its `value` is in here (by identity). */
|
|
25
|
+
value?: readonly T[];
|
|
26
|
+
/**
|
|
27
|
+
* The shared form-control name every box in the group carries, so a native form
|
|
28
|
+
* submits the whole selection under one key and `FormData.getAll(name)` reads it back.
|
|
29
|
+
*
|
|
30
|
+
* UNLIKE {@link RadioGroup}, this has no default. A radio group needs a shared `name`
|
|
31
|
+
* for the browser to make the set exclusive and arrow-navigable, so one is generated
|
|
32
|
+
* when none is given; checkboxes are independent controls and need nothing to behave
|
|
33
|
+
* correctly. Generating a name here would submit the selection under a random key,
|
|
34
|
+
* which is worse than submitting nothing.
|
|
35
|
+
*/
|
|
36
|
+
name?: string;
|
|
37
|
+
/** Disable every row. */
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
/** Accessible name for the group. Rendered as `aria-label` on the `group`. */
|
|
40
|
+
label?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Fires on every toggle with the NEXT selection, plus the option that moved and
|
|
43
|
+
* which way it went. The next selection is a fresh array — the group holds no state
|
|
44
|
+
* of its own, so `value` is yours to own.
|
|
45
|
+
*/
|
|
46
|
+
onChange?: (value: T[], option: CheckboxOption<T>, checked: boolean) => void;
|
|
47
|
+
/** Fires when a box loses focus — the commit point for a form field. */
|
|
48
|
+
onOptionBlur?: () => void;
|
|
49
|
+
/** Extra classes for each row. */
|
|
50
|
+
itemClass?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Presentation slot. Replaces the default label/description column with whatever
|
|
53
|
+
* you return, so a row can carry media, a badge or a price without a second
|
|
54
|
+
* checkbox component existing. The control, the row chrome and the group semantics
|
|
55
|
+
* stay ours.
|
|
56
|
+
*/
|
|
57
|
+
children?: (option: CheckboxOption<T>, state: { checked: boolean; disabled: boolean }) => JSX.Element;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A vertical set of checkbox rows in a bordered, divided list — the kit's standard
|
|
62
|
+
* "pick any number" control, and {@link RadioGroup}'s sibling: same options shape,
|
|
63
|
+
* same row chrome, same presentation slot, multi-value instead of single.
|
|
64
|
+
*
|
|
65
|
+
* Every row is a real `<input type="checkbox">` inside a `<label>`, so clicking
|
|
66
|
+
* anywhere on the row toggles it and the keyboard behaviour (a tab stop per box,
|
|
67
|
+
* Space to toggle) is the browser's rather than a reimplementation. The wrapper is
|
|
68
|
+
* `role="group"`, not `role="listbox"`: the boxes are independent controls and
|
|
69
|
+
* nothing here overrides what they already announce.
|
|
70
|
+
*
|
|
71
|
+
* Everything not listed in `CheckboxGroupProps` is forwarded to the group element, so
|
|
72
|
+
* `id`, `aria-labelledby`, any other `aria-*` and any `data-*` hook land where a form
|
|
73
|
+
* expects them. No validation is applied — "at least one" is your application's rule,
|
|
74
|
+
* not the kit's.
|
|
75
|
+
*
|
|
76
|
+
* ```tsx
|
|
77
|
+
* <CheckboxGroup
|
|
78
|
+
* label="Environments"
|
|
79
|
+
* name="env"
|
|
80
|
+
* options={[{ value: 'prod', label: 'Production' }, { value: 'staging', label: 'Staging' }]}
|
|
81
|
+
* value={envs()}
|
|
82
|
+
* onChange={setEnvs}
|
|
83
|
+
* />
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function CheckboxGroup<T = string>(props: CheckboxGroupProps<T>): JSX.Element {
|
|
87
|
+
const [local, rest] = splitProps(props, [
|
|
88
|
+
'options', 'value', 'name', 'disabled', 'label', 'onChange', 'onOptionBlur', 'itemClass', 'children', 'class',
|
|
89
|
+
]);
|
|
90
|
+
const selected = (): readonly T[] => local.value ?? [];
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
{...rest}
|
|
94
|
+
role="group"
|
|
95
|
+
aria-label={local.label}
|
|
96
|
+
class={cn('divide-y divide-border overflow-hidden rounded-lg border border-border', local.class)}
|
|
97
|
+
>
|
|
98
|
+
<For each={local.options}>
|
|
99
|
+
{(opt) => {
|
|
100
|
+
const checked = () => selected().includes(opt.value);
|
|
101
|
+
const rowDisabled = () => !!(local.disabled || opt.disabled);
|
|
102
|
+
return (
|
|
103
|
+
<label
|
|
104
|
+
class={cn(
|
|
105
|
+
'flex cursor-pointer items-center gap-3 px-3 py-2.5 text-sm transition-colors',
|
|
106
|
+
opt.description !== undefined && 'items-start',
|
|
107
|
+
checked()
|
|
108
|
+
? 'bg-accent font-medium text-accent-foreground'
|
|
109
|
+
: 'text-foreground hover:bg-muted/50',
|
|
110
|
+
local.itemClass,
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
<Checkbox
|
|
114
|
+
name={local.name}
|
|
115
|
+
value={String(opt.value)}
|
|
116
|
+
checked={checked()}
|
|
117
|
+
disabled={rowDisabled()}
|
|
118
|
+
class={opt.description === undefined ? undefined : 'mt-0.5'}
|
|
119
|
+
onChange={(e) => {
|
|
120
|
+
const on = e.currentTarget.checked;
|
|
121
|
+
const now = selected();
|
|
122
|
+
// Filter-or-append rather than a toggle on the previous array, so the
|
|
123
|
+
// emitted selection follows the CONTROL's state. They diverge when the
|
|
124
|
+
// caller re-renders mid-interaction, and following the box is what a
|
|
125
|
+
// native form would report.
|
|
126
|
+
const next = on ? [...now.filter((v) => v !== opt.value), opt.value] : now.filter((v) => v !== opt.value);
|
|
127
|
+
local.onChange?.(next, opt, on);
|
|
128
|
+
}}
|
|
129
|
+
onBlur={() => local.onOptionBlur?.()}
|
|
130
|
+
/>
|
|
131
|
+
<Show
|
|
132
|
+
when={local.children}
|
|
133
|
+
fallback={
|
|
134
|
+
// No description, a bare <span> — byte for byte what the form's
|
|
135
|
+
// hand-rolled checkbox list rendered before this component existed.
|
|
136
|
+
// The two-line column only appears when there is a second line.
|
|
137
|
+
<Show when={opt.description !== undefined} fallback={<span>{opt.label}</span>}>
|
|
138
|
+
<span class="flex flex-col gap-0.5">
|
|
139
|
+
<span>{opt.label}</span>
|
|
140
|
+
<span class="text-xs font-normal text-muted-foreground">{opt.description}</span>
|
|
141
|
+
</span>
|
|
142
|
+
</Show>
|
|
143
|
+
}
|
|
144
|
+
>
|
|
145
|
+
{(render) => render()(opt, { checked: checked(), disabled: rowDisabled() })}
|
|
146
|
+
</Show>
|
|
147
|
+
</label>
|
|
148
|
+
);
|
|
149
|
+
}}
|
|
150
|
+
</For>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|