@kitn.ai/ui 0.25.2 → 0.26.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 +2 -2
- package/dist/components/attachment-types.d.ts +46 -1
- package/dist/components/attachments.d.ts +31 -0
- package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
- package/dist/components/audio-visualizer/index.d.ts +15 -1
- package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
- package/dist/components/chat-thread.d.ts +3 -41
- package/dist/components/code-block.d.ts +22 -0
- package/dist/components/conversation-item.d.ts +54 -1
- package/dist/components/conversation-list.d.ts +65 -0
- package/dist/components/form.d.ts +42 -0
- package/dist/components/message.d.ts +7 -0
- package/dist/components/toast.d.ts +2 -1
- package/dist/components/voice-input.d.ts +8 -0
- package/dist/components/voice-output.d.ts +12 -1
- package/dist/components/workspace-shell.d.ts +87 -0
- package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
- package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
- package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
- package/dist/custom-elements.json +2931 -2572
- package/dist/diagnostics/hook.d.ts +81 -0
- package/dist/diagnostics/index.d.ts +7 -0
- package/dist/diagnostics/report-request.d.ts +61 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.js +209 -0
- 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/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/{Icon-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
- package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
- package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
- package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
- package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
- package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
- package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
- package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
- package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
- package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
- package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
- package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
- package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
- package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
- package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
- package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
- package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
- package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
- package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
- package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
- package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
- package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
- package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
- package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
- package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
- package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
- package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
- package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
- package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
- package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
- package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
- package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
- package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
- package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
- package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
- package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
- package/dist/elements/chunks/form-CQirdC4W.js +1 -0
- package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
- package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
- package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
- package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
- package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
- package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
- package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
- package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
- package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
- package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
- package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
- package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
- package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
- package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
- package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
- package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
- package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
- package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
- package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
- package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
- package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
- package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
- package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
- package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
- package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
- package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
- package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
- package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
- package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
- package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
- package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
- package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
- package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
- package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
- package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
- package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
- package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
- package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
- package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
- package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
- package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
- package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
- package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
- package/dist/elements/chunks/x-B6UnOiLw.js +1 -0
- 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.d.ts +1 -0
- package/dist/elements/conversation-item.js +1 -0
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/default-input.d.ts +3 -3
- package/dist/elements/define.d.ts +27 -0
- package/dist/elements/diagnostic-events.d.ts +74 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.d.ts +1 -0
- package/dist/elements/dock.js +149 -0
- package/dist/elements/dropdown.d.ts +1 -0
- package/dist/elements/dropdown.js +1 -0
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-diagnostics.d.ts +81 -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-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/reasoning.js +1 -1
- package/dist/elements/register.d.ts +2 -0
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +3 -3
- 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/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/slots.d.ts +35 -3
- 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 +232 -148
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5456 -4931
- package/dist/index.server.js +4142 -3669
- package/dist/kai-provider.es.js +40 -40
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +184 -69
- package/dist/llms/llms.txt +3 -3
- package/dist/mcp.es.js +2669 -546
- package/dist/primitives/use-resize-observer.d.ts +23 -0
- package/dist/primitives/use-speech-recognition.d.ts +8 -0
- package/dist/primitives/use-text-stream.d.ts +5 -0
- package/dist/react/index.d.ts +115 -266
- package/dist/react.js +247 -221
- package/dist/register-impl-DpE7icIb.js +293 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/tool-defs.d.ts +35 -0
- package/dist/schemas.js +403 -310
- package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
- package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
- package/dist/solid.d.ts +5 -0
- package/dist/solid.js +210 -203
- package/dist/solid.server.js +210 -203
- package/dist/state/index.d.ts +4 -0
- package/dist/state/persistence.d.ts +51 -0
- package/dist/state/stream.d.ts +50 -0
- package/dist/state/threads.d.ts +71 -0
- package/dist/state.js +269 -169
- package/dist/types.d.ts +4 -2
- package/dist/ui/dock.d.ts +109 -0
- package/dist/ui/hover-card.d.ts +44 -0
- package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
- package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
- package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
- package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
- package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
- package/dist/variant-custom-L16JLLZy.js +1 -0
- package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
- package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
- package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
- package/dist/wire/chunk.d.ts +47 -0
- package/dist/wire/diagnostics.d.ts +575 -0
- package/dist/wire/encode-probe.d.ts +70 -0
- package/dist/wire/encode.d.ts +24 -0
- package/dist/wire/index.d.ts +2 -0
- package/dist/wire/sse.d.ts +10 -2
- package/dist/wire.js +917 -480
- package/frameworks/react/index.tsx +132 -93
- package/llms-full.txt +184 -69
- package/llms.txt +3 -3
- package/package.json +26 -6
- package/src/agent-tooling/archetypes.ts +41 -9
- package/src/agent-tooling/catalog/README.md +676 -0
- package/src/agent-tooling/catalog/catalog-types.ts +231 -0
- package/src/agent-tooling/catalog/fabrications.ts +96 -0
- package/src/agent-tooling/catalog/invariants.ts +284 -0
- package/src/agent-tooling/catalog/labs-titles.ts +114 -0
- package/src/agent-tooling/catalog/scenarios.ts +87 -0
- package/src/agent-tooling/catalog/surfaces.ts +275 -0
- package/src/agent-tooling/integrations/anthropic.ts +7 -1
- package/src/agent-tooling/integrations/cloudflare.ts +43 -2
- package/src/agent-tooling/integrations/langgraph.ts +7 -1
- package/src/agent-tooling/integrations/mastra.ts +7 -1
- package/src/agent-tooling/integrations/mock.ts +92 -17
- package/src/agent-tooling/integrations/ollama.ts +7 -1
- package/src/agent-tooling/integrations/openai.ts +7 -1
- package/src/agent-tooling/integrations/openrouter.ts +7 -1
- package/src/agent-tooling/integrations/pi.ts +56 -1
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
- package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
- package/src/agent-tooling/mcp/manifest.ts +100 -1
- package/src/agent-tooling/mcp/server.ts +61 -5
- package/src/agent-tooling/mcp/tools/reference.ts +440 -4
- package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
- package/src/agent-tooling/mcp/tools/theme.ts +146 -30
- package/src/agent-tooling/mcp/validate-args.ts +141 -0
- package/src/agent-tooling/registry.ts +19 -7
- package/src/agent-tooling/route-emit.ts +26 -2
- package/src/components/attachment-types.ts +47 -1
- package/src/components/attachments.tsx +169 -22
- package/src/components/audio-visualizer/fit-scale.ts +112 -0
- package/src/components/audio-visualizer/index.tsx +80 -3
- package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
- package/src/components/audio-visualizer/variant-bar.tsx +38 -6
- package/src/components/audio-visualizer/variant-custom.tsx +8 -2
- package/src/components/audio-visualizer/variant-grid.tsx +16 -11
- package/src/components/audio-visualizer/variant-radial.tsx +15 -10
- package/src/components/audio-visualizer/variant-wave.tsx +7 -2
- package/src/components/chat-container.tsx +4 -1
- package/src/components/chat-thread.tsx +36 -7
- package/src/components/code-block.tsx +79 -3
- package/src/components/composer.tsx +8 -1
- package/src/components/conversation-item.tsx +114 -4
- package/src/components/conversation-list.tsx +212 -12
- package/src/components/embed.tsx +4 -0
- package/src/components/form.tsx +41 -14
- package/src/components/message.tsx +75 -5
- package/src/components/response-stream.tsx +13 -7
- package/src/components/screen.tsx +5 -0
- package/src/components/scroll-button.tsx +7 -0
- package/src/components/toast.tsx +11 -5
- package/src/components/voice-input.tsx +35 -3
- package/src/components/voice-output.tsx +46 -6
- package/src/components/workspace-shell.tsx +285 -0
- package/src/diagnostics/hook.ts +352 -0
- package/src/diagnostics/index.ts +82 -0
- package/src/diagnostics/report-request.ts +296 -0
- package/src/elements/attachments.tsx +6 -7
- package/src/elements/audio-visualizer.tsx +48 -4
- package/src/elements/chat-workspace.tsx +159 -287
- package/src/elements/chat.tsx +12 -9
- package/src/elements/code-block.tsx +19 -1
- package/src/elements/command.tsx +4 -1
- package/src/elements/compiled.css +1 -1
- package/src/elements/conversation-item.tsx +80 -0
- package/src/elements/conversation-list.tsx +60 -11
- package/src/elements/default-input.tsx +5 -5
- package/src/elements/define.tsx +168 -8
- package/src/elements/diagnostic-events.ts +114 -0
- package/src/elements/dialog.tsx +47 -0
- package/src/elements/disclosure.ts +34 -12
- package/src/elements/dock.tsx +204 -0
- package/src/elements/dropdown.tsx +123 -0
- package/src/elements/element-diagnostics.ts +392 -0
- package/src/elements/element-manifest.json +12 -0
- package/src/elements/element-meta.json +672 -439
- package/src/elements/element-nonscalar.json +170 -0
- package/src/elements/element-types.d.ts +232 -148
- package/src/elements/menu.tsx +13 -0
- package/src/elements/prompt-dock.tsx +2 -1
- package/src/elements/prompt-input.tsx +8 -8
- package/src/elements/register-impl.ts +46 -0
- package/src/elements/register.ts +28 -0
- package/src/elements/resizable.tsx +52 -25
- package/src/elements/scroll-button.tsx +5 -0
- package/src/elements/slots.ts +107 -12
- package/src/elements/styles.css +30 -6
- package/src/elements/thinking-bar.tsx +16 -7
- package/src/elements/toast.tsx +3 -0
- package/src/elements/tool.tsx +26 -5
- package/src/elements/voice-input.tsx +11 -2
- package/src/elements/voice-output.tsx +16 -3
- package/src/index.ts +2 -0
- package/src/primitives/use-resize-observer.ts +33 -0
- package/src/primitives/use-speech-recognition.ts +12 -1
- package/src/primitives/use-stick-to-bottom.ts +34 -2
- package/src/primitives/use-text-stream.ts +33 -2
- package/src/remote/provider-runtime.ts +6 -3
- package/src/schemas/index.ts +1 -0
- package/src/schemas/tool-defs.ts +297 -6
- package/src/solid.ts +5 -0
- package/src/state/index.ts +12 -0
- package/src/state/persistence.ts +180 -0
- package/src/state/stream.ts +71 -4
- package/src/state/threads.ts +142 -0
- package/src/types.ts +4 -2
- package/src/ui/dialog.tsx +6 -0
- package/src/ui/dock.tsx +445 -0
- package/src/ui/dropdown.tsx +67 -23
- package/src/ui/hover-card.tsx +156 -6
- package/src/ui/input.tsx +29 -10
- package/src/ui/resizable.tsx +11 -0
- package/src/wire/chunk.ts +47 -0
- package/src/wire/consume.ts +185 -26
- package/src/wire/diagnostics.ts +727 -0
- package/src/wire/encode-probe.ts +214 -0
- package/src/wire/encode.ts +267 -14
- package/src/wire/formats/anthropic.ts +8 -1
- package/src/wire/formats/openai.ts +5 -0
- package/src/wire/index.ts +22 -0
- package/src/wire/read.ts +250 -6
- package/src/wire/sse.ts +16 -3
- package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
- package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
- package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
- package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
- package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
- package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
- package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
- package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
- package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
- package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
- package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
- package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
- package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
- package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
- package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
- package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
- package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
- package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
- package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
- package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
- package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
- package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
- package/dist/elements/chunks/input-DrseEjhp.js +0 -1
- package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
- package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
- package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
- package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
- package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
- package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
- package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
- package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
- package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
- package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
- package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
- package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
- package/dist/register-impl-DGmGzrEM.js +0 -145
- package/dist/variant-custom-MkTNTycd.js +0 -1
- package/src/primitives/card-validate-generator.testlib.ts +0 -38
package/src/ui/dropdown.tsx
CHANGED
|
@@ -37,6 +37,63 @@ const useDropdown = () => {
|
|
|
37
37
|
// a parent's querySelectorAll scoped to its own menu never reaches sub items.
|
|
38
38
|
const ITEM_SELECTOR = '[role="menuitem"]:not([aria-disabled="true"]), [role="menuitemcheckbox"]:not([aria-disabled="true"]), [role="menuitemradio"]:not([aria-disabled="true"])';
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The roving-focus set, in FLAT-TREE order.
|
|
42
|
+
*
|
|
43
|
+
* ★ NOT `querySelectorAll`, and the difference is the whole point: a `<slot>` in the
|
|
44
|
+
* shadow tree contains NONE of the light-DOM nodes it projects, so a plain query
|
|
45
|
+
* returns zero items for any facade whose rows are SLOTTED (`<kai-dropdown>`) and
|
|
46
|
+
* ArrowUp/Down, Home/End and typeahead all reach nothing. The items-tree facades
|
|
47
|
+
* (`kai-menu`, `kai-model-switcher`, `kai-scope-picker`) render their rows INTO the
|
|
48
|
+
* shadow tree, so both spellings agree for them — which is exactly why this went
|
|
49
|
+
* unnoticed until a slotted menu body existed.
|
|
50
|
+
*
|
|
51
|
+
* Same class as `hasFocusableChild` in ./hover-card.tsx, which asks the boolean
|
|
52
|
+
* version of this question. This one needs the ORDER as well, so it walks the tree
|
|
53
|
+
* and splices each slot's assigned elements in AT THE SLOT'S POSITION rather than
|
|
54
|
+
* appending them; a menu may mix rendered and slotted rows. `flatten: true` follows
|
|
55
|
+
* a slot assigned to another slot.
|
|
56
|
+
*
|
|
57
|
+
* Scope is unchanged: submenu content is portaled to a SIBLING node, so a parent's
|
|
58
|
+
* walk still never reaches sub items.
|
|
59
|
+
*/
|
|
60
|
+
function menuItems(root: HTMLElement | undefined): HTMLElement[] {
|
|
61
|
+
if (!root) return [];
|
|
62
|
+
const out: HTMLElement[] = [];
|
|
63
|
+
const collect = (el: Element): void => {
|
|
64
|
+
if (el.matches(ITEM_SELECTOR)) out.push(el as HTMLElement);
|
|
65
|
+
for (const child of Array.from(el.children)) visit(child);
|
|
66
|
+
};
|
|
67
|
+
const visit = (el: Element): void => {
|
|
68
|
+
if (el.localName === 'slot') {
|
|
69
|
+
for (const assigned of (el as HTMLSlotElement).assignedElements({ flatten: true })) collect(assigned);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
collect(el);
|
|
73
|
+
};
|
|
74
|
+
for (const child of Array.from(root.children)) visit(child);
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The first roving-focus item, in flat-tree order. Replaces
|
|
79
|
+
* `querySelector(ITEM_SELECTOR)` for the reason `menuItems` replaces the `All`. */
|
|
80
|
+
const firstMenuItem = (root: HTMLElement | undefined): HTMLElement | undefined => menuItems(root)[0];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The focused item, resolved across BOTH trees.
|
|
84
|
+
*
|
|
85
|
+
* Inside a Shadow DOM `document.activeElement` returns the HOST rather than the
|
|
86
|
+
* focused menu item, so the menu's own root node is asked first. But a SLOTTED row
|
|
87
|
+
* lives in the LIGHT DOM: focus is then outside the shadow tree, that root reports
|
|
88
|
+
* null, and the document's answer is the real one. Asking only the shadow root would
|
|
89
|
+
* leave `currentIndex()` at -1 for every slotted row, so ArrowDown would restart at
|
|
90
|
+
* the top instead of advancing.
|
|
91
|
+
*/
|
|
92
|
+
function activeMenuItem(root: HTMLElement | undefined): Element | null {
|
|
93
|
+
const tree = root?.getRootNode() as Document | ShadowRoot | undefined;
|
|
94
|
+
return (tree?.activeElement ?? document.activeElement) as Element | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
40
97
|
/** Imperative open controller, handed to a parent (e.g. the kai-menu facade) via
|
|
41
98
|
* `controllerRef` so it can drive/observe the Dropdown's open state. */
|
|
42
99
|
export interface DropdownController { open: Accessor<boolean>; setOpen: (v: boolean) => void; }
|
|
@@ -103,8 +160,8 @@ export function Dropdown(props: DropdownProps) {
|
|
|
103
160
|
// attempt focus now and re-assert in the menu ref's microtask so it lands
|
|
104
161
|
// once the node exists. Skip disabled items (roving-focus contract).
|
|
105
162
|
if (opts?.viaKeyboard) {
|
|
106
|
-
queueMicrotask(() => menu()
|
|
107
|
-
menu()
|
|
163
|
+
queueMicrotask(() => firstMenuItem(menu())?.focus());
|
|
164
|
+
firstMenuItem(menu())?.focus();
|
|
108
165
|
}
|
|
109
166
|
} else if (opts?.returnFocus !== false) {
|
|
110
167
|
// Closing via keyboard/select: return focus to the trigger. The menu
|
|
@@ -179,23 +236,14 @@ export function DropdownContent(props: DropdownContentProps) {
|
|
|
179
236
|
refs: () => [ctx.trigger(), ctx.menu(), ...ctx.subMenus()],
|
|
180
237
|
});
|
|
181
238
|
|
|
182
|
-
const items = () =>
|
|
239
|
+
const items = () => menuItems(ctx.menu());
|
|
183
240
|
const focusIndex = (i: number) => {
|
|
184
241
|
const list = items();
|
|
185
242
|
if (!list.length) return;
|
|
186
243
|
const idx = ((i % list.length) + list.length) % list.length;
|
|
187
244
|
list[idx].focus();
|
|
188
245
|
};
|
|
189
|
-
|
|
190
|
-
// (every kitn-* element), `document.activeElement` returns the host element,
|
|
191
|
-
// not the focused menu item, which would break ArrowUp/Down roving focus.
|
|
192
|
-
// `getRootNode().activeElement` correctly returns the active node within the
|
|
193
|
-
// same tree (ShadowRoot or Document).
|
|
194
|
-
const activeItem = () => {
|
|
195
|
-
const root = ctx.menu()?.getRootNode() as Document | ShadowRoot | undefined;
|
|
196
|
-
return (root?.activeElement ?? document.activeElement) as Element | null;
|
|
197
|
-
};
|
|
198
|
-
const currentIndex = () => items().findIndex((el) => el === activeItem());
|
|
246
|
+
const currentIndex = () => items().findIndex((el) => el === activeMenuItem(ctx.menu()));
|
|
199
247
|
|
|
200
248
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
201
249
|
const list = items();
|
|
@@ -226,7 +274,7 @@ export function DropdownContent(props: DropdownContentProps) {
|
|
|
226
274
|
// synchronously; this ref-time microtask re-asserts focus once the
|
|
227
275
|
// menu node exists. Skip disabled items.
|
|
228
276
|
if (ctx.openedViaKeyboard()) {
|
|
229
|
-
queueMicrotask(() => el
|
|
277
|
+
queueMicrotask(() => firstMenuItem(el)?.focus());
|
|
230
278
|
}
|
|
231
279
|
}}
|
|
232
280
|
id={ctx.menuId}
|
|
@@ -424,8 +472,8 @@ export function DropdownSub(props: DropdownSubProps) {
|
|
|
424
472
|
setOpenSig(v);
|
|
425
473
|
if (v) {
|
|
426
474
|
if (opts?.viaKeyboard) {
|
|
427
|
-
queueMicrotask(() => menu()
|
|
428
|
-
menu()
|
|
475
|
+
queueMicrotask(() => firstMenuItem(menu())?.focus());
|
|
476
|
+
firstMenuItem(menu())?.focus();
|
|
429
477
|
}
|
|
430
478
|
} else if (opts?.returnFocus !== false) {
|
|
431
479
|
const el = trigger();
|
|
@@ -516,18 +564,14 @@ export function DropdownSubContent(props: DropdownSubContentProps) {
|
|
|
516
564
|
// useDismiss here would double-fire Escape because document listeners run
|
|
517
565
|
// after stopPropagation on the element, not on the document.
|
|
518
566
|
|
|
519
|
-
const items = () =>
|
|
567
|
+
const items = () => menuItems(sub.menu());
|
|
520
568
|
const focusIndex = (i: number) => {
|
|
521
569
|
const list = items();
|
|
522
570
|
if (!list.length) return;
|
|
523
571
|
const idx = ((i % list.length) + list.length) % list.length;
|
|
524
572
|
list[idx].focus();
|
|
525
573
|
};
|
|
526
|
-
const
|
|
527
|
-
const root = sub.menu()?.getRootNode() as Document | ShadowRoot | undefined;
|
|
528
|
-
return (root?.activeElement ?? document.activeElement) as Element | null;
|
|
529
|
-
};
|
|
530
|
-
const currentIndex = () => items().findIndex((el) => el === activeItem());
|
|
574
|
+
const currentIndex = () => items().findIndex((el) => el === activeMenuItem(sub.menu()));
|
|
531
575
|
|
|
532
576
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
533
577
|
const list = items();
|
|
@@ -559,7 +603,7 @@ export function DropdownSubContent(props: DropdownSubContentProps) {
|
|
|
559
603
|
const unregister = parent.registerSubMenu(el);
|
|
560
604
|
onCleanup(unregister);
|
|
561
605
|
if (sub.openedViaKeyboard()) {
|
|
562
|
-
queueMicrotask(() => el
|
|
606
|
+
queueMicrotask(() => firstMenuItem(el)?.focus());
|
|
563
607
|
}
|
|
564
608
|
}}
|
|
565
609
|
id={sub.menuId}
|
package/src/ui/hover-card.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createContext, useContext, createSignal, Show, splitProps, onCleanup,
|
|
2
|
+
createContext, useContext, createSignal, createUniqueId, Show, splitProps, onCleanup,
|
|
3
3
|
type JSX, type Accessor,
|
|
4
4
|
} from 'solid-js';
|
|
5
5
|
import { Portal } from 'solid-js/web';
|
|
@@ -17,6 +17,12 @@ interface HoverCardCtx {
|
|
|
17
17
|
setContent: (el: HTMLElement) => void;
|
|
18
18
|
trigger: Accessor<HTMLElement | undefined>;
|
|
19
19
|
content: Accessor<HTMLElement | undefined>;
|
|
20
|
+
/** Ties a focused trigger to the card it reveals. Without it a keyboard tab
|
|
21
|
+
* stop announces nothing, which is a focus stop that wastes the user's time. */
|
|
22
|
+
contentId: string;
|
|
23
|
+
/** Whether this card refuses to open. Read by the TRIGGER, not just by
|
|
24
|
+
* `enter()`: a card that cannot open must not be in the tab order either. */
|
|
25
|
+
disabled: Accessor<boolean>;
|
|
20
26
|
}
|
|
21
27
|
const Ctx = createContext<HoverCardCtx>();
|
|
22
28
|
const useHoverCard = () => {
|
|
@@ -45,6 +51,7 @@ export function HoverCardRoot(props: HoverCardRootProps) {
|
|
|
45
51
|
const [open, setOpen] = createSignal(props.defaultOpen ?? false);
|
|
46
52
|
const [trigger, setTrigger] = createSignal<HTMLElement>();
|
|
47
53
|
const [content, setContent] = createSignal<HTMLElement>();
|
|
54
|
+
const contentId = createUniqueId();
|
|
48
55
|
let timer: number | undefined;
|
|
49
56
|
props.controllerRef?.({ open, setOpen });
|
|
50
57
|
|
|
@@ -73,25 +80,167 @@ export function HoverCardRoot(props: HoverCardRootProps) {
|
|
|
73
80
|
open, enter, leave, close,
|
|
74
81
|
setTrigger, setContent,
|
|
75
82
|
trigger, content,
|
|
83
|
+
contentId,
|
|
84
|
+
disabled: () => props.disabled ?? false,
|
|
76
85
|
}}>
|
|
77
86
|
{props.children}
|
|
78
87
|
</Ctx.Provider>
|
|
79
88
|
);
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
export interface HoverCardTriggerProps {
|
|
91
|
+
export interface HoverCardTriggerProps {
|
|
92
|
+
children: JSX.Element;
|
|
93
|
+
class?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the trigger is its own tab stop. Omit for the automatic behaviour,
|
|
96
|
+
* which is right for every case in this repo: the trigger takes focus only
|
|
97
|
+
* when nothing inside it already can.
|
|
98
|
+
*
|
|
99
|
+
* Pass `false` to keep a trigger out of the tab order entirely, or `true` to
|
|
100
|
+
* force a stop. Both are escape hatches — reach for them when the automatic
|
|
101
|
+
* answer is wrong, not to restate it.
|
|
102
|
+
*/
|
|
103
|
+
focusable?: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Anything the platform puts in the tab order by default, plus anything a
|
|
107
|
+
* consumer opted in with `tabindex`. `tabindex="-1"` is deliberately excluded:
|
|
108
|
+
* it is programmatically focusable but the Tab key skips it, so a trigger
|
|
109
|
+
* containing only such an element still needs a stop of its own. */
|
|
110
|
+
const FOCUSABLE_CHILD =
|
|
111
|
+
'a[href],button,input,select,textarea,summary,[contenteditable=""],[contenteditable="true"],[tabindex]:not([tabindex^="-"])';
|
|
112
|
+
|
|
113
|
+
/** Does this subtree already offer the keyboard a way in? Slotted content is
|
|
114
|
+
* checked through `assignedElements()`, because a `<slot>` in the shadow tree
|
|
115
|
+
* contains none of the light-DOM nodes it projects — the `kai-hover-card`
|
|
116
|
+
* facade wraps a bare `<slot />`, so without this branch every use of that
|
|
117
|
+
* element would be told its children are inert.
|
|
118
|
+
*
|
|
119
|
+
* ★ WHEN this runs is as load-bearing as what it asks. At ref time the facade's
|
|
120
|
+
* trigger contains `<slot></slot>` with NOTHING assigned yet, so a single call
|
|
121
|
+
* from the ref answers "inert" for a slot that is about to receive a focusable
|
|
122
|
+
* `<a>` — and the trigger stamps a second tab stop on top of the child's. That
|
|
123
|
+
* is the very regression the `assignedElements()` branch was added to prevent,
|
|
124
|
+
* reintroduced by timing rather than by logic. Measured with a real Tab key:
|
|
125
|
+
* 4 stops across a 3-widget fixture. So the caller re-runs this on
|
|
126
|
+
* `slotchange`; see `HoverCardTrigger`. */
|
|
127
|
+
const hasFocusableChild = (root: HTMLElement): boolean => {
|
|
128
|
+
if (root.querySelector(FOCUSABLE_CHILD)) return true;
|
|
129
|
+
return Array.from(root.querySelectorAll('slot')).some((slot) =>
|
|
130
|
+
(slot as HTMLSlotElement).assignedElements().some(
|
|
131
|
+
(el) => el.matches(FOCUSABLE_CHILD) || el.querySelector(FOCUSABLE_CHILD),
|
|
132
|
+
),
|
|
133
|
+
);
|
|
134
|
+
};
|
|
83
135
|
|
|
136
|
+
/**
|
|
137
|
+
* ★ THE TRIGGER IS A TAB STOP WHEN, AND ONLY WHEN, ITS CHILDREN ARE NOT.
|
|
138
|
+
*
|
|
139
|
+
* `onFocusIn`/`onFocusOut` were here from the start and the focus-open path was
|
|
140
|
+
* plainly intended, but the span never set `tabindex`, so the Tab key could not
|
|
141
|
+
* land on it and neither handler could fire on a keyboard. It went unnoticed for
|
|
142
|
+
* as long as it did because `focusin` BUBBLES and every consumer at the time put
|
|
143
|
+
* something focusable inside the trigger — `source.tsx` an `<a>`, `context.tsx`
|
|
144
|
+
* a `<Button>` — so the card opened via the child and the span's own inertness
|
|
145
|
+
* never showed. The first trigger with inert children (an attachment tile: a
|
|
146
|
+
* div, an img, an svg) had no tab stop anywhere in it.
|
|
147
|
+
*
|
|
148
|
+
* Hence delegation rather than an unconditional `tabindex`: adding one always
|
|
149
|
+
* would give every existing consumer TWO stops for one card, which is a worse
|
|
150
|
+
* bug than the one being fixed. The check runs in the ref AND again on every
|
|
151
|
+
* `slotchange`, because slot assignment happens after the ref and a one-shot
|
|
152
|
+
* check calls a not-yet-filled `<slot>` inert (see `hasFocusableChild`).
|
|
153
|
+
*
|
|
154
|
+
* ★ THE FOCUS LISTENERS ARE NATIVE, NOT SOLID'S DELEGATED `onFocusIn`. Solid
|
|
155
|
+
* delegates a fixed set of events from the document and retargets them into
|
|
156
|
+
* component trees; inside these shadow roots that path runs for a PROGRAMMATIC
|
|
157
|
+
* `.focus()` and, in the deeper trees, not for a real Tab. Measured: tabbing to
|
|
158
|
+
* an attachment tile in a mounted `<kai-chat>` left the card shut and
|
|
159
|
+
* `aria-describedby` null while `.focus()` on the same element opened it — so
|
|
160
|
+
* every keyboard user got a tab stop that announced nothing and showed nothing,
|
|
161
|
+
* which is worse than no stop at all. `addEventListener` in the ref does not
|
|
162
|
+
* care how focus arrived. Anything in this kit relying on delegated focus
|
|
163
|
+
* events inside a shadow root is suspect for the same reason.
|
|
164
|
+
*
|
|
165
|
+
* `aria-describedby` is what makes the stop worth arriving at: the card is
|
|
166
|
+
* DESCRIPTIVE, not an action, so the trigger gets no `role="button"` — that
|
|
167
|
+
* would promise an activation that does not exist — and instead points at the
|
|
168
|
+
* content it reveals so a screen reader reads it out.
|
|
169
|
+
*/
|
|
84
170
|
export function HoverCardTrigger(props: HoverCardTriggerProps) {
|
|
85
171
|
const ctx = useHoverCard();
|
|
172
|
+
const [detectedInert, setDetectedInert] = createSignal(false);
|
|
173
|
+
// `&& !disabled` last, and it beats an explicit `focusable` — a disabled card
|
|
174
|
+
// early-returns out of `enter()`, so a stop here could never open anything.
|
|
175
|
+
// Landing on it would announce nothing and reveal nothing, which is the dead
|
|
176
|
+
// stop the note above argues against; forcing one is not an escape hatch
|
|
177
|
+
// anybody should get.
|
|
178
|
+
const isFocusable = () => (props.focusable ?? detectedInert()) && !ctx.disabled();
|
|
179
|
+
|
|
86
180
|
return (
|
|
87
181
|
<As
|
|
88
182
|
as="span"
|
|
89
|
-
class={cn(
|
|
90
|
-
|
|
183
|
+
class={cn(
|
|
184
|
+
'inline-block',
|
|
185
|
+
// A tab stop nobody can SEE is barely an improvement on no tab stop —
|
|
186
|
+
// WCAG 2.4.7. Only when this element is the stop; a delegating trigger
|
|
187
|
+
// must not draw a ring around its child's own focus state.
|
|
188
|
+
//
|
|
189
|
+
// AN OUTLINE, NOT A RING, and both halves of that were measured rather
|
|
190
|
+
// than assumed:
|
|
191
|
+
//
|
|
192
|
+
// 1. ★ A TAILWIND v4 UTILITY THAT ROUTES THROUGH `@property` IS INERT
|
|
193
|
+
// INSIDE THESE SHADOW ROOTS, and that is a trap far wider than this
|
|
194
|
+
// line. v4 gives `--tw-*` custom properties their defaults with
|
|
195
|
+
// `@property`, and an `@property` rule delivered through a shadow
|
|
196
|
+
// root's `adoptedStyleSheets` never registers — so the var resolves
|
|
197
|
+
// to nothing, the declaration is invalid, and the property falls
|
|
198
|
+
// back to its initial value. Measured in Chrome, twice:
|
|
199
|
+
// `ring-2 ring-offset-1` set `--tw-ring-shadow` correctly and
|
|
200
|
+
// computed `box-shadow: none`; `inset-ring-2` did the same; and
|
|
201
|
+
// `outline-2` alone computed `outline-style: none` while width,
|
|
202
|
+
// colour and offset all landed, because only the style goes
|
|
203
|
+
// through `var(--tw-outline-style)`. Hence the literal
|
|
204
|
+
// `[outline-style:solid]` — the one part that cannot be a var.
|
|
205
|
+
// 2. The offset is NEGATIVE so the outline lands inside the border
|
|
206
|
+
// box. A trigger that fills its container (`block size-full` inside
|
|
207
|
+
// the attachment tile's `overflow-hidden rounded-lg`) would
|
|
208
|
+
// otherwise have its focus indicator painted straight into the
|
|
209
|
+
// clip, and a trigger cannot know whether its container clips.
|
|
210
|
+
isFocusable() &&
|
|
211
|
+
'rounded-sm focus-visible:[outline-style:solid] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring',
|
|
212
|
+
props.class,
|
|
213
|
+
)}
|
|
214
|
+
ref={(el: HTMLElement) => {
|
|
215
|
+
ctx.setTrigger(el);
|
|
216
|
+
|
|
217
|
+
// Native listeners, deliberately — see the note above. `focusin` /
|
|
218
|
+
// `focusout` rather than `focus` / `blur` because these have to fire
|
|
219
|
+
// for a focusable CHILD too, and only the -in/-out pair bubbles.
|
|
220
|
+
const enter = () => ctx.enter();
|
|
221
|
+
const leave = () => ctx.leave();
|
|
222
|
+
el.addEventListener('focusin', enter);
|
|
223
|
+
el.addEventListener('focusout', leave);
|
|
224
|
+
onCleanup(() => {
|
|
225
|
+
el.removeEventListener('focusin', enter);
|
|
226
|
+
el.removeEventListener('focusout', leave);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Ask now for the direct-children case, and again whenever a slot is
|
|
230
|
+
// filled or emptied. Both are needed: `slotchange` never fires for a
|
|
231
|
+
// trigger with no slot in it, and the ref-time answer is wrong for one
|
|
232
|
+
// that has.
|
|
233
|
+
const evaluate = () => setDetectedInert(!hasFocusableChild(el));
|
|
234
|
+
evaluate();
|
|
235
|
+
for (const slot of Array.from(el.querySelectorAll('slot'))) {
|
|
236
|
+
slot.addEventListener('slotchange', evaluate);
|
|
237
|
+
onCleanup(() => slot.removeEventListener('slotchange', evaluate));
|
|
238
|
+
}
|
|
239
|
+
}}
|
|
240
|
+
tabIndex={isFocusable() ? 0 : undefined}
|
|
241
|
+
aria-describedby={isFocusable() && ctx.open() ? ctx.contentId : undefined}
|
|
91
242
|
onPointerEnter={ctx.enter}
|
|
92
243
|
onPointerLeave={ctx.leave}
|
|
93
|
-
onFocusIn={ctx.enter}
|
|
94
|
-
onFocusOut={ctx.leave}
|
|
95
244
|
>
|
|
96
245
|
{props.children}
|
|
97
246
|
</As>
|
|
@@ -166,6 +315,7 @@ export function HoverCardContent(props: HoverCardContentProps) {
|
|
|
166
315
|
>
|
|
167
316
|
{/* Inner card: all visual + animation classes and the presence state. */}
|
|
168
317
|
<div
|
|
318
|
+
id={ctx.contentId}
|
|
169
319
|
data-expanded={presence.state() === 'open' ? '' : undefined}
|
|
170
320
|
data-closed={presence.state() === 'closed' ? '' : undefined}
|
|
171
321
|
class={cn(
|
package/src/ui/input.tsx
CHANGED
|
@@ -69,7 +69,21 @@ export function Input(props: InputProps): JSX.Element {
|
|
|
69
69
|
const hasHint = () => !!local.error || !!local.hint;
|
|
70
70
|
const hasAffix = () => local.leading != null || local.trailing != null;
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// The class arrives as an ACCESSOR, not a string, and this is load-bearing.
|
|
73
|
+
//
|
|
74
|
+
// Solid evaluates a `<Show>` `fallback` inside the Show's own memo. When the
|
|
75
|
+
// class was computed at the call site — `inputEl(cn(FIELD_BASE, …,
|
|
76
|
+
// isInvalid() && INVALID, local.class), …)` — those reads happened in that
|
|
77
|
+
// memo, so any of them changing re-ran the memo and BUILT A NEW `<input>`
|
|
78
|
+
// NODE: focus, caret and IME composition state all died with the old one. A
|
|
79
|
+
// consumer whose `invalid` is derived from the value (`kai-form` did exactly
|
|
80
|
+
// that) lost focus after every character.
|
|
81
|
+
//
|
|
82
|
+
// Passing a function moves every reactive read inside the element, where
|
|
83
|
+
// Solid compiles it into a nested effect that sets the attribute on the
|
|
84
|
+
// EXISTING node. Same reason the affix branch was always fine: it inserts the
|
|
85
|
+
// input through a function. Pinned by `tests/ui/input-node-identity.test.tsx`.
|
|
86
|
+
const inputEl = (cls: () => string, part: string): JSX.Element => (
|
|
73
87
|
<input
|
|
74
88
|
{...rest}
|
|
75
89
|
id={id}
|
|
@@ -77,7 +91,7 @@ export function Input(props: InputProps): JSX.Element {
|
|
|
77
91
|
disabled={local.disabled}
|
|
78
92
|
aria-invalid={isInvalid() ? 'true' : undefined}
|
|
79
93
|
aria-describedby={hasHint() ? hintId : undefined}
|
|
80
|
-
class={cn(SEARCH_RESET, cls)}
|
|
94
|
+
class={cn(SEARCH_RESET, cls())}
|
|
81
95
|
onInput={(e) => local.onValueInput?.(e.currentTarget.value)}
|
|
82
96
|
onBlur={(e) => {
|
|
83
97
|
const handler = local.onBlur;
|
|
@@ -87,6 +101,17 @@ export function Input(props: InputProps): JSX.Element {
|
|
|
87
101
|
/>
|
|
88
102
|
);
|
|
89
103
|
|
|
104
|
+
const fieldClass = () =>
|
|
105
|
+
cn(FIELD_BASE, local.size === 'sm' && SIZE_SM, isInvalid() && INVALID, local.class);
|
|
106
|
+
|
|
107
|
+
// Created on first use and then REUSED, so toggling an affix on or off does
|
|
108
|
+
// not discard a focused input either. The lazy cache also keeps the unused
|
|
109
|
+
// branch's node (and its effects) from being built at all.
|
|
110
|
+
let plainNode: JSX.Element;
|
|
111
|
+
const plainInput = () => (plainNode ??= inputEl(fieldClass, 'field input'));
|
|
112
|
+
let rowNode: JSX.Element;
|
|
113
|
+
const rowInput = () => (rowNode ??= inputEl(() => ROW_INPUT, 'input'));
|
|
114
|
+
|
|
90
115
|
return (
|
|
91
116
|
<div class="flex w-full flex-col gap-1.5">
|
|
92
117
|
<Show when={local.label}>
|
|
@@ -95,13 +120,7 @@ export function Input(props: InputProps): JSX.Element {
|
|
|
95
120
|
</label>
|
|
96
121
|
</Show>
|
|
97
122
|
|
|
98
|
-
<Show
|
|
99
|
-
when={hasAffix()}
|
|
100
|
-
fallback={inputEl(
|
|
101
|
-
cn(FIELD_BASE, local.size === 'sm' && SIZE_SM, isInvalid() && INVALID, local.class),
|
|
102
|
-
'field input',
|
|
103
|
-
)}
|
|
104
|
-
>
|
|
123
|
+
<Show when={hasAffix()} fallback={plainInput()}>
|
|
105
124
|
<div
|
|
106
125
|
part="field"
|
|
107
126
|
class={cn(
|
|
@@ -115,7 +134,7 @@ export function Input(props: InputProps): JSX.Element {
|
|
|
115
134
|
<Show when={local.leading}>
|
|
116
135
|
<span class="flex shrink-0 items-center text-muted-foreground">{local.leading}</span>
|
|
117
136
|
</Show>
|
|
118
|
-
{
|
|
137
|
+
{rowInput()}
|
|
119
138
|
<Show when={local.trailing}>
|
|
120
139
|
<span class="flex shrink-0 items-center text-muted-foreground">{local.trailing}</span>
|
|
121
140
|
</Show>
|
package/src/ui/resizable.tsx
CHANGED
|
@@ -509,6 +509,17 @@ function ResizableHandle(props: ResizableHandleProps) {
|
|
|
509
509
|
style={{
|
|
510
510
|
cursor: isStatic() ? 'default' : isHoriz() ? 'col-resize' : 'row-resize',
|
|
511
511
|
[isHoriz() ? 'width' : 'height']: '8px',
|
|
512
|
+
// The panels on either side are given explicit `flex-shrink: 0` (see
|
|
513
|
+
// `applyDelta`/`resetPanelToDefault`/`ResizablePanel.sizeStyle`) so THEY
|
|
514
|
+
// never yield space back to the handle. Without an equally explicit
|
|
515
|
+
// shrink here the handle keeps the flexbox default (`flex-shrink: 1`),
|
|
516
|
+
// so the moment its neighbors' sizes sum to (or exceed) the container's
|
|
517
|
+
// main axis — e.g. two percentage sizes that add to 100% after a
|
|
518
|
+
// maximize/restore round trip — flexbox has nowhere else to reclaim the
|
|
519
|
+
// 8px shortfall from and squeezes the handle itself to a computed width
|
|
520
|
+
// of 0. That makes the divider invisible AND undraggable (no hit area)
|
|
521
|
+
// until reload. Pin it to never shrink, matching its neighbors.
|
|
522
|
+
'flex-shrink': '0',
|
|
512
523
|
'background': showStripHighlight() ? 'var(--color-muted-foreground, #98989f)' : 'transparent',
|
|
513
524
|
'opacity': showStripHighlight() ? '0.3' : '1',
|
|
514
525
|
}}
|
package/src/wire/chunk.ts
CHANGED
|
@@ -42,6 +42,21 @@ export interface ModelUsage {
|
|
|
42
42
|
|
|
43
43
|
export interface ModelStreamChunk {
|
|
44
44
|
text?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The model id the RESPONSE stated, verbatim; REPORT, NEVER INFER.
|
|
47
|
+
*
|
|
48
|
+
* Read from the response rather than the request, which is what makes it work
|
|
49
|
+
* at all when the app builds its own fetch and the kit never sees what was
|
|
50
|
+
* asked for. Providers commonly resolve an alias (ask for `gpt-4o`, get
|
|
51
|
+
* `gpt-4o-2024-08-06`); through a gateway the value is the gateway's own id.
|
|
52
|
+
* Both are reasons to pass the string through untouched.
|
|
53
|
+
*
|
|
54
|
+
* It is NOT guaranteed. A proxy can strip or rewrite it and a custom endpoint
|
|
55
|
+
* may omit it, so a consumer renders it as ABSENT when it is absent. Filling
|
|
56
|
+
* the gap with the requested id would lie in exactly the requested-vs-served
|
|
57
|
+
* mismatch this field exists to catch.
|
|
58
|
+
*/
|
|
59
|
+
model?: string;
|
|
45
60
|
/**
|
|
46
61
|
* Reasoning delta. `''` is MEANINGFUL, not a no-op: a redacted block has no
|
|
47
62
|
* readable text but still carries a payload that must round-trip, and a format
|
|
@@ -170,6 +185,38 @@ export interface ModelTurn {
|
|
|
170
185
|
export interface ConsumeOptions {
|
|
171
186
|
/** Label for the reasoning disclosure. Defaults to 'Thinking'. */
|
|
172
187
|
reasoningLabel?: string;
|
|
188
|
+
/**
|
|
189
|
+
* Correlates diagnostics and namespaces reasoning parts for this consume call;
|
|
190
|
+
* assigned automatically when absent.
|
|
191
|
+
*
|
|
192
|
+
* Supply one only to tie a read to an id you already hold. Two reads into the
|
|
193
|
+
* SAME sink must not share a value: the id is what keeps a second round's
|
|
194
|
+
* block 0 from merging into the first round's reasoning part.
|
|
195
|
+
*/
|
|
196
|
+
streamId?: string;
|
|
197
|
+
/**
|
|
198
|
+
* The app's own grouping of several reads into ONE logical turn, carried onto
|
|
199
|
+
* every diagnostic event this read emits.
|
|
200
|
+
*
|
|
201
|
+
* THE KIT REPORTS WHAT THE APP DECLARES AND GROUPS NOTHING ON ITS OWN. A chat
|
|
202
|
+
* app running a tool loop, or fanning out to sub-agents, makes several model
|
|
203
|
+
* calls that belong to one turn; the kit sees one Response at a time and has
|
|
204
|
+
* no way to know which ones those are. So it does not guess:
|
|
205
|
+
*
|
|
206
|
+
* readOpenAIStream(res, sink, { traceId: 'turn-42', label: 'planner' })
|
|
207
|
+
*
|
|
208
|
+
* Absent when not supplied -- the key is not present on the events at all,
|
|
209
|
+
* rather than present and undefined. Purely diagnostic: nothing in the parse
|
|
210
|
+
* branches on it and it never reaches a provider.
|
|
211
|
+
*/
|
|
212
|
+
traceId?: string;
|
|
213
|
+
/** The app's name for THIS read inside its trace (`'planner'`,
|
|
214
|
+
* `'executor'`, `'retry-2'`). Carried onto every diagnostic event, and
|
|
215
|
+
* absent when not supplied. Never derived from the format or the model.
|
|
216
|
+
*
|
|
217
|
+
* Not to be confused with `reasoningLabel`, which is UI copy for the
|
|
218
|
+
* reasoning disclosure; this one is never rendered to an end user. */
|
|
219
|
+
label?: string;
|
|
173
220
|
/** Fires once per tool call the moment its arguments parse cleanly. This is
|
|
174
221
|
* the hook a host's tool loop waits on. There is deliberately no
|
|
175
222
|
* per-fragment callback: `ToolPart.rawInput` is written on every fragment,
|