@kitn.ai/ui 0.26.0 → 0.27.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 +16 -5
- 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-BnFKqqtY.js} +1 -1
- package/dist/action-icons-CBskfBMz.js +1 -0
- package/dist/arrow-down-Br0qtpsz.js +1 -0
- package/dist/arrow-left-Dj7xgE12.js +1 -0
- package/dist/artifact-CvXEm1lS.js +1 -0
- package/dist/assets/dev-5XzXYR21.js +91 -0
- package/dist/attachments-C43ZegEq.js +1 -0
- package/dist/{elements/chunks/audio-visualizer-D7lB6mvR.js → audio-visualizer-B-79y1Tj.js} +1 -1
- package/dist/badge-BpUbq8c0.js +1 -0
- package/dist/bash-InADTalH.js +1 -6
- package/dist/bell-sn17Ip9v.js +1 -0
- package/dist/{elements/chunks/button-flUbeufF.js → button-BH6rN0U-.js} +1 -1
- package/dist/card-renderer-Cb1gQmth.js +1 -0
- package/dist/card-routing-DifQgS7n.js +1 -0
- package/dist/check-DPeeMUYx.js +1 -0
- package/dist/checkbox-Dc9Yov_B.js +1 -0
- package/dist/checkbox-group-B1kDLLYk.js +1 -0
- package/dist/chevron-down-CHSWk7ZT.js +1 -0
- package/dist/chevron-right-B7l_fMgx.js +1 -0
- package/dist/choice-card-BT3c1S8g.js +1 -0
- package/dist/circle-Bb0yOpqM.js +1 -0
- package/dist/circle-check-BGZpCiz7.js +1 -0
- package/dist/circle-x-wobWkoSx.js +1 -0
- package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
- package/dist/{elements/chunks/code-block-BdLiTitd.js → code-block-CUQ5mtv0.js} +1 -1
- package/dist/{elements/chunks/collapsible-BzbXpPzr.js → collapsible-CPJbf6FM.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 +46 -1
- package/dist/components/conversation-item.d.ts +19 -7
- package/dist/components/conversation-list.d.ts +17 -5
- package/dist/components/form-widgets.d.ts +67 -2
- package/dist/components/form.d.ts +48 -1
- 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/composer-CjngLzal.js +106 -0
- package/dist/confirm-card-DCx_A4bU.js +1 -0
- package/dist/construct-cli.es.js +1169 -0
- package/dist/{elements/chunks/context-VLIL3sJ-.js → context-BY8V-Nlp.js} +1 -1
- package/dist/controllable-D43BKmtX.js +1 -0
- package/dist/conversation-list-D4lLU_yS.js +1 -0
- package/dist/copy-B6DO4PqB.js +1 -0
- package/dist/core-AYMC6_lb.js +12 -5874
- package/dist/{create-tween-CQED4T0g.js → create-tween-BPzaefTp.js} +1 -1
- package/dist/{create-tween-BFT6c-Y8.js → create-tween-CnMHviKB.js} +1 -1
- package/dist/{create-tween-CJNlzoaO.js → create-tween-D1aiGZZh.js} +1 -1
- package/dist/{elements/chunks/create-tween-c4Z8TDYz.js → create-tween-DUSQUKpr.js} +2 -2
- package/dist/css-M7EaDHN_.js +1 -6
- package/dist/custom-elements.json +1400 -218
- package/dist/default-input-CucQEOqH.js +1 -0
- package/dist/define-CGGX-7Vg.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-CbLtajUQ.js} +1 -1
- package/dist/{elements/chunks/dropdown-BgWTthUx.js → dropdown-BwhViJFd.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 +101 -7
- 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 +362 -24
- package/dist/{elements/chunks/ellipsis-7gU33mL2.js → ellipsis-DFr4Vqr3.js} +1 -1
- package/dist/embed-5s_vU4J0.js +1 -0
- package/dist/engine-javascript-vq0WuIJl.js +14 -2516
- package/dist/{elements/chunks/external-link-CSOEsNlx.js → external-link-u9d85ORQ.js} +1 -1
- package/dist/file-text-CX7_x42o.js +1 -0
- package/dist/{elements/chunks/file-tree-zvB2i7P9.js → file-tree-D3kBjXI5.js} +1 -1
- package/dist/folder-V-YdIL8f.js +1 -0
- package/dist/form-BRNUMCN2.js +1 -0
- package/dist/github-dark-dimmed-DUshB20C.js +1 -4
- package/dist/github-light-JYsPkUQd.js +1 -4
- package/dist/hover-card-DaeiYrTA.js +1 -0
- package/dist/html-CPZ3oZQ7.js +1 -10
- package/dist/icon-BpixdZNq.js +1 -0
- package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7330 -5852
- package/dist/index.server.js +5823 -4759
- package/dist/info-8mFsIjS3.js +1 -0
- package/dist/input-d1Yuu-hz.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-961zMPFB.js +1 -0
- package/dist/link-DlZZBGaR.js +1 -0
- package/dist/{elements/chunks/link-preview-OQ9IEq0f.js → link-preview-DIIbV2Sd.js} +1 -1
- package/dist/loader-YQszHFaW.js +1 -0
- package/dist/{elements/chunks/markdown-DgBFaGAz.js → markdown-CVk1MPsj.js} +1 -1
- package/dist/mcp.es.js +1285 -79
- package/dist/message-CwKVEdZg.js +1 -0
- package/dist/message-DS8lzHc1.js +1 -0
- package/dist/message-circle-Xf8Q-vZp.js +1 -0
- package/dist/message-square-Cxnwgkns.js +1 -0
- package/dist/{elements/chunks/minimize-2-DPIUKTrT.js → minimize-2-UEZmP2QT.js} +1 -1
- package/dist/{elements/chunks/model-switcher-DsT8tFB0.js → model-switcher-Kk1jEQ5t.js} +1 -1
- package/dist/{elements/chunks/overlay-DuiwdXjm.js → overlay-C1od6LOz.js} +1 -1
- package/dist/panel-right-ryET0yyn.js +1 -0
- package/dist/paperclip-BDNoZFJP.js +1 -0
- package/dist/play-OFIvYXUQ.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/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-BvYX0aoy.js +1 -0
- package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
- package/dist/radio-DcL23cNJ.js +1 -0
- package/dist/react/index.d.ts +193 -19
- package/dist/react.js +216 -180
- package/dist/reasoning-Cackq4Rh.js +1 -0
- package/dist/register-impl-C7rENvPK.js +424 -0
- package/dist/{elements/chunks/resizable-D181b4fM.js → resizable-Cp9HUYih.js} +1 -1
- package/dist/rotate-ccw-0DLRDHH5.js +1 -0
- package/dist/rotate-cw-DOB9IGcU.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-emVmWP4K.js +1 -0
- package/dist/scroll-button-CeP_gv1x.js +1 -0
- package/dist/select-CWmu00tS.js +1 -0
- package/dist/{elements/chunks/separator-CeJHQqLW.js → separator-CE6vly6z.js} +1 -1
- package/dist/{elements/chunks/settings-CqaHgJF9.js → settings-CE-78SvZ.js} +1 -1
- package/dist/{elements/chunks/settings-group-CjqjLCOn.js → settings-group-ZFWNxFQR.js} +1 -1
- package/dist/{elements/chunks/skeleton-D8akBnKS.js → skeleton-BebdP4mf.js} +1 -1
- package/dist/slider-BAKRYUlz.js +1 -0
- package/dist/{solid-BtdqNZI7.js → solid-BE5ui6sy.js} +8727 -7212
- package/dist/{solid-CnavwBye.js → solid-DFhsce3O.js} +7015 -5886
- package/dist/solid.d.ts +10 -0
- package/dist/solid.js +240 -234
- package/dist/solid.server.js +240 -234
- package/dist/source-B0FlguSx.js +1 -0
- package/dist/star-CBMZvLuN.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/{elements/chunks/store-Cjq0qwYN.js → store-7eoadydQ.js} +1 -1
- package/dist/svelte--5p79yCD.js +1 -15
- package/dist/switch-CViARZCy.js +1 -0
- package/dist/tasks-card-MjCcOhR_.js +1 -0
- package/dist/text-shimmer-DKlwRF2G.js +1 -0
- package/dist/textarea-3ay2Me2-.js +1 -0
- package/dist/theme.tokens.css +82 -13
- package/dist/{elements/chunks/thumbs-up-DX2Ijk3H.js → thumbs-up-DQcu2HMO.js} +1 -1
- package/dist/toast-store-C6yKOhbk.js +1 -0
- package/dist/{elements/chunks/tool-DKQ2D1DP.js → tool-BCYNlfKy.js} +1 -1
- package/dist/{elements/chunks/tooltip-B0G99y8w.js → tooltip-05zI4hjs.js} +1 -1
- package/dist/trash-2-BR1RxGky.js +1 -0
- package/dist/{elements/chunks/triangle-alert-F9cyUEyy.js → triangle-alert-DHohDOCy.js} +1 -1
- package/dist/tsx-B8rCNbgL.js +1 -6
- 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 +22 -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-BbGW6A_u.js +1 -0
- package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
- package/dist/use-card-resolution-CJW4grFa.js +1 -0
- package/dist/{variant-aurora-B8II3-I1.js → variant-aurora-BUOVgPOB.js} +2 -2
- package/dist/{variant-aurora-CMNWOSJR.js → variant-aurora-CUsUJvFI.js} +2 -2
- package/dist/{variant-aurora-CcuDMWwQ.js → variant-aurora-CW2i4bfr.js} +1 -1
- package/dist/{elements/chunks/variant-aurora-qt6Zct8M.js → variant-aurora-xiMPty8f.js} +1 -1
- package/dist/{variant-custom-L16JLLZy.js → variant-custom-BQtOnmeO.js} +1 -1
- package/dist/{variant-custom-C714mqKl.js → variant-custom-CAm9L-E5.js} +2 -2
- package/dist/{variant-custom-BdvYC7af.js → variant-custom-CAnzzppW.js} +2 -2
- package/dist/{elements/chunks/variant-custom-tJp22_BQ.js → variant-custom-D91HjAKz.js} +1 -1
- package/dist/{variant-wave-ecwykeyL.js → variant-wave-Ca1Ls4_y.js} +2 -2
- package/dist/{variant-wave-D7ruiA1W.js → variant-wave-CxX39DKd.js} +2 -2
- package/dist/{elements/chunks/variant-wave-CaQ5ti39.js → variant-wave-DYyzIIZv.js} +1 -1
- package/dist/{variant-wave-2S4La2Ka.js → variant-wave-uY5crU_K.js} +1 -1
- package/dist/vue-BmIZj4XD.js +1 -33
- package/dist/x-C5m1JFgi.js +1 -0
- package/frameworks/react/index.tsx +198 -20
- package/llms-full.txt +1178 -21
- package/llms.txt +6 -3
- package/package.json +17 -4
- package/src/agent-tooling/catalog/invariants.ts +1 -1
- 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 +131 -0
- package/src/agent-tooling/construct/codegen.ts +1353 -0
- package/src/agent-tooling/construct/construct.v1.schema.json +250 -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 +23 -0
- package/src/agent-tooling/construct/schema.ts +371 -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 +72 -9
- 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 +33 -9
- package/src/components/conversation-list.tsx +25 -10
- 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/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/elements/attachments.tsx +3 -1
- package/src/elements/chat-workspace.tsx +1 -1
- package/src/elements/chat.tsx +22 -4
- 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 +736 -46
- package/src/elements/element-nonscalar.json +15 -0
- package/src/elements/element-types.d.ts +362 -24
- 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 +2 -1
- 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/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 +10 -0
- package/src/state/index.ts +1 -1
- package/src/state/mock.ts +92 -6
- 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 +145 -6
- 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 +84 -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/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/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
|
@@ -1,25 +1,64 @@
|
|
|
1
|
-
import { type JSX, For, Show, createSignal } from 'solid-js';
|
|
1
|
+
import { type JSX, For, Show, createMemo, createSignal } from 'solid-js';
|
|
2
2
|
import { cn } from '../utils/cn';
|
|
3
3
|
import { Textarea } from '../ui/textarea';
|
|
4
4
|
import { Input, FIELD_BASE as inputBase } from '../ui/input';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
5
|
+
import { Slider } from '../ui/slider';
|
|
6
|
+
import { Select } from '../ui/select';
|
|
7
|
+
import { Switch } from '../ui/switch';
|
|
8
|
+
import { Checkbox } from '../ui/checkbox';
|
|
9
|
+
import { RadioGroup } from '../ui/radio';
|
|
10
|
+
import { CheckboxGroup } from '../ui/checkbox-group';
|
|
11
|
+
import { Button } from '../ui/button';
|
|
12
|
+
import { Star, X } from 'lucide-solid';
|
|
13
|
+
import type { FieldMaskHint, FormField } from './form';
|
|
7
14
|
|
|
8
15
|
/** The shared prop shape every leaf widget receives from FieldRow. */
|
|
9
16
|
export interface WidgetProps {
|
|
10
17
|
id: string;
|
|
11
18
|
value: unknown;
|
|
12
19
|
field: FormField;
|
|
20
|
+
/**
|
|
21
|
+
* The field's resolved format hints, or an empty resolution.
|
|
22
|
+
*
|
|
23
|
+
* RESOLVED IN `FieldRow`, not here. The row already has to resolve them to render
|
|
24
|
+
* the format hint text and put its id in `aria-describedby`, and resolving the same
|
|
25
|
+
* untrusted `x-kai-*` keys a second time in the widget would warn twice about the
|
|
26
|
+
* same bad hint and could disagree with what the row said out loud.
|
|
27
|
+
*/
|
|
28
|
+
mask?: FieldMaskHint;
|
|
13
29
|
disabled: boolean;
|
|
14
30
|
placeholder?: string;
|
|
15
31
|
required: boolean;
|
|
16
32
|
invalid: boolean;
|
|
17
33
|
describedBy?: string;
|
|
18
34
|
label: string;
|
|
35
|
+
/**
|
|
36
|
+
* The id of the row's VISIBLE label element, for widgets whose control is a group
|
|
37
|
+
* rather than one labelable input.
|
|
38
|
+
*
|
|
39
|
+
* `FieldRow` used to suppress its `<label>` for those kinds and each widget named
|
|
40
|
+
* itself with `aria-label={props.label}`, which is a name only a screen reader can
|
|
41
|
+
* reach: "Severity", "Environments" and "Tags" were announced and invisible. The row
|
|
42
|
+
* now always renders the text and hands its id down here, so one string names the
|
|
43
|
+
* group and everybody can see it. Absent means the widget is on its own (a bare
|
|
44
|
+
* widget rendered outside `FieldRow`), so `aria-label` stays the fallback.
|
|
45
|
+
*/
|
|
46
|
+
labelledBy?: string;
|
|
19
47
|
onInput: (value: unknown) => void;
|
|
20
48
|
onBlur: () => void;
|
|
21
49
|
}
|
|
22
50
|
|
|
51
|
+
/**
|
|
52
|
+
* How a GROUP widget names itself: the row's visible label when there is one,
|
|
53
|
+
* `aria-label` only as the fallback. Never both — two accessible names on one element
|
|
54
|
+
* is one too many, and `aria-labelledby` would win silently anyway.
|
|
55
|
+
*/
|
|
56
|
+
function groupNameProps(p: WidgetProps): { 'aria-labelledby'?: string; 'aria-label'?: string } {
|
|
57
|
+
return p.labelledBy !== undefined
|
|
58
|
+
? { 'aria-labelledby': p.labelledBy }
|
|
59
|
+
: { 'aria-label': p.label };
|
|
60
|
+
}
|
|
61
|
+
|
|
23
62
|
function ariaProps(p: WidgetProps) {
|
|
24
63
|
return {
|
|
25
64
|
'aria-required': p.required || undefined,
|
|
@@ -65,6 +104,16 @@ export function TextWidget(
|
|
|
65
104
|
disabled={props.disabled}
|
|
66
105
|
minLength={props.field.minLength}
|
|
67
106
|
maxLength={props.field.maxLength}
|
|
107
|
+
// Masking (spec §7.3). Each is `undefined` for a field with no format hints, so
|
|
108
|
+
// an unhinted field renders exactly the input it rendered before. The hint TEXT
|
|
109
|
+
// is deliberately not passed as `Input`'s own `hint`: `FieldRow` renders it and
|
|
110
|
+
// owns the `aria-describedby` chain, and `Input`'s hint would mint a second one
|
|
111
|
+
// (spec §6). The submitted value is the CANONICAL one — `Input` emits canonical
|
|
112
|
+
// through `onValueInput` whenever a mask is active (spec §4), which is what
|
|
113
|
+
// makes "exactly one value per field" true without this widget choosing.
|
|
114
|
+
format={props.mask?.format}
|
|
115
|
+
guide={props.mask?.guide}
|
|
116
|
+
semantic={props.mask?.semantic}
|
|
68
117
|
{...ariaProps(props)}
|
|
69
118
|
onValueInput={(value) => props.onInput(value)}
|
|
70
119
|
onValueChange={() => props.onBlur()}
|
|
@@ -99,94 +148,135 @@ export function TextareaWidget(props: WidgetProps): JSX.Element {
|
|
|
99
148
|
|
|
100
149
|
export function NumberWidget(props: WidgetProps): JSX.Element {
|
|
101
150
|
const step = () => props.field['x-kai-step'] ?? (props.field.type === 'integer' ? 1 : undefined);
|
|
151
|
+
// `Input`, not a raw `<input class={inputBase}>` (plan step 6). The two render the
|
|
152
|
+
// same box — `FIELD_BASE` is the former `inputBase` — so nothing is visible. What the
|
|
153
|
+
// bypass lost was the focus-node-reuse fix documented at `ui/input.tsx:262-268`: with
|
|
154
|
+
// the class computed at the CALL SITE, `props.invalid` changing rebuilt the `<input>`
|
|
155
|
+
// node and took focus and caret with it, and `kai-form` derives `invalid` from the
|
|
156
|
+
// field's own value. `min`/`max`/`step` come from the schema and are forwarded
|
|
157
|
+
// untouched; no bound is invented here.
|
|
158
|
+
//
|
|
159
|
+
// No `format`/`guide`/`semantic`: masking is a text-field affordance and a
|
|
160
|
+
// `type="number"` field has no mask hints to resolve.
|
|
102
161
|
return (
|
|
103
|
-
<
|
|
162
|
+
<Input
|
|
104
163
|
id={props.id}
|
|
105
164
|
data-control
|
|
106
165
|
type="number"
|
|
107
|
-
class={cn(inputBase, props.invalid && 'border-destructive dark:border-red-400/70')}
|
|
108
166
|
value={props.value === undefined || props.value === null ? '' : String(props.value)}
|
|
109
167
|
placeholder={props.placeholder}
|
|
168
|
+
invalid={props.invalid}
|
|
110
169
|
disabled={props.disabled}
|
|
111
170
|
min={props.field.minimum}
|
|
112
171
|
max={props.field.maximum}
|
|
113
172
|
step={step()}
|
|
114
173
|
{...ariaProps(props)}
|
|
115
|
-
|
|
116
|
-
|
|
174
|
+
onValueInput={(value) => props.onInput(value)}
|
|
175
|
+
onValueChange={() => props.onBlur()}
|
|
117
176
|
/>
|
|
118
177
|
);
|
|
119
178
|
}
|
|
120
179
|
|
|
121
180
|
export function SliderWidget(props: WidgetProps): JSX.Element {
|
|
181
|
+
// The 0..100 fallback is the WIDGET's, not the primitive's (plan §4). This widget is
|
|
182
|
+
// reading a consumer-authored JSON-Schema field where `minimum`/`maximum` are
|
|
183
|
+
// optional; `Slider` itself requires both, so no other caller inherits this guess.
|
|
122
184
|
const min = () => props.field.minimum ?? 0;
|
|
123
185
|
const max = () => props.field.maximum ?? 100;
|
|
124
186
|
const step = () => props.field['x-kai-step'] ?? (props.field.type === 'integer' ? 1 : undefined);
|
|
125
187
|
const current = () => (props.value === undefined || props.value === null ? min() : Number(props.value));
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
};
|
|
188
|
+
// The value bubble and the row that holds it BOTH live in the primitive now
|
|
189
|
+
// (`valueLabel`), so this widget no longer hand-builds either. Same markup, same
|
|
190
|
+
// classes, one owner, and the readout picked up an `aria-hidden` it did not have
|
|
191
|
+
// here: the slider already announces the number through `aria-valuetext` below.
|
|
131
192
|
return (
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
onInput={(e) => props.onInput(Number(e.currentTarget.value))}
|
|
147
|
-
onBlur={props.onBlur}
|
|
148
|
-
/>
|
|
149
|
-
<span class="min-w-9 shrink-0 rounded-md bg-background px-2 py-1 text-center text-sm font-medium tabular-nums text-foreground shadow-sm">
|
|
150
|
-
{current()}
|
|
151
|
-
</span>
|
|
152
|
-
</div>
|
|
193
|
+
<Slider
|
|
194
|
+
id={props.id}
|
|
195
|
+
data-control
|
|
196
|
+
value={current()}
|
|
197
|
+
min={min()}
|
|
198
|
+
max={max()}
|
|
199
|
+
step={step()}
|
|
200
|
+
disabled={props.disabled}
|
|
201
|
+
valueLabel
|
|
202
|
+
aria-valuetext={String(current())}
|
|
203
|
+
{...ariaProps(props)}
|
|
204
|
+
onInput={(e) => props.onInput(Number(e.currentTarget.value))}
|
|
205
|
+
onBlur={props.onBlur}
|
|
206
|
+
/>
|
|
153
207
|
);
|
|
154
208
|
}
|
|
155
209
|
|
|
156
210
|
export function RatingWidget(props: WidgetProps): JSX.Element {
|
|
157
211
|
const max = () => props.field.maximum ?? 5;
|
|
212
|
+
const min = () => props.field.minimum ?? 1;
|
|
158
213
|
const current = () => Number(props.value ?? 0);
|
|
159
214
|
const stars = () => Array.from({ length: max() }, (_, i) => i + 1);
|
|
215
|
+
/**
|
|
216
|
+
* Roving tabindex. Exactly one `role="radio"` is in the tab order — the selected
|
|
217
|
+
* star, or the first one while nothing is selected. The GROUP is deliberately not
|
|
218
|
+
* a tab stop: it used to be (`tabindex={0}` with every radio at `-1`), and the
|
|
219
|
+
* consequence was that arrow keys changed the value while focus never left the
|
|
220
|
+
* group, so the focus ring sat on the whole row and a screen reader announced the
|
|
221
|
+
* radiogroup instead of "3 stars, selected". Roving tabindex is the pattern the
|
|
222
|
+
* ARIA radiogroup spec asks for and it makes both symptoms go away at once.
|
|
223
|
+
*/
|
|
224
|
+
const activeStar = () => (current() >= min() && current() <= max() ? current() : min());
|
|
225
|
+
const refs = new Map<number, HTMLButtonElement>();
|
|
226
|
+
/** Set the value AND move DOM focus to the star that now owns it. */
|
|
227
|
+
const select = (n: number): void => {
|
|
228
|
+
const next = Math.min(max(), Math.max(min(), n));
|
|
229
|
+
props.onInput(next);
|
|
230
|
+
refs.get(next)?.focus();
|
|
231
|
+
};
|
|
160
232
|
const onKey = (e: KeyboardEvent): void => {
|
|
233
|
+
if (props.disabled) return;
|
|
161
234
|
if (e.key === 'ArrowRight' || e.key === 'ArrowUp') {
|
|
162
235
|
e.preventDefault();
|
|
163
|
-
|
|
236
|
+
select(current() + 1);
|
|
164
237
|
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') {
|
|
165
238
|
e.preventDefault();
|
|
166
|
-
|
|
239
|
+
select(current() - 1);
|
|
240
|
+
} else if (e.key === 'Home') {
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
select(min());
|
|
243
|
+
} else if (e.key === 'End') {
|
|
244
|
+
e.preventDefault();
|
|
245
|
+
select(max());
|
|
167
246
|
}
|
|
168
247
|
};
|
|
169
248
|
return (
|
|
249
|
+
// `groupNameProps`: the row renders a visible "Rating" label whose `for` had
|
|
250
|
+
// nothing to point at (there is no single labelable control here, only stars).
|
|
251
|
+
// Pointing the group at that same visible text makes ONE string name the control.
|
|
170
252
|
<div
|
|
171
253
|
role="radiogroup"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
tabindex={0}
|
|
175
|
-
class="flex items-center gap-1 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
254
|
+
{...groupNameProps(props)}
|
|
255
|
+
class="flex items-center gap-1 rounded-md"
|
|
176
256
|
{...ariaProps(props)}
|
|
177
|
-
onKeyDown={onKey}
|
|
178
257
|
>
|
|
179
258
|
<For each={stars()}>
|
|
180
259
|
{(n) => (
|
|
181
260
|
<button
|
|
182
261
|
type="button"
|
|
183
262
|
role="radio"
|
|
263
|
+
// `data-control` rides the roving tab stop, so `form.focusField()` —
|
|
264
|
+
// which focuses the single `[data-control]` inside the field — lands on
|
|
265
|
+
// the same star Tab would reach. It used to sit on the group, which is no
|
|
266
|
+
// longer focusable. No `id={props.id}`: the row's `<label for>` had
|
|
267
|
+
// nothing to point at before either, and pointing it at a star would make
|
|
268
|
+
// clicking the field label set the rating to 1. The group is named by the
|
|
269
|
+
// row's visible label (see `groupNameProps` above), which is what names it.
|
|
270
|
+
data-control={activeStar() === n ? '' : undefined}
|
|
184
271
|
aria-checked={current() === n}
|
|
185
272
|
aria-label={`${n} ${n === 1 ? 'star' : 'stars'}`}
|
|
186
|
-
tabindex={-1}
|
|
273
|
+
tabindex={activeStar() === n ? 0 : -1}
|
|
187
274
|
disabled={props.disabled}
|
|
188
|
-
|
|
189
|
-
|
|
275
|
+
ref={(el) => refs.set(n, el)}
|
|
276
|
+
class="rounded p-0.5 text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
277
|
+
onKeyDown={onKey}
|
|
278
|
+
onClick={() => select(n)}
|
|
279
|
+
onBlur={props.onBlur}
|
|
190
280
|
>
|
|
191
281
|
<Star
|
|
192
282
|
size={20}
|
|
@@ -200,47 +290,48 @@ export function RatingWidget(props: WidgetProps): JSX.Element {
|
|
|
200
290
|
);
|
|
201
291
|
}
|
|
202
292
|
|
|
293
|
+
/**
|
|
294
|
+
* The boolean field's switch IS `ui/switch.tsx` — it is not a lookalike.
|
|
295
|
+
*
|
|
296
|
+
* This used to hand-roll its own `<button role="switch">` at 44×24 while
|
|
297
|
+
* `<kai-switch>` shipped the same control at 36×20, so a consumer putting the two
|
|
298
|
+
* side by side saw two different switches; and its thumb used `bg-background`,
|
|
299
|
+
* re-introducing in the copy the dark-mode disappearing-thumb bug that
|
|
300
|
+
* `ui/switch.tsx:80` carries a comment about having fixed. Delegating removes both.
|
|
301
|
+
* The size convergence (44×24 → 36×20) is visible inside `kai-form` and intended.
|
|
302
|
+
*
|
|
303
|
+
* The four form-only hooks — `id`, `data-control` and the `aria-required` /
|
|
304
|
+
* `aria-invalid` / `aria-describedby` trio — are ordinary props now (plan decision
|
|
305
|
+
* D-7). They used to be stamped onto the button through `buttonRef` + a
|
|
306
|
+
* `createEffect`, which worked but meant this widget reached into the primitive's
|
|
307
|
+
* DOM to make `form.focusField()` land. `Switch` forwards anything it does not own
|
|
308
|
+
* to its inner button, exactly like `Checkbox` and `Radio` forward to their inputs,
|
|
309
|
+
* so all four ride the same path every other widget's hooks do.
|
|
310
|
+
*/
|
|
203
311
|
export function SwitchWidget(props: WidgetProps): JSX.Element {
|
|
204
312
|
const on = () => props.value === true;
|
|
205
|
-
const toggle = (): void => {
|
|
206
|
-
if (props.disabled) return;
|
|
207
|
-
props.onInput(!on());
|
|
208
|
-
props.onBlur();
|
|
209
|
-
};
|
|
210
313
|
return (
|
|
211
|
-
<
|
|
314
|
+
<Switch
|
|
212
315
|
id={props.id}
|
|
213
|
-
data-control
|
|
214
|
-
|
|
215
|
-
role="switch"
|
|
216
|
-
aria-checked={on()}
|
|
217
|
-
aria-label={props.label}
|
|
316
|
+
data-control=""
|
|
317
|
+
checked={on()}
|
|
218
318
|
disabled={props.disabled}
|
|
219
|
-
|
|
220
|
-
'relative inline-flex h-6 w-11 shrink-0 items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
|
|
221
|
-
on() ? 'bg-[var(--color-primary)]' : 'bg-muted',
|
|
222
|
-
)}
|
|
319
|
+
label={props.label}
|
|
223
320
|
{...ariaProps(props)}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
on() ? 'translate-x-5' : 'translate-x-0.5',
|
|
230
|
-
)}
|
|
231
|
-
/>
|
|
232
|
-
</button>
|
|
321
|
+
onChange={(next) => {
|
|
322
|
+
props.onInput(next);
|
|
323
|
+
props.onBlur();
|
|
324
|
+
}}
|
|
325
|
+
/>
|
|
233
326
|
);
|
|
234
327
|
}
|
|
235
328
|
|
|
236
329
|
export function CheckboxWidget(props: WidgetProps): JSX.Element {
|
|
237
330
|
return (
|
|
238
331
|
<label class="-mx-1.5 inline-flex cursor-pointer items-center gap-2.5 rounded-md px-1.5 py-1.5 text-sm text-foreground transition-colors hover:bg-muted/60">
|
|
239
|
-
<
|
|
332
|
+
<Checkbox
|
|
240
333
|
id={props.id}
|
|
241
|
-
data-control
|
|
242
|
-
type="checkbox"
|
|
243
|
-
class="kai-checkbox"
|
|
334
|
+
data-control=""
|
|
244
335
|
checked={props.value === true}
|
|
245
336
|
disabled={props.disabled}
|
|
246
337
|
{...ariaProps(props)}
|
|
@@ -255,70 +346,49 @@ export function CheckboxWidget(props: WidgetProps): JSX.Element {
|
|
|
255
346
|
}
|
|
256
347
|
|
|
257
348
|
export function RadioGroupWidget(props: WidgetProps): JSX.Element {
|
|
258
|
-
|
|
349
|
+
// The row chrome, the `radiogroup` wrapper and the shared `name` all live in the
|
|
350
|
+
// primitive now; this widget only turns a JSON-Schema `enum` into options.
|
|
351
|
+
const options = createMemo(() =>
|
|
352
|
+
((props.field.enum ?? []) as unknown[]).map((opt) => ({ value: opt, label: String(opt) })),
|
|
353
|
+
);
|
|
259
354
|
return (
|
|
260
|
-
<
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
355
|
+
<RadioGroup<unknown>
|
|
356
|
+
data-control=""
|
|
357
|
+
{...groupNameProps(props)}
|
|
358
|
+
name={props.id}
|
|
359
|
+
options={options()}
|
|
360
|
+
value={props.value}
|
|
361
|
+
disabled={props.disabled}
|
|
265
362
|
{...ariaProps(props)}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
'flex cursor-pointer items-center gap-3 px-3 py-2.5 text-sm transition-colors',
|
|
272
|
-
props.value === opt
|
|
273
|
-
? 'bg-accent font-medium text-accent-foreground'
|
|
274
|
-
: 'text-foreground hover:bg-muted/50',
|
|
275
|
-
)}
|
|
276
|
-
>
|
|
277
|
-
<input
|
|
278
|
-
type="radio"
|
|
279
|
-
name={props.id}
|
|
280
|
-
class="kai-radio"
|
|
281
|
-
value={String(opt)}
|
|
282
|
-
checked={props.value === opt}
|
|
283
|
-
disabled={props.disabled}
|
|
284
|
-
onChange={() => {
|
|
285
|
-
props.onInput(opt);
|
|
286
|
-
props.onBlur();
|
|
287
|
-
}}
|
|
288
|
-
/>
|
|
289
|
-
<span>{String(opt)}</span>
|
|
290
|
-
</label>
|
|
291
|
-
)}
|
|
292
|
-
</For>
|
|
293
|
-
</div>
|
|
363
|
+
onChange={(value) => {
|
|
364
|
+
props.onInput(value);
|
|
365
|
+
props.onBlur();
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
294
368
|
);
|
|
295
369
|
}
|
|
296
370
|
|
|
297
371
|
export function SelectWidget(props: WidgetProps): JSX.Element {
|
|
298
|
-
|
|
372
|
+
// The primitive renders no placeholder row unless it is given one; the 'Select…'
|
|
373
|
+
// wording is this widget's, because a schema-driven field has nowhere else to get it.
|
|
374
|
+
const options = createMemo(() =>
|
|
375
|
+
((props.field.enum ?? []) as unknown[]).map((opt) => ({ value: opt, label: String(opt) })),
|
|
376
|
+
);
|
|
299
377
|
return (
|
|
300
|
-
<
|
|
378
|
+
<Select<unknown>
|
|
301
379
|
id={props.id}
|
|
302
380
|
data-control
|
|
303
|
-
|
|
381
|
+
options={options()}
|
|
382
|
+
value={props.value}
|
|
383
|
+
placeholder={props.placeholder ?? 'Select…'}
|
|
384
|
+
invalid={props.invalid}
|
|
304
385
|
disabled={props.disabled}
|
|
305
386
|
{...ariaProps(props)}
|
|
306
387
|
onChange={(e) => {
|
|
307
388
|
props.onInput(e.currentTarget.value);
|
|
308
389
|
props.onBlur();
|
|
309
390
|
}}
|
|
310
|
-
|
|
311
|
-
<option value="" disabled selected={props.value === undefined || props.value === ''}>
|
|
312
|
-
{props.placeholder ?? 'Select…'}
|
|
313
|
-
</option>
|
|
314
|
-
<For each={options()}>
|
|
315
|
-
{(opt) => (
|
|
316
|
-
<option value={String(opt)} selected={props.value === opt}>
|
|
317
|
-
{String(opt)}
|
|
318
|
-
</option>
|
|
319
|
-
)}
|
|
320
|
-
</For>
|
|
321
|
-
</select>
|
|
391
|
+
/>
|
|
322
392
|
);
|
|
323
393
|
}
|
|
324
394
|
|
|
@@ -328,81 +398,67 @@ function itemEnum(field: FormField): unknown[] {
|
|
|
328
398
|
return [];
|
|
329
399
|
}
|
|
330
400
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
data-control
|
|
344
|
-
class="divide-y divide-border overflow-hidden rounded-lg border border-input"
|
|
345
|
-
{...ariaProps(props)}
|
|
346
|
-
>
|
|
347
|
-
<For each={itemEnum(props.field)}>
|
|
348
|
-
{(opt) => (
|
|
349
|
-
<label
|
|
350
|
-
class={cn(
|
|
351
|
-
'flex cursor-pointer items-center gap-3 px-3 py-2.5 text-sm transition-colors',
|
|
352
|
-
selected().includes(opt)
|
|
353
|
-
? 'bg-accent font-medium text-accent-foreground'
|
|
354
|
-
: 'text-foreground hover:bg-muted/50',
|
|
355
|
-
)}
|
|
356
|
-
>
|
|
357
|
-
<input
|
|
358
|
-
type="checkbox"
|
|
359
|
-
class="kai-checkbox"
|
|
360
|
-
checked={selected().includes(opt)}
|
|
361
|
-
disabled={props.disabled}
|
|
362
|
-
onChange={() => toggle(opt)}
|
|
363
|
-
/>
|
|
364
|
-
<span>{String(opt)}</span>
|
|
365
|
-
</label>
|
|
366
|
-
)}
|
|
367
|
-
</For>
|
|
368
|
-
</div>
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
export function MultiSelectWidget(props: WidgetProps): JSX.Element {
|
|
401
|
+
/**
|
|
402
|
+
* An array field whose items are an `enum`, as a list of checkboxes.
|
|
403
|
+
*
|
|
404
|
+
* The bordered/divided row chrome, the `role="group"` wrapper and the rows themselves
|
|
405
|
+
* live in `ui/checkbox-group.tsx` now — this widget only turns a JSON-Schema
|
|
406
|
+
* `items.enum` into options and owns the array in and out. It used to hand-roll the
|
|
407
|
+
* identical chrome beside `RadioGroup`, which already owned it.
|
|
408
|
+
*
|
|
409
|
+
* The emitted array holds the SCHEMA's values, not their string forms, so a numeric
|
|
410
|
+
* or boolean enum survives a round trip.
|
|
411
|
+
*/
|
|
412
|
+
export function CheckboxGroupWidget(props: WidgetProps & { class?: string }): JSX.Element {
|
|
373
413
|
const selected = () => (Array.isArray(props.value) ? (props.value as unknown[]) : []);
|
|
414
|
+
const options = createMemo(() => itemEnum(props.field).map((opt) => ({ value: opt, label: String(opt) })));
|
|
415
|
+
// `name={props.id}`: a shared name so a native form submits the whole selection under
|
|
416
|
+
// one key and `FormData.getAll()` reads it back. `kai-form` collects through its own
|
|
417
|
+
// store rather than a native submit, but the control participating correctly is free
|
|
418
|
+
// here and losing it would be a silent downgrade.
|
|
374
419
|
return (
|
|
375
|
-
<
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
420
|
+
<CheckboxGroup<unknown>
|
|
421
|
+
data-control=""
|
|
422
|
+
{...groupNameProps(props)}
|
|
423
|
+
name={props.id}
|
|
424
|
+
class={props.class}
|
|
425
|
+
options={options()}
|
|
426
|
+
value={selected()}
|
|
381
427
|
disabled={props.disabled}
|
|
382
428
|
{...ariaProps(props)}
|
|
383
|
-
onChange={(
|
|
384
|
-
|
|
385
|
-
props.onInput(vals);
|
|
429
|
+
onChange={(next) => {
|
|
430
|
+
props.onInput(next);
|
|
386
431
|
props.onBlur();
|
|
387
432
|
}}
|
|
388
|
-
|
|
389
|
-
<For each={itemEnum(props.field)}>
|
|
390
|
-
{(opt) => (
|
|
391
|
-
<option value={String(opt)} selected={selected().includes(opt)}>
|
|
392
|
-
{String(opt)}
|
|
393
|
-
</option>
|
|
394
|
-
)}
|
|
395
|
-
</For>
|
|
396
|
-
</select>
|
|
433
|
+
/>
|
|
397
434
|
);
|
|
398
435
|
}
|
|
399
436
|
|
|
437
|
+
/**
|
|
438
|
+
* The LONG version of `checkbox-group`: an array-of-enum field whose option list is
|
|
439
|
+
* over the row's `inlineMax`.
|
|
440
|
+
*
|
|
441
|
+
* This was a `<select multiple>` until decision D-3 was ruled. `<select multiple>` is a
|
|
442
|
+
* poor control on every platform — the multi-select affordance is invisible, discovering
|
|
443
|
+
* it means knowing to ctrl/cmd-click, and there is no touch story at all. It is the same
|
|
444
|
+
* control as `checkbox-group`, so it renders as one, with a scroll cap because the only
|
|
445
|
+
* thing that made a long list bearable in a chat card was the select's fixed-height box.
|
|
446
|
+
*
|
|
447
|
+
* The scroll cap is a presentation decision (how a long list fits), not a limit: every
|
|
448
|
+
* option is rendered, nothing is truncated or dropped.
|
|
449
|
+
*/
|
|
450
|
+
export function MultiSelectWidget(props: WidgetProps): JSX.Element {
|
|
451
|
+
return <CheckboxGroupWidget {...props} class="max-h-60 overflow-y-auto" />;
|
|
452
|
+
}
|
|
453
|
+
|
|
400
454
|
export function TagListWidget(props: WidgetProps): JSX.Element {
|
|
401
455
|
const tags = () => (Array.isArray(props.value) ? (props.value as string[]) : []);
|
|
402
456
|
const [draft, setDraft] = createSignal('');
|
|
403
457
|
const add = (): void => {
|
|
404
458
|
const v = draft().trim();
|
|
405
459
|
if (!v) return;
|
|
460
|
+
// No cap on how many tags may be added. How many is too many lands in a policy
|
|
461
|
+
// document, which makes it the consuming application's call (CLAUDE.md, plan §4).
|
|
406
462
|
props.onInput([...tags(), v]);
|
|
407
463
|
setDraft('');
|
|
408
464
|
props.onBlur();
|
|
@@ -412,36 +468,47 @@ export function TagListWidget(props: WidgetProps): JSX.Element {
|
|
|
412
468
|
props.onBlur();
|
|
413
469
|
};
|
|
414
470
|
return (
|
|
415
|
-
<div class="flex flex-col gap-2" role="group"
|
|
471
|
+
<div class="flex flex-col gap-2" role="group" {...groupNameProps(props)}>
|
|
416
472
|
<div class="flex flex-wrap gap-1.5">
|
|
417
473
|
<For each={tags()}>
|
|
418
474
|
{(tag, i) => (
|
|
419
475
|
<span class="inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-xs text-foreground">
|
|
420
476
|
{tag}
|
|
421
|
-
|
|
477
|
+
{/* `Button`, not a bare `<button>`, and a lucide `X` rather than the
|
|
478
|
+
literal "✕" character it used to render. A bare glyph is what a screen
|
|
479
|
+
reader falls back to when nothing else names the control; the icon is
|
|
480
|
+
`aria-hidden` and the `aria-label` is the only name, so every remove
|
|
481
|
+
control announces which tag it removes. */}
|
|
482
|
+
<Button
|
|
422
483
|
type="button"
|
|
484
|
+
variant="ghost"
|
|
485
|
+
size="icon-sm"
|
|
486
|
+
class="size-4 rounded-full p-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
423
487
|
aria-label={`Remove ${tag}`}
|
|
424
488
|
disabled={props.disabled}
|
|
425
|
-
class="text-muted-foreground hover:text-foreground"
|
|
426
489
|
onClick={() => remove(i())}
|
|
427
490
|
>
|
|
428
|
-
|
|
429
|
-
</
|
|
491
|
+
<X size={12} aria-hidden="true" />
|
|
492
|
+
</Button>
|
|
430
493
|
</span>
|
|
431
494
|
)}
|
|
432
495
|
</For>
|
|
433
496
|
</div>
|
|
434
497
|
<div class="flex items-center gap-2">
|
|
435
|
-
|
|
498
|
+
{/* `Input` rather than a raw `<input class={inputBase}>` (plan step 6). Same box,
|
|
499
|
+
same classes; what the bypass lost was the focus-node-reuse fix at
|
|
500
|
+
`ui/input.tsx:262-268` and the masking `Input` owns. The draft field is not a
|
|
501
|
+
form control of its own — it holds text on its way to becoming a tag — so it
|
|
502
|
+
carries the row's `id` and `data-control` and nothing else. */}
|
|
503
|
+
<Input
|
|
436
504
|
id={props.id}
|
|
437
505
|
data-control
|
|
438
506
|
type="text"
|
|
439
|
-
class={cn(inputBase)}
|
|
440
507
|
value={draft()}
|
|
441
508
|
placeholder={props.placeholder ?? 'Add…'}
|
|
442
509
|
disabled={props.disabled}
|
|
443
510
|
{...ariaProps(props)}
|
|
444
|
-
|
|
511
|
+
onValueInput={(value) => setDraft(value)}
|
|
445
512
|
onKeyDown={(e) => {
|
|
446
513
|
if (e.key === 'Enter') {
|
|
447
514
|
e.preventDefault();
|
|
@@ -449,14 +516,9 @@ export function TagListWidget(props: WidgetProps): JSX.Element {
|
|
|
449
516
|
}
|
|
450
517
|
}}
|
|
451
518
|
/>
|
|
452
|
-
<button
|
|
453
|
-
type="button"
|
|
454
|
-
class="rounded-md border border-input px-3 py-2 text-sm text-foreground hover:bg-muted disabled:opacity-50"
|
|
455
|
-
disabled={props.disabled}
|
|
456
|
-
onClick={add}
|
|
457
|
-
>
|
|
519
|
+
<Button type="button" variant="outline" size="md" disabled={props.disabled} onClick={add}>
|
|
458
520
|
Add
|
|
459
|
-
</
|
|
521
|
+
</Button>
|
|
460
522
|
</div>
|
|
461
523
|
</div>
|
|
462
524
|
);
|