@kitn.ai/ui 0.20.1 → 0.22.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 +44 -45
- package/dist/components/artifact-card.d.ts +51 -0
- package/dist/components/artifact.d.ts +1 -1
- package/dist/components/attachments.d.ts +2 -2
- package/dist/components/audio-visualizer/index.d.ts +3 -3
- package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
- package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
- package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
- package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
- package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
- package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
- package/dist/components/card-fallback.d.ts +12 -2
- package/dist/components/card-renderer.d.ts +63 -2
- package/dist/components/chat-container.d.ts +3 -0
- package/dist/components/chat-scope-picker.d.ts +2 -2
- package/dist/components/chat-thread.d.ts +77 -18
- package/dist/components/choice-card.d.ts +3 -28
- package/dist/components/composer-dom.d.ts +1 -1
- package/dist/components/composer-highlight.d.ts +1 -1
- package/dist/components/composer-history.d.ts +1 -1
- package/dist/components/composer.d.ts +1 -1
- package/dist/components/confirm-card.d.ts +3 -18
- package/dist/components/context.d.ts +8 -4
- package/dist/components/conversation-item.d.ts +1 -1
- package/dist/components/conversation-list.d.ts +4 -3
- package/dist/components/embed.d.ts +3 -3
- package/dist/components/file-tree.d.ts +11 -22
- package/dist/components/form-widgets.d.ts +1 -1
- package/dist/components/form.d.ts +3 -44
- package/dist/components/link-preview.d.ts +3 -3
- package/dist/components/loader.d.ts +30 -48
- package/dist/components/message-skills.d.ts +4 -0
- package/dist/components/message.d.ts +84 -21
- package/dist/components/model-switcher.d.ts +2 -2
- package/dist/components/prompt-input.d.ts +2 -2
- package/dist/components/response-compare-types.d.ts +11 -2
- package/dist/components/response-compare.d.ts +3 -3
- package/dist/components/source.d.ts +12 -3
- package/dist/components/tasks-card.d.ts +3 -31
- package/dist/components/thread.d.ts +17 -5
- package/dist/components/toast.d.ts +2 -2
- package/dist/components/tool-classify.d.ts +8 -0
- package/dist/components/tool-types.d.ts +29 -1
- package/dist/components/tool.d.ts +3 -3
- package/dist/components/use-card-resolution.d.ts +1 -1
- package/dist/create-tween-COnPwAQN.js +338 -0
- package/dist/create-tween-DmTUaXWE.js +341 -0
- package/dist/create-tween-qs-jjvNA.js +7 -0
- package/dist/custom-elements.json +1726 -463
- 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/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-types.d.ts +70 -10
- 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-Tj58ES_d.js → Icon-CHG5z5L7.js} +1 -1
- package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-BAtCEEgg.js} +1 -1
- package/dist/elements/chunks/arrow-left-CFPTeL1Y.js +1 -0
- package/dist/elements/chunks/artifact-fBZOE4SO.js +1 -0
- package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-DpYMe5VQ.js} +1 -1
- package/dist/elements/chunks/audio-visualizer-DFwzAcuL.js +1 -0
- package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-D6zodm6E.js} +1 -1
- package/dist/elements/chunks/{button-ZWNif3--.js → button-C42vrGvO.js} +1 -1
- package/dist/elements/chunks/card-renderer-DMZsSuvU.js +1 -0
- package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
- package/dist/elements/chunks/chat-thread-CNNEhol-.js +1 -0
- package/dist/elements/chunks/check-7Q3K-iXE.js +1 -0
- package/dist/elements/chunks/chevron-down-CpNYLn7E.js +1 -0
- package/dist/elements/chunks/chevron-right-DrH85Fha.js +1 -0
- package/dist/elements/chunks/choice-card-YoWjpsFV.js +1 -0
- package/dist/elements/chunks/circle-C9KRjKYv.js +1 -0
- package/dist/elements/chunks/circle-check-Dmi7Y4zw.js +1 -0
- package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-QIxXda6k.js} +1 -1
- package/dist/elements/chunks/code-block-CqeugReY.js +1 -0
- package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-sTv-_5lF.js} +1 -1
- package/dist/elements/chunks/{composer-BguS0QL_.js → composer-ldQyeVEB.js} +2 -2
- package/dist/elements/chunks/confirm-card-C0RNOwfn.js +1 -0
- package/dist/elements/chunks/context-5qgypNHC.js +1 -0
- package/dist/elements/chunks/conversation-list-DLzOacXz.js +1 -0
- package/dist/elements/chunks/create-tween-V4TXZ93X.js +7 -0
- package/dist/elements/chunks/default-input-DeeRSDHc.js +1 -0
- package/dist/elements/chunks/define-lE-QvQFn.js +1 -0
- package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DZSa9jog.js} +1 -1
- package/dist/elements/chunks/{download-Dsbg3ste.js → download-B2ZmnGqW.js} +1 -1
- package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-R3B7cSRE.js} +1 -1
- package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BmxceY4l.js} +1 -1
- package/dist/elements/chunks/embed-BVrtTZPo.js +1 -0
- package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-BbdR7YO7.js} +1 -1
- package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-D7dijo1A.js} +1 -1
- package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-0upJ5jeb.js} +1 -1
- package/dist/elements/chunks/{folder-glP5rha4.js → folder-D8z58t7B.js} +1 -1
- package/dist/elements/chunks/form-p5BKJNw5.js +1 -0
- package/dist/elements/chunks/hover-card-CN9s2md5.js +1 -0
- package/dist/elements/chunks/{icon-BLb8beoi.js → icon-CNiKP37E.js} +1 -1
- package/dist/elements/chunks/{info-CIsv92JK.js → info-BSnuJiHI.js} +1 -1
- package/dist/elements/chunks/{input-Dds1kFyt.js → input-BauDmzxX.js} +1 -1
- package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-BlHJaP9b.js} +1 -1
- package/dist/elements/chunks/{link-i4r_k7z4.js → link-DEi5iUWC.js} +1 -1
- package/dist/elements/chunks/link-preview-BaHyM6W4.js +1 -0
- package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-CmJG_Fkq.js} +1 -1
- package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-D2jaMpCN.js} +1 -1
- package/dist/elements/chunks/message-C5UfXZ2Z.js +1 -0
- package/dist/elements/chunks/message-CXWLmmLn.js +1 -0
- package/dist/elements/chunks/message-square-hXylwsy0.js +1 -0
- package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-C9ytZFMU.js} +1 -1
- package/dist/elements/chunks/model-switcher-CivGfD7-.js +1 -0
- package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-DJL_sbOm.js} +1 -1
- package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CFaB8naM.js} +1 -1
- package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-Br5SksfR.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-CMIvtAhU.js} +1 -1
- package/dist/elements/chunks/reasoning-DpZcNzLq.js +1 -0
- package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-Q8i3T9a_.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-B5IHCBFV.js} +1 -1
- package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-eLeydBje.js} +1 -1
- package/dist/elements/chunks/scroll-button-DWbz0yA1.js +1 -0
- package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-Xic78AD_.js} +1 -1
- package/dist/elements/chunks/{settings-B8lmec4D.js → settings-B8H51pAx.js} +1 -1
- package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-CsG9eYLe.js} +1 -1
- package/dist/elements/chunks/{share-C3lTiYGt.js → share-CNTx0z__.js} +1 -1
- package/dist/elements/chunks/skeleton-CLlHTcMI.js +1 -0
- package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
- package/dist/elements/chunks/source-BoVcU5ok.js +1 -0
- package/dist/elements/chunks/{star-CSJtGxgv.js → star-CxQVTVhI.js} +1 -1
- package/dist/elements/chunks/{store-CHyW95TP.js → store-CLtS5CUG.js} +1 -1
- package/dist/elements/chunks/tasks-card-CzlkLmUt.js +1 -0
- package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D5NhpIQ1.js} +1 -1
- package/dist/elements/chunks/textarea-x5Uxsmtm.js +1 -0
- package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-UobcJnx9.js} +1 -1
- package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-BOfs9eXq.js} +1 -1
- package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-Be6Dh_dt.js} +1 -1
- package/dist/elements/chunks/tooltip-RxMxyCLF.js +1 -0
- package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C4yxH2XN.js} +1 -1
- package/dist/elements/chunks/use-card-resolution-jva9ehH6.js +1 -0
- package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Dx4VRcjS.js} +2 -2
- package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-Y1ZTWgEa.js} +1 -1
- package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-B9gml9Cg.js} +2 -2
- package/dist/elements/chunks/{video-B7vmenoC.js → video-CrPHsytT.js} +1 -1
- package/dist/elements/chunks/x-_-OPHept.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-list.js +1 -1
- package/dist/elements/default-input.d.ts +34 -7
- package/dist/elements/define.d.ts +1 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/disclosure.d.ts +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-data-types.d.ts +107 -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 +8 -6
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- 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/slot-text.d.ts +51 -0
- package/dist/elements/slots.d.ts +21 -0
- 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/validate-messages.d.ts +25 -0
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +2734 -217
- package/dist/index.d.ts +163 -125
- package/dist/index.js +6572 -5990
- package/dist/index.server.d.ts +5 -0
- package/dist/index.server.js +5403 -4860
- package/dist/kai-provider.es.d.ts +5 -0
- package/dist/kai-provider.es.js +79 -77
- package/dist/kai.es.d.ts +5 -0
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +774 -151
- package/dist/llms/llms.txt +57 -5
- package/dist/mcp.es.js +4756 -782
- package/dist/primitives/card-component-types.d.ts +11 -0
- package/dist/primitives/card-data-types.d.ts +196 -0
- package/dist/primitives/card-host.d.ts +1 -1
- package/dist/primitives/card-recovery.d.ts +3 -3
- package/dist/primitives/card-registry.d.ts +4 -14
- package/dist/primitives/card-resolution.d.ts +1 -1
- package/dist/primitives/card-routing.d.ts +1 -1
- package/dist/primitives/card-tags.d.ts +11 -0
- package/dist/primitives/card-validate-cards.d.ts +57 -0
- package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
- package/dist/primitives/card-validate-schemas.d.ts +17 -0
- package/dist/primitives/card-validate.d.ts +24 -0
- package/dist/primitives/create-kai-chat.d.ts +3 -3
- package/dist/primitives/embed-providers.d.ts +1 -1
- package/dist/primitives/link-preview.d.ts +1 -1
- package/dist/primitives/message-feedback.d.ts +2 -2
- package/dist/primitives/toast-store.d.ts +1 -1
- package/dist/react/index.d.ts +730 -302
- package/dist/react/use-kai-chat.d.ts +8 -4
- package/dist/react.d.ts +5 -0
- package/dist/react.js +418 -317
- package/dist/register-impl-AxIlqjo7.js +145 -0
- package/dist/remote/host-embed.d.ts +1 -1
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/provider-runtime.d.ts +2 -2
- package/dist/remote/provider.d.ts +2 -2
- package/dist/remote/validate.d.ts +1 -1
- package/dist/remote/wire.d.ts +1 -1
- package/dist/schemas/artifact.schema.json +92 -0
- package/dist/schemas/embed.schema.json +0 -2
- package/dist/schemas/from-tool-call.d.ts +48 -0
- package/dist/schemas/index.d.ts +48 -0
- package/dist/schemas/link.schema.json +0 -2
- package/dist/schemas/provider-subsets.d.ts +136 -0
- package/dist/schemas/registry.d.ts +140 -0
- package/dist/schemas/tool-defs.d.ts +121 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +717 -0
- package/dist/solid-BGYSJyHG.js +22960 -0
- package/dist/solid-Dt2nEwfl.js +20194 -0
- package/dist/solid.d.ts +66 -0
- package/dist/solid.js +261 -0
- package/dist/solid.server.d.ts +5 -0
- package/dist/solid.server.js +261 -0
- package/dist/state/index.d.ts +14 -4
- package/dist/state/messages.d.ts +8 -3
- package/dist/state/mock.d.ts +46 -0
- package/dist/state/parts.d.ts +85 -0
- package/dist/state/stream.d.ts +15 -9
- package/dist/state.d.ts +5 -0
- package/dist/state.js +253 -84
- package/dist/ui/action-icons.d.ts +1 -1
- package/dist/ui/collapsible.d.ts +11 -6
- package/dist/ui/dropdown.d.ts +47 -42
- package/dist/ui/pane-group.d.ts +1 -1
- package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-B44X69AG.js} +1 -1
- package/dist/variant-aurora-BRhV8Flk.js +391 -0
- package/dist/variant-aurora-Ds5ou9BH.js +381 -0
- package/dist/variant-custom-B20FhtYj.js +170 -0
- package/dist/{variant-custom-Dif-IV4u.js → variant-custom-BvA1Schl.js} +1 -1
- package/dist/variant-custom-D6Rtvneb.js +200 -0
- package/dist/{variant-wave-DbfNdvSi.js → variant-wave-BUQtkryq.js} +1 -1
- package/dist/variant-wave-BVYnqy9L.js +218 -0
- package/dist/variant-wave-DXC6_LcH.js +208 -0
- package/dist/wire/chunk.d.ts +171 -0
- package/dist/wire/consume.d.ts +26 -0
- package/dist/wire/encode.d.ts +221 -0
- package/dist/wire/files.d.ts +116 -0
- package/dist/wire/formats/anthropic.d.ts +2 -0
- package/dist/wire/formats/openai.d.ts +5 -0
- package/dist/wire/index.d.ts +16 -0
- package/dist/wire/media-types.d.ts +106 -0
- package/dist/wire/read.d.ts +26 -0
- package/dist/wire/sink-helpers.d.ts +21 -0
- package/dist/wire/sse.d.ts +33 -0
- package/dist/wire.d.ts +5 -53
- package/dist/wire.js +1126 -0
- package/frameworks/react/index.tsx +191 -158
- package/frameworks/react/runtime.tsx +21 -1
- package/frameworks/react/use-kai-chat.tsx +11 -1
- package/llms-full.txt +774 -151
- package/llms.txt +57 -5
- package/package.json +62 -8
- package/src/agent-tooling/archetypes.ts +127 -0
- package/src/agent-tooling/integrations/anthropic.ts +436 -0
- package/src/agent-tooling/integrations/cloudflare.ts +141 -52
- package/src/agent-tooling/integrations/langgraph.ts +80 -13
- package/src/agent-tooling/integrations/mastra.ts +161 -12
- package/src/agent-tooling/integrations/mock.ts +47 -8
- package/src/agent-tooling/integrations/ollama.ts +68 -24
- package/src/agent-tooling/integrations/openai.ts +89 -0
- package/src/agent-tooling/integrations/openrouter.ts +58 -8
- package/src/agent-tooling/integrations/pi.ts +104 -23
- package/src/agent-tooling/integrations/pydantic-ai.ts +61 -5
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +120 -13
- package/src/agent-tooling/mcp/manifest.ts +206 -25
- package/src/agent-tooling/mcp/tools/debug.ts +13 -7
- package/src/agent-tooling/mcp/tools/reference.ts +285 -6
- package/src/agent-tooling/mcp/tools/scaffold.ts +4152 -661
- package/src/agent-tooling/registry.ts +74 -0
- package/src/agent-tooling/types.ts +361 -1
- package/src/components/artifact-card.tsx +120 -0
- package/src/components/audio-visualizer/index.tsx +9 -0
- package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
- package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
- package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
- package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
- package/src/components/card-fallback.tsx +42 -10
- package/src/components/card-renderer.tsx +146 -9
- package/src/components/chat-container.tsx +4 -0
- package/src/components/chat-thread.tsx +128 -52
- package/src/components/choice-card.tsx +17 -29
- package/src/components/composer.tsx +12 -2
- package/src/components/confirm-card.tsx +24 -20
- package/src/components/context.tsx +30 -16
- package/src/components/conversation-item.tsx +6 -0
- package/src/components/conversation-list.tsx +18 -2
- package/src/components/file-tree.tsx +11 -22
- package/src/components/file-upload.tsx +14 -8
- package/src/components/form.tsx +14 -47
- package/src/components/loader.tsx +39 -12
- package/src/components/message-skills.tsx +4 -0
- package/src/components/message.tsx +294 -46
- package/src/components/model-switcher.tsx +38 -8
- package/src/components/reasoning.tsx +92 -5
- package/src/components/response-compare-types.ts +25 -0
- package/src/components/response-compare.tsx +4 -9
- package/src/components/source.tsx +50 -24
- package/src/components/tasks-card.tsx +10 -33
- package/src/components/thread.tsx +84 -42
- package/src/components/toast.tsx +17 -6
- package/src/components/tool-classify.ts +26 -0
- package/src/components/tool-types.ts +31 -1
- package/src/elements/agent-card.tsx +11 -11
- package/src/elements/artifact.tsx +15 -7
- package/src/elements/attachments.tsx +6 -4
- package/src/elements/avatar.tsx +1 -1
- package/src/elements/button.tsx +29 -6
- package/src/elements/card.tsx +2 -2
- package/src/elements/cards.tsx +109 -14
- package/src/elements/chain-of-thought.tsx +14 -10
- package/src/elements/chat-scope-picker.tsx +9 -7
- package/src/elements/chat-types.ts +59 -6
- package/src/elements/chat-workspace.tsx +50 -9
- package/src/elements/chat.tsx +90 -3
- package/src/elements/choice.tsx +8 -8
- package/src/elements/coachmark.tsx +1 -1
- package/src/elements/command.tsx +8 -23
- package/src/elements/compare.tsx +7 -6
- package/src/elements/compiled.css +1 -1
- package/src/elements/composer.tsx +8 -8
- package/src/elements/confirm-card.tsx +9 -9
- package/src/elements/context-meter.tsx +4 -18
- package/src/elements/conversation-list.tsx +14 -9
- package/src/elements/default-input.tsx +131 -8
- package/src/elements/define.tsx +178 -43
- package/src/elements/editable-label.tsx +17 -2
- package/src/elements/element-data-types.ts +112 -0
- package/src/elements/element-meta.json +1624 -306
- package/src/elements/element-types.d.ts +2645 -214
- package/src/elements/feedback-bar.tsx +1 -1
- package/src/elements/file-tree.tsx +1 -1
- package/src/elements/file-upload.tsx +1 -1
- package/src/elements/form.tsx +14 -6
- package/src/elements/input.tsx +1 -1
- package/src/elements/kbd.tsx +1 -1
- package/src/elements/menu.tsx +40 -32
- package/src/elements/message-skills.tsx +7 -9
- package/src/elements/message.tsx +200 -32
- package/src/elements/model-switcher.tsx +5 -3
- package/src/elements/pane.tsx +1 -1
- package/src/elements/prompt-dock.tsx +8 -8
- package/src/elements/prompt-input.tsx +10 -10
- package/src/elements/prompt-suggestions.tsx +7 -4
- package/src/elements/reasoning.tsx +1 -1
- package/src/elements/register.ts +9 -0
- package/src/elements/resizable.globals.d.ts +36 -0
- package/src/elements/resizable.tsx +24 -13
- package/src/elements/response-stream.tsx +1 -1
- package/src/elements/scroll-button.tsx +1 -1
- package/src/elements/search.tsx +6 -1
- package/src/elements/segmented.tsx +5 -11
- package/src/elements/separator.tsx +1 -1
- package/src/elements/slot-text.ts +73 -0
- package/src/elements/slots.ts +67 -24
- package/src/elements/source.tsx +21 -22
- package/src/elements/switch.tsx +3 -3
- package/src/elements/tasks.tsx +5 -4
- package/src/elements/thread.tsx +36 -4
- package/src/elements/toast.tsx +3 -2
- package/src/elements/tool.tsx +1 -1
- package/src/elements/validate-messages.ts +76 -0
- package/src/elements/voice-input.tsx +7 -7
- package/src/elements/voice-output.tsx +4 -4
- package/src/index.ts +145 -6
- package/src/primitives/card-component-types.ts +63 -0
- package/src/primitives/card-data-types.ts +312 -0
- package/src/primitives/card-recovery.ts +13 -2
- package/src/primitives/card-registry.tsx +34 -23
- package/src/primitives/card-schemas/artifact.schema.json +92 -0
- package/src/primitives/card-schemas/embed.schema.json +0 -2
- package/src/primitives/card-schemas/link.schema.json +0 -2
- package/src/primitives/card-tags.ts +61 -0
- package/src/primitives/card-validate-cards.ts +255 -0
- package/src/primitives/card-validate-generator.testlib.ts +38 -0
- package/src/primitives/card-validate-schemas.ts +58 -0
- package/src/primitives/card-validate.ts +48 -20
- package/src/primitives/create-tween.ts +54 -2
- package/src/primitives/message-feedback.ts +3 -2
- package/src/primitives/use-audio-analysis.ts +20 -1
- package/src/primitives/use-sequencer.ts +20 -1
- package/src/schemas/from-tool-call.ts +151 -0
- package/src/schemas/index.ts +340 -0
- package/src/schemas/provider-subsets.ts +538 -0
- package/src/schemas/registry.ts +355 -0
- package/src/schemas/tool-defs.ts +492 -0
- package/src/solid.ts +150 -0
- package/src/state/index.ts +27 -1
- package/src/state/messages.ts +21 -4
- package/src/state/mock.ts +211 -0
- package/src/state/parts.ts +295 -0
- package/src/state/stream.ts +56 -58
- package/src/ui/collapsible.tsx +17 -5
- package/src/ui/dropdown.tsx +46 -12
- package/src/ui/overlay.tsx +10 -4
- package/src/wire/chunk.ts +201 -0
- package/src/wire/consume.ts +440 -0
- package/src/wire/encode.ts +689 -0
- package/src/wire/files.ts +342 -0
- package/src/wire/formats/anthropic.ts +267 -0
- package/src/wire/formats/openai.ts +187 -0
- package/src/wire/index.ts +73 -0
- package/src/wire/media-types.ts +344 -0
- package/src/wire/read.ts +152 -0
- package/src/wire/sink-helpers.ts +50 -0
- package/src/wire/sse.ts +140 -0
- package/dist/create-tween-hvpmSuwW.js +0 -7
- package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
- package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
- package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
- package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
- package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
- package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
- package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
- package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
- package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
- package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
- package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
- package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
- package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
- package/dist/elements/chunks/default-input-Cqqilg9t.js +0 -1
- package/dist/elements/chunks/define-D1din7Ld.js +0 -1
- package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
- package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
- package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
- package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
- package/dist/elements/chunks/model-switcher-B94YAKsM.js +0 -1
- package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
- package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
- package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
- package/dist/elements/chunks/slots--V19HwUb.js +0 -1
- package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
- package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
- package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
- package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
- package/dist/host-embed.d.ts +0 -33
- package/dist/kai-provider.d.ts +0 -2
- package/dist/origin.d.ts +0 -5
- package/dist/provider-runtime.d.ts +0 -27
- package/dist/register-impl-DQyejdfb.js +0 -145
- package/dist/validate.d.ts +0 -4
- package/dist/version.d.ts +0 -4
- package/src/components/component-meta.json +0 -4550
- package/src/elements/card-media.jpg +0 -0
- package/src/elements/resizable.d.ts +0 -27
- package/src/elements/video-poster.jpg +0 -0
|
@@ -31,7 +31,7 @@ export function parseKaiTriggerItemElement(n: Element, char: string, kind: strin
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
interface Props extends Record<string, unknown> {
|
|
34
|
-
/** Controlled value
|
|
34
|
+
/** Controlled value: a string or a full ComposerDoc (set as JS property). */
|
|
35
35
|
value?: string | ComposerDoc;
|
|
36
36
|
/** Placeholder text shown when the composer is empty. */
|
|
37
37
|
placeholder?: string;
|
|
@@ -43,9 +43,9 @@ interface Props extends Record<string, unknown> {
|
|
|
43
43
|
maxHeight?: number | string;
|
|
44
44
|
/** Whether pressing Enter (without Shift) submits. Default true. */
|
|
45
45
|
submitOnEnter?: boolean;
|
|
46
|
-
/** Trigger definitions
|
|
46
|
+
/** Trigger definitions. Set as a JS property. */
|
|
47
47
|
triggers?: TriggerDef[];
|
|
48
|
-
/** Keyword highlight rules
|
|
48
|
+
/** Keyword highlight rules. Set as a JS property. */
|
|
49
49
|
highlights?: HighlightRule[];
|
|
50
50
|
/** Default icon per entity kind (kind → image URL/data-URI) for items without
|
|
51
51
|
* their own `icon`. Overrides the built-in agent/plugin glyphs. JS property. */
|
|
@@ -67,7 +67,7 @@ interface Events {
|
|
|
67
67
|
/** The active trigger was dismissed (Escape, space, or outside click). */
|
|
68
68
|
'kai-trigger-close': Record<string, never>;
|
|
69
69
|
/** The composer gained focus. `focus`/`blur` are NOT composed natively, so they
|
|
70
|
-
* don't escape the shadow root
|
|
70
|
+
* don't escape the shadow root; these re-expose them on the host. (For
|
|
71
71
|
* `keydown`/`paste`/`focusin`/`focusout`, listen NATIVELY on `<kai-composer>`:
|
|
72
72
|
* they're composed and already cross the shadow boundary.) */
|
|
73
73
|
'kai-focus': { originalEvent: FocusEvent };
|
|
@@ -100,7 +100,7 @@ defineWebComponent<Props, Events>('kai-composer', {
|
|
|
100
100
|
let controller: ComposerController | undefined;
|
|
101
101
|
expose({
|
|
102
102
|
/** Focus the editable element. `focus`/`blur` are NOT composed natively, so a
|
|
103
|
-
* host-level focus() can't reach the editable inside the shadow root
|
|
103
|
+
* host-level focus() can't reach the editable inside the shadow root. This is
|
|
104
104
|
* the only way to focus the composer programmatically. */
|
|
105
105
|
focus: (options?: FocusOptions) => controller?.focus(options),
|
|
106
106
|
/** Blur the editable element. */
|
|
@@ -108,9 +108,9 @@ defineWebComponent<Props, Events>('kai-composer', {
|
|
|
108
108
|
/** Empty the composer to a blank doc (resets the internal value + history
|
|
109
109
|
* baseline; fires kai-value-change). */
|
|
110
110
|
clear: () => controller?.clear(),
|
|
111
|
-
/** Submit the current content programmatically
|
|
112
|
-
* kai-submit). Named `send`, not `submit`, to match the shared
|
|
113
|
-
* avoid any submit collision. */
|
|
111
|
+
/** Submit the current content programmatically, on the same path as Enter
|
|
112
|
+
* (fires kai-submit). Named `send`, not `submit`, to match the shared
|
|
113
|
+
* vocabulary and avoid any submit collision. */
|
|
114
114
|
send: () => controller?.send(),
|
|
115
115
|
/** Insert an atomic entity pill (skill/agent/plugin) at the caret without
|
|
116
116
|
* typing a trigger (fires kai-entity-add). */
|
|
@@ -6,13 +6,13 @@ interface Props extends Record<string, unknown> {
|
|
|
6
6
|
/** The confirm definition (the CardEnvelope.data). Set as a JS PROPERTY:
|
|
7
7
|
* `el.data = { body, tone, actions:[…] }`. Import `ConfirmCardData` from
|
|
8
8
|
* `@kitn.ai/ui` for the full shape. */
|
|
9
|
-
data?:
|
|
9
|
+
data?: ConfirmCardData;
|
|
10
10
|
/** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
|
|
11
11
|
cardId?: string;
|
|
12
12
|
/** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
|
|
13
13
|
heading?: string;
|
|
14
|
-
/** Focus the default action on mount (off by default
|
|
15
|
-
* Attribute: `autofocus`. */
|
|
14
|
+
/** Focus the default action on mount (off by default, so nothing steals
|
|
15
|
+
* focus). Attribute: `autofocus`. */
|
|
16
16
|
autofocus?: boolean;
|
|
17
17
|
/** Set when the user resolved this card; renders the read-only view. Property:
|
|
18
18
|
* `el.resolution = { kind:'action', action:'…' }`. */
|
|
@@ -48,22 +48,22 @@ defineWebComponent<Props>(
|
|
|
48
48
|
// `dismiss`/`reopen`).
|
|
49
49
|
let controller: ConfirmController | undefined;
|
|
50
50
|
expose({
|
|
51
|
-
/** Focus the default action button (or the first action if none is default)
|
|
52
|
-
*
|
|
51
|
+
/** Focus the default action button (or the first action if none is default).
|
|
52
|
+
* The same target `autofocus` focuses on mount, but on demand. */
|
|
53
53
|
focus: (options?: FocusOptions) => controller?.focus(options),
|
|
54
|
-
/** Activate an action by id
|
|
54
|
+
/** Activate an action by id: emits the `action` verb on kai-card and resolves
|
|
55
55
|
* the card (single-shot). With no id, invokes the default action. */
|
|
56
56
|
confirm: (actionId?: string) => controller?.confirm(actionId),
|
|
57
|
-
/** Trigger the dismiss path
|
|
57
|
+
/** Trigger the dismiss path: emits `dismiss` on kai-card and optimistically
|
|
58
58
|
* collapses the card to its re-openable stub. */
|
|
59
59
|
dismiss: () => controller?.dismiss(),
|
|
60
|
-
/** Re-open a dismissed card from its stub
|
|
60
|
+
/** Re-open a dismissed card from its stub: emits `reopen` on kai-card. */
|
|
61
61
|
reopen: () => controller?.reopen(),
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
return (
|
|
65
65
|
<ConfirmCard
|
|
66
|
-
data={props.data
|
|
66
|
+
data={props.data}
|
|
67
67
|
cardId={props.cardId ?? (element.id || 'kai-confirm')}
|
|
68
68
|
heading={props.heading}
|
|
69
69
|
autofocus={flag('autofocus')}
|
|
@@ -16,27 +16,13 @@ import {
|
|
|
16
16
|
DEFAULT_DANGER_THRESHOLD,
|
|
17
17
|
type ContextSeverity,
|
|
18
18
|
} from '../components/context';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
usedTokens: number;
|
|
23
|
-
/** The model's total context-window size in tokens. */
|
|
24
|
-
maxTokens: number;
|
|
25
|
-
/** Tokens attributed to the prompt/input, shown in the breakdown. */
|
|
26
|
-
inputTokens?: number;
|
|
27
|
-
/** Tokens attributed to the generated output, shown in the breakdown. */
|
|
28
|
-
outputTokens?: number;
|
|
29
|
-
/** Tokens attributed to reasoning/thinking, shown when present. */
|
|
30
|
-
reasoningTokens?: number;
|
|
31
|
-
/** Tokens served from prompt cache, shown when present. */
|
|
32
|
-
cacheTokens?: number;
|
|
33
|
-
/** Estimated cost in dollars for this usage, shown in the footer. */
|
|
34
|
-
estimatedCost?: number;
|
|
35
|
-
}
|
|
19
|
+
// Public shape of the `context` prop; lives in ./element-data-types so the ROOT
|
|
20
|
+
// entry can re-export it (see that file's header).
|
|
21
|
+
import type { KaiContextUsage } from './element-data-types';
|
|
36
22
|
|
|
37
23
|
interface Props extends Record<string, unknown> {
|
|
38
24
|
/** Token-usage data. Set as a JS property. */
|
|
39
|
-
context?:
|
|
25
|
+
context?: KaiContextUsage;
|
|
40
26
|
/**
|
|
41
27
|
* Fraction (0–1) above which the meter turns yellow.
|
|
42
28
|
* Defaults to `0.7` (70%).
|
|
@@ -6,13 +6,18 @@ import { ConversationList, CollapsedRail, type ConversationListController } from
|
|
|
6
6
|
import type { ConversationGroup, ConversationSummary, ConversationScope } from '../types';
|
|
7
7
|
|
|
8
8
|
interface Props extends Record<string, unknown> {
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
/** The list's section headers (`{ id, name, sortOrder, createdAt }`), rendered
|
|
10
|
+
* in array order. A group carries no conversations of its own; it is matched
|
|
11
|
+
* against `conversations` by id, so the two props are complementary rather
|
|
12
|
+
* than alternatives. Omit for an ungrouped list. Set as a JS property. */
|
|
13
|
+
groups?: ConversationGroup[];
|
|
14
|
+
/** Every conversation the list renders, flat. Each one is filed under the group
|
|
15
|
+
* whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId`
|
|
16
|
+
* matching no entry in `groups`, falls into a trailing "Ungrouped" section, so
|
|
17
|
+
* nothing you pass in is ever dropped. There is no recency bucketing. Set as a
|
|
18
|
+
* JS property. Omit to supply them as `<kai-conversation>` light-DOM children
|
|
19
|
+
* instead, or for the empty state. */
|
|
20
|
+
conversations?: ConversationSummary[];
|
|
16
21
|
/** The id of the currently-open conversation, highlighted in the list. */
|
|
17
22
|
activeId?: string;
|
|
18
23
|
/** Controlled collapsed state. Set as a JS property (`el.collapsed = true`) to
|
|
@@ -115,7 +120,7 @@ defineWebComponent<Props, Events>('kai-conversations', {
|
|
|
115
120
|
/** Clear the internal search query (resets the list filter) and fire
|
|
116
121
|
* kai-search with an empty string. */
|
|
117
122
|
clear: () => controller?.clearSearch(),
|
|
118
|
-
/** Programmatically select a conversation by id
|
|
123
|
+
/** Programmatically select a conversation by id. The mirror of the
|
|
119
124
|
* kai-conversation-select event (a convenience over driving `activeId`). */
|
|
120
125
|
select: (id: string) => dispatch('kai-conversation-select', { id }),
|
|
121
126
|
/** Collapse the rail to its floating reopen button (fires `kai-collapse-toggle`). */
|
|
@@ -132,7 +137,7 @@ defineWebComponent<Props, Events>('kai-conversations', {
|
|
|
132
137
|
fallback={<CollapsedRail onExpand={() => setCollapsedTo(false)} />}
|
|
133
138
|
>
|
|
134
139
|
<ConversationList
|
|
135
|
-
groups={props.groups}
|
|
140
|
+
groups={props.groups ?? []}
|
|
136
141
|
conversations={allConversations()}
|
|
137
142
|
activeId={props.activeId}
|
|
138
143
|
onSelect={(id) => dispatch('kai-conversation-select', { id })}
|
|
@@ -16,6 +16,32 @@ import {
|
|
|
16
16
|
} from '../components/attachments';
|
|
17
17
|
import { actionIcon } from '../ui/action-icons';
|
|
18
18
|
import type { CustomAction } from './chat-types';
|
|
19
|
+
// The LEAF module, deliberately not the `../wire` barrel: this needs the media
|
|
20
|
+
// declaration, and importing the barrel would pull the whole stream adapter into
|
|
21
|
+
// the elements bundle for a table of strings. Sharing the module itself (rather
|
|
22
|
+
// than copying the list into the composer) is the entire point -- a second list
|
|
23
|
+
// here is the drift this design exists to prevent.
|
|
24
|
+
import { resolveMediaPolicy, type MediaTypeFilter } from '../wire/media-types';
|
|
25
|
+
// Also a leaf, and imported for the same reason: when the browser cannot name a
|
|
26
|
+
// file, the question "can this be sent?" is answered by decoding its bytes, and
|
|
27
|
+
// this module is where that decode already lives. Asking IT rather than growing
|
|
28
|
+
// a second decoder here is what keeps the composer's answer and the encoder's
|
|
29
|
+
// answer the same answer, byte for byte, instead of two that agree today.
|
|
30
|
+
import { classifyAttachment } from '../wire/files';
|
|
31
|
+
|
|
32
|
+
/** One file the composer refused, as facts rather than as a message. The kit
|
|
33
|
+
* says what happened; the application decides what the user reads. */
|
|
34
|
+
export interface RejectedAttachment {
|
|
35
|
+
filename: string;
|
|
36
|
+
/** The browser's media type for the file, or `''` when it could not tell. An
|
|
37
|
+
* empty one is not by itself why a file was rejected: an unnamed file is
|
|
38
|
+
* decided by decoding its bytes, so `''` here means the decode is what said
|
|
39
|
+
* no (binary), or that your `accept` left no text type for it to land in. */
|
|
40
|
+
mediaType: string;
|
|
41
|
+
/** `'filtered'` = this kit could have sent it, your `accept` excluded it.
|
|
42
|
+
* `'unsupported'` = no API takes this as message content at all. */
|
|
43
|
+
reason: 'filtered' | 'unsupported';
|
|
44
|
+
}
|
|
19
45
|
|
|
20
46
|
export interface DefaultPromptInputProps {
|
|
21
47
|
/** String = controlled text mirror; ComposerDoc = a seed that pre-populates pills. */
|
|
@@ -31,14 +57,27 @@ export interface DefaultPromptInputProps {
|
|
|
31
57
|
* `onAttachmentsChange` is provided (e.g. when a `+` menu already covers
|
|
32
58
|
* file-attach). Defaults to `true`. */
|
|
33
59
|
attach?: boolean;
|
|
60
|
+
/** Which attachment media types the user may stage, in HTML `accept` syntax
|
|
61
|
+
* (`'image/*,application/pdf'`). Omitted means no filter, exactly as before.
|
|
62
|
+
*
|
|
63
|
+
* NARROWED BY WHAT THE ENCODERS CAN SEND: `'image/*'` here resolves to the
|
|
64
|
+
* four image formats both APIs actually accept, not to every image type the
|
|
65
|
+
* OS will offer. It is the same string, resolved by the same function against
|
|
66
|
+
* the same declaration, as `toOpenAIMessages(msgs, { accept })` -- so a file
|
|
67
|
+
* the picker allows is a file the wire can carry. */
|
|
68
|
+
accept?: MediaTypeFilter;
|
|
69
|
+
/** Files that were dropped because `accept` excluded them. Carries the facts
|
|
70
|
+
* (name, media type, reason) and renders nothing itself: what the user should
|
|
71
|
+
* see is the application's call, not the kit's. */
|
|
72
|
+
onAttachmentsRejected?: (rejected: RejectedAttachment[]) => void;
|
|
34
73
|
/** Show a Search (Globe) button in the left toolbar; calls `onSearch`. */
|
|
35
74
|
search?: boolean;
|
|
36
75
|
/** Show a Voice (Mic) button in the left toolbar; calls `onVoice`. */
|
|
37
76
|
voice?: boolean;
|
|
38
77
|
/** Send-button visibility. `'always'` (default) always shows it; `'auto'` shows
|
|
39
|
-
* it only when there's text/attachments (an empty composer hides it
|
|
40
|
-
* still submits). To hide it entirely (Enter-only), it's pure CSS:
|
|
41
|
-
* `::part(send){display:none}
|
|
78
|
+
* it only when there's text/attachments (an empty composer hides it, though
|
|
79
|
+
* Enter still submits). To hide it entirely (Enter-only), it's pure CSS:
|
|
80
|
+
* `::part(send){display:none}`, no prop needed. Restyle via `::part(send)`.
|
|
42
81
|
* The Stop button (stoppable + loading) is unaffected. */
|
|
43
82
|
submit?: 'always' | 'auto';
|
|
44
83
|
onValueChange: (v: string) => void;
|
|
@@ -64,7 +103,23 @@ export interface DefaultPromptInputProps {
|
|
|
64
103
|
onComposerChange?: (change: ComposerChange) => void;
|
|
65
104
|
}
|
|
66
105
|
|
|
67
|
-
|
|
106
|
+
/** The staged file as a `data:` URI.
|
|
107
|
+
*
|
|
108
|
+
* NOT `URL.createObjectURL`. An object URL resolves only inside the tab that
|
|
109
|
+
* minted it, so it renders a perfect thumbnail here and is meaningless to
|
|
110
|
+
* anything downstream — `toOpenAIMessages` / `toAnthropicMessages` refuse it,
|
|
111
|
+
* and before they refused it the attachment reached the model as nothing at
|
|
112
|
+
* all. A data URI previews identically and is the one form both providers
|
|
113
|
+
* actually take. */
|
|
114
|
+
const readAsDataUrl = (file: File): Promise<string> =>
|
|
115
|
+
new Promise((resolve, reject) => {
|
|
116
|
+
const reader = new FileReader();
|
|
117
|
+
reader.onload = () => resolve(String(reader.result));
|
|
118
|
+
reader.onerror = () => reject(reader.error ?? new Error(`Could not read ${file.name}`));
|
|
119
|
+
reader.readAsDataURL(file);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
async function fileToAttachment(file: File): Promise<AttachmentData> {
|
|
68
123
|
const id =
|
|
69
124
|
typeof crypto !== 'undefined' && crypto.randomUUID
|
|
70
125
|
? crypto.randomUUID()
|
|
@@ -74,7 +129,9 @@ function fileToAttachment(file: File): AttachmentData {
|
|
|
74
129
|
type: 'file',
|
|
75
130
|
filename: file.name,
|
|
76
131
|
mediaType: file.type || undefined,
|
|
77
|
-
|
|
132
|
+
// EVERY file, not just images. A document used to get no `url` at all,
|
|
133
|
+
// which left it unencodable for exactly the same reason a blob: URL is.
|
|
134
|
+
url: await readAsDataUrl(file),
|
|
78
135
|
};
|
|
79
136
|
}
|
|
80
137
|
|
|
@@ -83,9 +140,67 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
83
140
|
const attachments = () => props.attachments ?? [];
|
|
84
141
|
const canAttach = () => !!props.onAttachmentsChange;
|
|
85
142
|
|
|
86
|
-
|
|
143
|
+
// The SAME resolver the encoders call, on the same declaration. Recomputed
|
|
144
|
+
// reactively so a host that swaps `accept` at runtime moves the picker hint
|
|
145
|
+
// and the filter together.
|
|
146
|
+
const mediaPolicy = () => resolveMediaPolicy({ accept: props.accept });
|
|
147
|
+
|
|
148
|
+
const addFiles = async (files: FileList | null) => {
|
|
87
149
|
if (!files?.length || !props.onAttachmentsChange) return;
|
|
88
|
-
|
|
150
|
+
|
|
151
|
+
// The `accept` ATTRIBUTE below is only a hint — every OS dialog offers an
|
|
152
|
+
// "All Files" escape, and it does not apply to drag-and-drop at all. So the
|
|
153
|
+
// filter that actually holds is this one, in JS, on the files as staged.
|
|
154
|
+
const policy = mediaPolicy();
|
|
155
|
+
const picked = Array.from(files);
|
|
156
|
+
// No `accept` means no filtering, which is what every existing consumer gets
|
|
157
|
+
// today. Opting in is what turns the picker into a guarantee.
|
|
158
|
+
const decisions = picked.map((file) =>
|
|
159
|
+
props.accept === undefined ? undefined : policy.decide(file.type),
|
|
160
|
+
);
|
|
161
|
+
// Read a file when its media type says yes, and ALSO when its media type
|
|
162
|
+
// says nothing at all: `undetermined` means the browser could not name it,
|
|
163
|
+
// and the only honest way to answer is to look at the bytes. Reading is the
|
|
164
|
+
// cost of that answer, so it is spent only on files that could still be
|
|
165
|
+
// staged — a file the filter already rejected is never read.
|
|
166
|
+
const needsBytes = (d: (typeof decisions)[number]): boolean =>
|
|
167
|
+
d === undefined || d.status === 'allowed' || d.status === 'undetermined';
|
|
168
|
+
// One parallel pass, index-aligned with `picked`, so the staged order is the
|
|
169
|
+
// PICK order rather than whichever file finished reading first.
|
|
170
|
+
const read = await Promise.all(
|
|
171
|
+
picked.map((file, i) => (needsBytes(decisions[i]) ? fileToAttachment(file) : undefined)),
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const staged: AttachmentData[] = [];
|
|
175
|
+
const rejected: RejectedAttachment[] = [];
|
|
176
|
+
picked.forEach((file, i) => {
|
|
177
|
+
const decision = decisions[i];
|
|
178
|
+
const attachment = read[i];
|
|
179
|
+
if (decision === undefined || decision.status === 'allowed') {
|
|
180
|
+
if (attachment) staged.push(attachment);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (decision.status === 'undetermined') {
|
|
184
|
+
// The bytes are in hand now, so the encoder can answer for real. Its
|
|
185
|
+
// verdict IS the composer's verdict: staging anything it would refuse is
|
|
186
|
+
// the exact defect (#186) this whole design exists to prevent.
|
|
187
|
+
if (attachment && classifyAttachment(attachment, policy).status === 'encodable') {
|
|
188
|
+
staged.push(attachment);
|
|
189
|
+
} else {
|
|
190
|
+
// Not `filtered`: the kit could not encode this either, so pointing
|
|
191
|
+
// the developer at their own `accept` would send them the wrong way.
|
|
192
|
+
rejected.push({ filename: file.name, mediaType: file.type, reason: 'unsupported' });
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
rejected.push({ filename: file.name, mediaType: file.type, reason: decision.status });
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (rejected.length > 0) props.onAttachmentsRejected?.(rejected);
|
|
200
|
+
if (staged.length === 0) return;
|
|
201
|
+
// Re-read `attachments()` AFTER the await — a second drop while these were
|
|
202
|
+
// being read would otherwise be overwritten by this call's stale snapshot.
|
|
203
|
+
props.onAttachmentsChange?.([...attachments(), ...staged]);
|
|
89
204
|
};
|
|
90
205
|
const removeAttachment = (id: string) =>
|
|
91
206
|
props.onAttachmentsChange?.(attachments().filter((a) => a.id !== id));
|
|
@@ -156,8 +271,16 @@ export function DefaultPromptInput(props: DefaultPromptInputProps) {
|
|
|
156
271
|
type="file"
|
|
157
272
|
multiple
|
|
158
273
|
class="hidden"
|
|
274
|
+
// Derived from the same policy as the filter above, never spelled
|
|
275
|
+
// out again. `accept="image/*"` narrows to the image formats the
|
|
276
|
+
// wire can actually carry, so the OS dialog greys out the SVG
|
|
277
|
+
// that would otherwise 400 at request time.
|
|
278
|
+
accept={props.accept === undefined ? undefined : mediaPolicy().accept}
|
|
159
279
|
onChange={(e) => {
|
|
160
|
-
|
|
280
|
+
// Reading is async now, so this is deliberately not awaited.
|
|
281
|
+
// `addFiles` captures the FileList synchronously, before its
|
|
282
|
+
// first await, so clearing the input below cannot race it.
|
|
283
|
+
void addFiles(e.currentTarget.files);
|
|
161
284
|
e.currentTarget.value = ''; // allow re-picking the same file
|
|
162
285
|
}}
|
|
163
286
|
/>
|
package/src/elements/define.tsx
CHANGED
|
@@ -96,6 +96,147 @@ function toAttr(name: string): string {
|
|
|
96
96
|
return name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Every property name the built-in element prototype chain exposes as an ACCESSOR
|
|
101
|
+
* — `role`, `hidden`, `autofocus`, `dir`, `title`, `className`, … — i.e. the
|
|
102
|
+
* reflected IDL attributes plus the handful of read-only accessors. Methods
|
|
103
|
+
* (`focus`, `append`) are plain data properties and are deliberately not included:
|
|
104
|
+
* assigning over one shadows it on the instance but destroys nothing on the DOM.
|
|
105
|
+
*
|
|
106
|
+
* DERIVED, not listed. A hand-written list is a list someone has to remember to
|
|
107
|
+
* extend: the collision set is (this element's prop names) × (whatever the browser
|
|
108
|
+
* reflects), and both sides move — the kit adds props, and the platform keeps
|
|
109
|
+
* adding globals (`inert`, `popover`, `writingSuggestions` are all recent). Reading
|
|
110
|
+
* it off the live prototype chain means a prop that starts colliding tomorrow is
|
|
111
|
+
* protected the day it lands, in whichever engines have shipped the accessor,
|
|
112
|
+
* without a code change. It also self-limits: in an engine that has NOT shipped a
|
|
113
|
+
* given accessor (jsdom has no `autofocus`) the name is simply not a collision
|
|
114
|
+
* there, which is exactly right.
|
|
115
|
+
*
|
|
116
|
+
* Computed once — the chain does not change at runtime.
|
|
117
|
+
*/
|
|
118
|
+
let globalAccessorNames: Set<string> | undefined;
|
|
119
|
+
export function reflectedGlobalPropNames(): Set<string> {
|
|
120
|
+
if (globalAccessorNames) return globalAccessorNames;
|
|
121
|
+
const names = new Set<string>();
|
|
122
|
+
let proto: object | null =
|
|
123
|
+
typeof HTMLElement === 'undefined' ? null : (HTMLElement.prototype as object);
|
|
124
|
+
while (proto && proto !== Object.prototype) {
|
|
125
|
+
for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(proto))) {
|
|
126
|
+
if (descriptor.get || descriptor.set) names.add(name);
|
|
127
|
+
}
|
|
128
|
+
proto = Object.getPrototypeOf(proto) as object | null;
|
|
129
|
+
}
|
|
130
|
+
globalAccessorNames = names;
|
|
131
|
+
return names;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Replace inherited reflected accessors with plain, non-reflecting per-instance
|
|
136
|
+
* stores on this element class's prototype. Setting the property then only STORES
|
|
137
|
+
* the value — component-register still reads it back to seed the styling prop, and
|
|
138
|
+
* later shadows this with its own (also non-reflecting) instance accessor — and it
|
|
139
|
+
* can never touch a host attribute.
|
|
140
|
+
*/
|
|
141
|
+
function installNonReflectingProps(proto: object, keys: readonly string[]): void {
|
|
142
|
+
for (const key of keys) {
|
|
143
|
+
const store = Symbol(`kai-prop:${key}`);
|
|
144
|
+
Object.defineProperty(proto, key, {
|
|
145
|
+
get(this: Record<symbol, unknown>) {
|
|
146
|
+
return this[store];
|
|
147
|
+
},
|
|
148
|
+
set(this: Record<symbol, unknown>, value: unknown) {
|
|
149
|
+
this[store] = value;
|
|
150
|
+
},
|
|
151
|
+
enumerable: true,
|
|
152
|
+
configurable: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Run `define` (solid-element's `customElement`, which calls
|
|
159
|
+
* `customElements.define` internally) with `keys` already shadowed on the element
|
|
160
|
+
* class — BEFORE the registry ever sees it.
|
|
161
|
+
*
|
|
162
|
+
* THE ORDER IS THE WHOLE POINT. `customElements.define()` synchronously upgrades
|
|
163
|
+
* every matching element the parser has already produced, and component-register's
|
|
164
|
+
* constructor runs `this[prop] = undefined` for every declared prop. If the
|
|
165
|
+
* non-reflecting accessor is installed AFTER the define call, that assignment lands
|
|
166
|
+
* on the NATIVE setter, which for a reflected IDL attribute means the author's
|
|
167
|
+
* markup is destroyed before a line of facade code runs — `undefined` coerces to
|
|
168
|
+
* `null`/`false` and the setter removes the attribute. Measured in chromium from
|
|
169
|
+
* parsed HTML (`node scripts/probe-upgrade-attribute-loss.mjs`):
|
|
170
|
+
*
|
|
171
|
+
* <kai-message role="user"> → role attr gone, el.role "assistant",
|
|
172
|
+
* the row renders as "Assistant message"
|
|
173
|
+
* <kai-resizable-item hidden> → hidden attr gone, el.hidden false, the
|
|
174
|
+
* panel is slotted and RENDERS VISIBLE
|
|
175
|
+
* <kai-confirm autofocus> → autofocus attr gone, nothing focused
|
|
176
|
+
*
|
|
177
|
+
* Two of those are silent: `hidden` and `autofocus` are legitimate global
|
|
178
|
+
* attributes, so no a11y rule and no console warning fires — the value simply
|
|
179
|
+
* evaporates. Only `role` was loud enough to get noticed.
|
|
180
|
+
*
|
|
181
|
+
* WHY A TRANSIENT REGISTRY WRAP and not something tidier: the class does not exist
|
|
182
|
+
* until `register()` builds it, and `register()` defines it in the same statement.
|
|
183
|
+
* component-register can take a `BaseElement` (a prototype we control) or a
|
|
184
|
+
* `customElements`-alike (a registry we control) via its third argument — either
|
|
185
|
+
* would be cleaner — but solid-element's `customElement()` calls `register(tag,
|
|
186
|
+
* props)` with no options and forwards nothing, and `component-register` is not a
|
|
187
|
+
* declared dependency of this package, so reaching past solid-element to call
|
|
188
|
+
* `register` ourselves would ship an undeclared import to consumers. Intercepting
|
|
189
|
+
* the one `define` call is the smallest seam that exists.
|
|
190
|
+
*
|
|
191
|
+
* The wrap is synchronous, restored in a `finally`, delegates to whatever was
|
|
192
|
+
* installed (so a polyfilled or consumer-wrapped registry keeps working), acts only
|
|
193
|
+
* on OUR tag, and is re-entrancy safe: a facade that defines another element during
|
|
194
|
+
* its upgrade nests a second wrap and unwinds it in order.
|
|
195
|
+
*/
|
|
196
|
+
function defineWithNonReflectingProps<T>(
|
|
197
|
+
tag: string,
|
|
198
|
+
keys: readonly string[],
|
|
199
|
+
define: () => T,
|
|
200
|
+
): { result: T; handled: boolean } {
|
|
201
|
+
// `handled` = nothing more for the caller to do. With no colliding props there is
|
|
202
|
+
// nothing to install at all, so that case is trivially handled.
|
|
203
|
+
if (keys.length === 0) return { result: define(), handled: true };
|
|
204
|
+
const registry = customElements;
|
|
205
|
+
const hadOwnDefine = Object.prototype.hasOwnProperty.call(registry, 'define');
|
|
206
|
+
const inner = registry.define;
|
|
207
|
+
let handled = false;
|
|
208
|
+
let restore: (() => void) | undefined;
|
|
209
|
+
try {
|
|
210
|
+
registry.define = function (
|
|
211
|
+
this: CustomElementRegistry,
|
|
212
|
+
name: string,
|
|
213
|
+
constructor: CustomElementConstructor,
|
|
214
|
+
options?: ElementDefinitionOptions,
|
|
215
|
+
) {
|
|
216
|
+
if (name === tag) {
|
|
217
|
+
installNonReflectingProps(constructor.prototype, keys);
|
|
218
|
+
handled = true;
|
|
219
|
+
}
|
|
220
|
+
return inner.call(this, name, constructor, options);
|
|
221
|
+
};
|
|
222
|
+
restore = () => {
|
|
223
|
+
if (hadOwnDefine) registry.define = inner;
|
|
224
|
+
else delete (registry as unknown as Record<string, unknown>).define;
|
|
225
|
+
};
|
|
226
|
+
} catch {
|
|
227
|
+
// A frozen registry (SES lockdown, a sealed polyfill, a hardened extension
|
|
228
|
+
// page): we cannot get ahead of define() there. Leave `handled` false so the
|
|
229
|
+
// caller installs on the returned class instead — the OLD, too-late timing, but
|
|
230
|
+
// registration itself still succeeds. Getting this wrong the other way would
|
|
231
|
+
// take all 80 elements down in that environment to fix three props in it.
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
return { result: define(), handled };
|
|
235
|
+
} finally {
|
|
236
|
+
restore?.();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
99
240
|
/** Underlying flag resolution; see `WebComponentContext.flag`. */
|
|
100
241
|
function resolveFlag(element: HTMLElement, value: unknown, attribute: string): boolean {
|
|
101
242
|
if (value === true) return true;
|
|
@@ -148,27 +289,33 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
148
289
|
}
|
|
149
290
|
}
|
|
150
291
|
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
292
|
+
// Every element gets a `theme` property/attribute. It drives a `.dark` class on
|
|
293
|
+
// an inner wrapper, which the injected kit CSS already styles — so dark mode
|
|
294
|
+
// works in standalone Shadow-DOM usage with no token duplication.
|
|
295
|
+
//
|
|
296
|
+
// The `as` annotation is load-bearing, not decoration: scripts/gen-element-api.mjs
|
|
297
|
+
// reads THIS object literal to learn which props every element gets for free, and
|
|
298
|
+
// takes each one's type and doc comment off the checker. Without it the inferred
|
|
299
|
+
// type is the literal `"auto"`. Add a universal prop here and it appears in
|
|
300
|
+
// element-meta.json, the generated .d.ts, custom-elements.json and llms-full.txt
|
|
301
|
+
// for all 79 elements, with no second list to update.
|
|
302
|
+
const defaults = {
|
|
303
|
+
/** Color mode (`auto` follows prefers-color-scheme). */
|
|
304
|
+
theme: 'auto' as 'light' | 'dark' | 'auto',
|
|
305
|
+
...propDefaults,
|
|
306
|
+
};
|
|
164
307
|
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
308
|
+
// Props that NAME a reflected global IDL accessor (today, across all 80 elements:
|
|
309
|
+
// `role` on kai-message, `hidden` on kai-resizable-item, `autofocus` on
|
|
310
|
+
// kai-confirm). Unlike RESERVED (which throws), these are legitimate domain props
|
|
311
|
+
// — `role` is the SPEAKER, `hidden` and `autofocus` mean what they say — and they
|
|
312
|
+
// stay usable, but the native accessor must not be the one that receives them.
|
|
313
|
+
// See defineWithNonReflectingProps for what the native setter does to the author's
|
|
314
|
+
// markup if it is, and why the shadowing has to be in place before the registry
|
|
315
|
+
// sees the class. RESERVED names never get here; they already threw above.
|
|
316
|
+
const shadowedProps = Object.keys(defaults).filter((key) => reflectedGlobalPropNames().has(key));
|
|
170
317
|
|
|
171
|
-
const
|
|
318
|
+
const renderFacade = (props: typeof defaults, options: { element: object }) => {
|
|
172
319
|
const element = options.element as HTMLElement;
|
|
173
320
|
let portalNode!: HTMLDivElement;
|
|
174
321
|
|
|
@@ -239,30 +386,18 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
239
386
|
</div>
|
|
240
387
|
</>
|
|
241
388
|
);
|
|
242
|
-
}
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const { result: Ctor, handled } = defineWithNonReflectingProps(tag, shadowedProps, () =>
|
|
392
|
+
customElement(tag, defaults, renderFacade),
|
|
393
|
+
);
|
|
243
394
|
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
// the
|
|
250
|
-
// shadows this with its own (also non-reflecting) per-instance accessor.
|
|
395
|
+
// Belt and braces for the one path the wrap cannot see: if `customElements.define`
|
|
396
|
+
// was never reached for this tag (a registry that defines lazily, or a future
|
|
397
|
+
// solid-element that stops calling it synchronously), install on the returned class
|
|
398
|
+
// instead. That is the OLD, too-late timing — it still protects every element
|
|
399
|
+
// constructed from here on, it just cannot rescue an already-upgraded one. Skipped
|
|
400
|
+
// in the normal case, where the accessors are already on this same prototype.
|
|
251
401
|
const proto = (Ctor as unknown as { prototype?: object } | undefined)?.prototype;
|
|
252
|
-
if (proto)
|
|
253
|
-
for (const key of NON_REFLECTING) {
|
|
254
|
-
if (!(key in defaults)) continue;
|
|
255
|
-
const store = Symbol(`kai-prop:${key}`);
|
|
256
|
-
Object.defineProperty(proto, key, {
|
|
257
|
-
get(this: Record<symbol, unknown>) {
|
|
258
|
-
return this[store];
|
|
259
|
-
},
|
|
260
|
-
set(this: Record<symbol, unknown>, value: unknown) {
|
|
261
|
-
this[store] = value;
|
|
262
|
-
},
|
|
263
|
-
enumerable: true,
|
|
264
|
-
configurable: true,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
}
|
|
402
|
+
if (!handled && proto) installNonReflectingProps(proto, shadowedProps);
|
|
268
403
|
}
|
|
@@ -3,7 +3,7 @@ import { defineWebComponent } from './define';
|
|
|
3
3
|
import { EditableLabel } from '../ui/editable-label';
|
|
4
4
|
|
|
5
5
|
interface Props extends Record<string, unknown> {
|
|
6
|
-
/** The label text
|
|
6
|
+
/** The label text. Settable and reflected to the `value` attribute. Read
|
|
7
7
|
* `el.value` for live state. */
|
|
8
8
|
value?: string;
|
|
9
9
|
/** Controlled edit state. `el.editing = true` opens the field; reflected to the
|
|
@@ -104,7 +104,22 @@ defineWebComponent<Props, Events>('kai-editable-label', {
|
|
|
104
104
|
}
|
|
105
105
|
};
|
|
106
106
|
|
|
107
|
-
expose({
|
|
107
|
+
expose({
|
|
108
|
+
/** Switch the label into its editing field, which autofocuses and selects the
|
|
109
|
+
* current text. Same entry point as a user double-click, and a no-op while
|
|
110
|
+
* `disabled`. Commit with `commit()` or by blurring, abandon with `cancel()`
|
|
111
|
+
* or Escape. */
|
|
112
|
+
edit: enter,
|
|
113
|
+
/** Close the field and keep what was typed, exactly as blurring it does.
|
|
114
|
+
* `kai-rename` fires only when the text actually changed, so committing an
|
|
115
|
+
* untouched field is silent. A no-op while the field is closed. */
|
|
116
|
+
commit,
|
|
117
|
+
/** Abandon the edit, exactly as Escape does: the original text is restored,
|
|
118
|
+
* the field closes and `kai-cancel` fires. `kai-rename` never fires, even if
|
|
119
|
+
* the field was edited. Also works when `editing` was set programmatically
|
|
120
|
+
* and the field has not rendered yet. */
|
|
121
|
+
cancel,
|
|
122
|
+
});
|
|
108
123
|
|
|
109
124
|
return (
|
|
110
125
|
<>
|