@kitn.ai/ui 0.30.0 → 0.31.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 +1 -1
- package/dist/{Icon-BzBET3E-.js → Icon-DJDuE_-x.js} +1 -1
- package/dist/action-icons-xkBBVKkF.js +1 -0
- package/dist/agent-tooling/construct/schema-url.d.ts +9 -0
- package/dist/agent-tooling/construct/schema.d.ts +76 -1
- package/dist/agent-tooling/construct/templates.d.ts +45 -0
- package/dist/arrow-down-DU6wOl-h.js +1 -0
- package/dist/arrow-left-DD46kN17.js +1 -0
- package/dist/{artifact-DGjzHYUT.js → artifact-DoUAr1Fd.js} +1 -1
- package/dist/assets/dev-DwzHsqYD.js +553 -0
- package/dist/{attachments-YvXHlg88.js → attachments-BLWPDiLH.js} +1 -1
- package/dist/{audio-visualizer-9lz5IHsL.js → audio-visualizer-DhHicYvV.js} +1 -1
- package/dist/{badge-Dh_vp8A5.js → badge-BGm7pcwi.js} +1 -1
- package/dist/{bell-CT7_WUwS.js → bell-CMEdSo7A.js} +1 -1
- package/dist/builder-app/App.d.ts +1 -0
- package/dist/builder-app/edit-guard.d.ts +8 -0
- package/dist/builder-app/main.d.ts +0 -0
- package/dist/builder-page/assets/index-C10OF-ur.js +72 -0
- package/dist/builder-page/assets/index-CFyKLBlu.css +1 -0
- package/dist/builder-page/index.html +20 -0
- package/dist/button-BM0YTTDQ.js +1 -0
- package/dist/{card-renderer-DZVaGH2e.js → card-renderer-Ds-wXc6_.js} +1 -1
- package/dist/check-BG1VfYjm.js +1 -0
- package/dist/{checkbox-group-BiryEXPQ.js → checkbox-group-CqXj8ehz.js} +1 -1
- package/dist/{checkbox-Bu2T2e0U.js → checkbox-np_5mKxM.js} +1 -1
- package/dist/chevron-down-CZ4W0hy6.js +1 -0
- package/dist/chevron-right-5gdxkCTF.js +1 -0
- package/dist/{choice-card-CM6IjkmL.js → choice-card-BYBmPAT5.js} +1 -1
- package/dist/circle-C0lwjD2A.js +1 -0
- package/dist/circle-check-BfT1cxQU.js +1 -0
- package/dist/{circle-x-Bk-dA5Wf.js → circle-x-DzUfvRqr.js} +1 -1
- package/dist/{code-block-B26t-Q7q.js → code-block-H6E5Eurc.js} +1 -1
- package/dist/{collapsible-Bhc636Cf.js → collapsible-CBq1Tj6y.js} +1 -1
- package/dist/components/builder-message-actions.d.ts +9 -18
- package/dist/components/builder-panel-derived.d.ts +18 -0
- package/dist/components/builder-panel.d.ts +51 -0
- package/dist/components/builder-start.d.ts +14 -10
- package/dist/components/chat-thread.d.ts +27 -1
- package/dist/components/construct-form-paths.d.ts +54 -0
- package/dist/components/message.d.ts +5 -0
- package/dist/{composer-DMI85B6j.js → composer-B5IvgxAF.js} +1 -1
- package/dist/{confirm-card-B0YIAwXp.js → confirm-card-B63pZIfG.js} +1 -1
- package/dist/construct-cli.es.js +469 -125
- package/dist/construct-templates.d.ts +5 -0
- package/dist/construct-templates.js +266 -0
- package/dist/construct.js +269 -86
- package/dist/{context-DWgfhqWp.js → context-Byqor78j.js} +1 -1
- package/dist/controllable-C4BSt73s.js +1 -0
- package/dist/{conversation-item-BsOsVGmT.js → conversation-item-piXZFRWa.js} +1 -1
- package/dist/{conversation-list-aX3Vtc7L.js → conversation-list-B5yu9cPg.js} +1 -1
- package/dist/{copy-BmEwsNhJ.js → copy-DfI2NYEX.js} +1 -1
- package/dist/{create-tween-CNOAU3Fx.js → create-tween-C4j96_Aj.js} +1 -1
- package/dist/{create-tween-WfqiENls.js → create-tween-CHleHI8m.js} +1 -1
- package/dist/{create-tween-wlFtW9Kh.js → create-tween-DQ6lOhdl.js} +1 -1
- package/dist/{create-tween-zt79ba32.js → create-tween-DyjTgkJi.js} +1 -1
- package/dist/custom-elements.json +40 -5
- package/dist/{default-input-DMV5Eaze.js → default-input-Bwmt8Lai.js} +1 -1
- package/dist/define-B3OpsIDE.js +1 -0
- package/dist/define.js +2 -2
- package/dist/define.server.js +2 -2
- package/dist/{disclosure-NnACBxsC.js → disclosure-ZSAlD4wg.js} +1 -1
- package/dist/{dropdown-DT7G0RGR.js → dropdown-CSRhiMN2.js} +1 -1
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/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-actions.d.ts +12 -0
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-types.d.ts +7 -2
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkbox-group.js +1 -1
- package/dist/elements/checkbox.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-item.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.js +1 -1
- package/dist/elements/dropdown.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-grid.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/radio-group.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +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/select.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/slider.js +1 -1
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +22 -10
- package/dist/{ellipsis-6pJ7SxYT.js → ellipsis-CJ5QIjWo.js} +1 -1
- package/dist/{embed-BMje-nxj.js → embed-6Vji0PfS.js} +1 -1
- package/dist/{external-link-Beh9DKeR.js → external-link-BPTtTdet.js} +1 -1
- package/dist/{file-text-D1GPfZ8Q.js → file-text-eVjcFDrK.js} +1 -1
- package/dist/{file-tree-Dwj2lqg-.js → file-tree-a9tfWYmg.js} +1 -1
- package/dist/{folder-b6D8x1DY.js → folder-DN6Ilq6d.js} +1 -1
- package/dist/{form-DWgdxDXu.js → form-U1cwhcQx.js} +1 -1
- package/dist/{hover-card-CWRkqm40.js → hover-card-C5ptzZOZ.js} +1 -1
- package/dist/{icon-CL0jdhsf.js → icon-BOMrFp0D.js} +1 -1
- package/dist/index.js +1991 -1989
- package/dist/index.server.js +1886 -1884
- package/dist/{info-0cOUherQ.js → info-DxzKsIHn.js} +1 -1
- package/dist/{input-BH6Ru91Q.js → input-aW72PACH.js} +1 -1
- package/dist/kai.es.js +1 -1
- package/dist/{kbd-DRAnVtNX.js → kbd-BP-6MwUs.js} +1 -1
- package/dist/{link-preview-DODgRyV9.js → link-preview-CZaDilMG.js} +1 -1
- package/dist/{link-Dlr0vssB.js → link-rlN11N0B.js} +1 -1
- package/dist/{loader-KvJB6NrT.js → loader-BdFK70fF.js} +1 -1
- package/dist/{markdown-DX9tuJVj.js → markdown-DjEaoNz-.js} +1 -1
- package/dist/mcp.es.js +579 -107
- package/dist/message--Nlbemtp.js +1 -0
- package/dist/message-C88yTu2A.js +1 -0
- package/dist/message-circle-CZeVDN1f.js +1 -0
- package/dist/message-square-BXNBKC6i.js +1 -0
- package/dist/{minimize-2-CyOHJwcN.js → minimize-2-BxLOOSEf.js} +1 -1
- package/dist/{model-switcher-0hrKDiK2.js → model-switcher-BxRvub2A.js} +1 -1
- package/dist/{overlay-DBMFXie6.js → overlay-CDwKJtHN.js} +1 -1
- package/dist/{panel-right-t8SWJL6e.js → panel-right-BovedCwW.js} +1 -1
- package/dist/{paperclip-CdzuOjbU.js → paperclip-D0Xyjja2.js} +1 -1
- package/dist/play-DGJ4MgB2.js +1 -0
- package/dist/primitives/speech.d.ts +35 -0
- package/dist/{progress-bar-CiBtxMWT.js → progress-bar-D-aQfy5h.js} +1 -1
- package/dist/{prompt-suggestion-I_DHP21i.js → prompt-suggestion-C6qLjMEQ.js} +1 -1
- package/dist/{radio-C_p3n39l.js → radio-D1CkXISc.js} +1 -1
- package/dist/react/index.d.ts +22 -6
- package/dist/react.js +13 -13
- package/dist/{reasoning-BDwy_tIe.js → reasoning-DARmbgmE.js} +1 -1
- package/dist/register-impl-WcrdIihK.js +424 -0
- package/dist/{resizable-SYC-fStM.js → resizable-Da6oSO2z.js} +1 -1
- package/dist/{rotate-ccw-B1nwmwuY.js → rotate-ccw-31ed1xK_.js} +1 -1
- package/dist/{rotate-cw-CLAnBcm1.js → rotate-cw-qkl39dw3.js} +1 -1
- package/dist/{scroll-area-zRyXoLhI.js → scroll-area-RrKnLYlA.js} +1 -1
- package/dist/{scroll-button-jzrbYCG9.js → scroll-button--QeNjobK.js} +1 -1
- package/dist/{select-DM-0nEe0.js → select-CS_0mWW3.js} +1 -1
- package/dist/{separator-BToOOoOt.js → separator-fT-zBypW.js} +1 -1
- package/dist/{settings-PaGo_S4U.js → settings-dfkV_WDm.js} +1 -1
- package/dist/{settings-group-BFXGIfXq.js → settings-group-CX694npG.js} +1 -1
- package/dist/{skeleton-DYpmFnnm.js → skeleton-BsqKD9TV.js} +1 -1
- package/dist/{slider-ebutAmkQ.js → slider-_EWfTH9H.js} +1 -1
- package/dist/{solid-15v_3WVL.js → solid-BITWG_VE.js} +3073 -3006
- package/dist/{solid-5P-otgdi.js → solid-CRFYWlBw.js} +3865 -3797
- package/dist/solid.js +1 -1
- package/dist/solid.server.js +1 -1
- package/dist/{source-c1gpRl9h.js → source-Da1TFiuj.js} +1 -1
- package/dist/speech-CjbWuqG2.js +1 -0
- package/dist/{star-pcV_3LqF.js → star-ZvSbbXS6.js} +1 -1
- package/dist/{store-CSgbFFCg.js → store-DE5lcr9q.js} +1 -1
- package/dist/{switch-Bh2OdLV2.js → switch-P_fsHqv3.js} +1 -1
- package/dist/{tasks-card-CbqQAn2S.js → tasks-card-B86qqyZm.js} +1 -1
- package/dist/{text-shimmer-BSzgEnDH.js → text-shimmer-BAkw_65G.js} +1 -1
- package/dist/{textarea-DrYNt_0z.js → textarea-Bkinatpi.js} +1 -1
- package/dist/{thumbs-up-BL3-NenK.js → thumbs-up-CDGW7a4o.js} +1 -1
- package/dist/{toast-store-DkOlYczt.js → toast-store-B94I50AO.js} +1 -1
- package/dist/{tool-CeIWUVUe.js → tool-CUiCeyi1.js} +1 -1
- package/dist/{tooltip-GA-nmg06.js → tooltip-B3wlQSPh.js} +1 -1
- package/dist/{trash-2-CT66QWlm.js → trash-2-Cu8yZmFQ.js} +1 -1
- package/dist/{triangle-alert-CeHLWxhD.js → triangle-alert-CCtDUUmn.js} +1 -1
- package/dist/ui/button-variant-names.d.ts +12 -0
- package/dist/ui/button.d.ts +6 -0
- package/dist/{upload-9Csb0F0J.js → upload-CxotJlLv.js} +1 -1
- package/dist/{use-card-resolution-Zg0a08Z-.js → use-card-resolution-DILUvNyj.js} +1 -1
- package/dist/{variant-aurora-4VBQHIKV.js → variant-aurora-BGaXBBGf.js} +1 -1
- package/dist/{variant-aurora-LE3fqxgw.js → variant-aurora-BLcQmvkd.js} +1 -1
- package/dist/{variant-aurora-DBeCAQNk.js → variant-aurora-DZIfAtx1.js} +2 -2
- package/dist/{variant-aurora-FzHVXHby.js → variant-aurora-DpoIW4ZX.js} +2 -2
- package/dist/{variant-custom-DI5R0rln.js → variant-custom-BhtWdAxE.js} +1 -1
- package/dist/{variant-custom-WWnrrRHS.js → variant-custom-BtApuSyr.js} +2 -2
- package/dist/{variant-custom-BQkNuPpE.js → variant-custom-C-zS4ZtE.js} +2 -2
- package/dist/{variant-custom-AFRGcOBS.js → variant-custom-swi9PXxU.js} +1 -1
- package/dist/{variant-wave-wTP5v_jU.js → variant-wave-7JCIwhpb.js} +1 -1
- package/dist/{variant-wave-nSI2sfII.js → variant-wave-BG4nGr6x.js} +1 -1
- package/dist/{variant-wave-DuQV7DhQ.js → variant-wave-C32lM9G2.js} +2 -2
- package/dist/{variant-wave-DUCbH5oI.js → variant-wave-Dl6jOiK3.js} +2 -2
- package/dist/x-C4niw05N.js +1 -0
- package/frameworks/react/index.tsx +12 -6
- package/llms-full.txt +9 -6
- package/llms.txt +1 -1
- package/package.json +7 -3
- package/solid.css +17 -2
- package/src/elements/element-meta.json +39 -14
- package/dist/action-icons-DaW2pU6m.js +0 -1
- package/dist/arrow-down-D783iMJj.js +0 -1
- package/dist/arrow-left-0VLM5kyg.js +0 -1
- package/dist/assets/dev-5XzXYR21.js +0 -91
- package/dist/button-C_3te6fm.js +0 -1
- package/dist/check-CiEOVnEN.js +0 -1
- package/dist/chevron-down-CSobuZRG.js +0 -1
- package/dist/chevron-right-DR9ZAP-2.js +0 -1
- package/dist/circle-BC66EYF7.js +0 -1
- package/dist/circle-check-BqmahW0O.js +0 -1
- package/dist/controllable-BagRpyHv.js +0 -1
- package/dist/define-CI0y3eE4.js +0 -1
- package/dist/message-BqQ7Ot5q.js +0 -1
- package/dist/message-DkaGfBd8.js +0 -1
- package/dist/message-circle-BPpp5QKv.js +0 -1
- package/dist/message-square-BLbsd9BJ.js +0 -1
- package/dist/play-BXy1u7rW.js +0 -1
- package/dist/register-impl-DvEnhIxx.js +0 -424
- package/dist/x-eiO9V6J6.js +0 -1
package/dist/construct-cli.es.js
CHANGED
|
@@ -15,7 +15,14 @@ function isSafeUrl(url) {
|
|
|
15
15
|
const scheme = schemeOf(url);
|
|
16
16
|
return scheme !== void 0 && SAFE_SCHEMES.includes(scheme);
|
|
17
17
|
}
|
|
18
|
+
const CHAT_MESSAGE_ACTIONS = ["copy", "like", "dislike", "regenerate", "edit", "speak"];
|
|
19
|
+
const BUTTON_VARIANT_NAMES = ["default", "ghost", "subtle", "outline", "destructive"];
|
|
18
20
|
const TAG_RE = /^[a-z][a-z0-9]*-[a-z0-9-]+$/;
|
|
21
|
+
const TriggerEntrySchema = z.object({
|
|
22
|
+
id: z.string().min(1),
|
|
23
|
+
label: z.string().min(1),
|
|
24
|
+
description: z.string().min(1).optional()
|
|
25
|
+
}).strict();
|
|
19
26
|
const ProviderSchema = z.discriminatedUnion("mode", [
|
|
20
27
|
z.object({ mode: z.literal("mock") }).strict(),
|
|
21
28
|
z.object({
|
|
@@ -84,7 +91,26 @@ const ConstructSchema = z.object({
|
|
|
84
91
|
/** Rendered in ChatThread's built-in header bar (left side). Construct-
|
|
85
92
|
* authored/untrusted text, like theme.accent/provider.url — JSON.stringify'd
|
|
86
93
|
* at its one emit site, never a raw JSX attribute string. */
|
|
87
|
-
title: z.string().min(1).optional()
|
|
94
|
+
title: z.string().min(1).optional(),
|
|
95
|
+
/** Renders a theme-toggle Button in ChatThread's header-end region,
|
|
96
|
+
* flipping the host's `theme` attribute (the attribute
|
|
97
|
+
* defineWebComponent already owns) via the facade's ctx.element —
|
|
98
|
+
* codegen work, no new kit surface (B-10). */
|
|
99
|
+
themeToggle: z.boolean().optional(),
|
|
100
|
+
/** Header action buttons (label + kit Button variant), rendered in
|
|
101
|
+
* the header-end region. Vocabulary-never-logic: the construct
|
|
102
|
+
* cannot say what an action DOES, so each click dispatches a
|
|
103
|
+
* non-bubbling `kai-header-action` CustomEvent on the host with
|
|
104
|
+
* `detail: { label }` — the consumer's listening seam (B-10).
|
|
105
|
+
* `variant` derives from the kit Button's own list
|
|
106
|
+
* (BUTTON_VARIANT_NAMES — B-6a), never restated. `label` is
|
|
107
|
+
* construct-authored untrusted text, JSON.stringify'd at emit. */
|
|
108
|
+
actions: z.array(
|
|
109
|
+
z.object({
|
|
110
|
+
label: z.string().min(1),
|
|
111
|
+
variant: z.enum(BUTTON_VARIANT_NAMES).optional()
|
|
112
|
+
}).strict()
|
|
113
|
+
).min(1).optional()
|
|
88
114
|
}).strict().optional(),
|
|
89
115
|
/** Greeting shown while the thread is empty (no messages yet) — the
|
|
90
116
|
* proven "welcome screen" pattern (Intercom-class): title + optional
|
|
@@ -193,7 +219,28 @@ const ConstructSchema = z.object({
|
|
|
193
219
|
* vocabulary; HOW it persists (the ConversationStore adapter,
|
|
194
220
|
* localStorage vs. a fetch endpoint) is codegen's call, never
|
|
195
221
|
* vocabulary here (C-3 — no transport-layer vocabulary). */
|
|
196
|
-
conversations: z.literal(true).optional()
|
|
222
|
+
conversations: z.literal(true).optional(),
|
|
223
|
+
/** Role-scoped default action bars, threaded onto ChatThread's
|
|
224
|
+
* `userActions`/`assistantActions` props (B-3/B-7b). Ordered
|
|
225
|
+
* arrays; enum ids ONLY, read off the ONE const
|
|
226
|
+
* (CHAT_MESSAGE_ACTIONS — B-6): a CustomAction is an id the APP
|
|
227
|
+
* must handle, a construct has no app code, so emitting one is a
|
|
228
|
+
* dead affordance. Duplicates within one array rejected below
|
|
229
|
+
* (superRefine, the slots pattern). min(1): an empty list IS the
|
|
230
|
+
* absent key. */
|
|
231
|
+
messageActions: z.object({
|
|
232
|
+
user: z.array(z.enum(CHAT_MESSAGE_ACTIONS)).min(1).optional(),
|
|
233
|
+
assistant: z.array(z.enum(CHAT_MESSAGE_ACTIONS)).min(1).optional()
|
|
234
|
+
}).strict().optional(),
|
|
235
|
+
/** The citations STRIP (the `part="citations"` row consecutive
|
|
236
|
+
* `source` parts already collapse into — message.tsx). `strip:
|
|
237
|
+
* false` hides it (emits ChatThread's `hideSources`); `strip:
|
|
238
|
+
* true` or the key absent emits NOTHING — the row already renders,
|
|
239
|
+
* the kit default IS the on state, the same anchored-on-the-
|
|
240
|
+
* default convention as `reasoning: 'full'` (B-4). */
|
|
241
|
+
sources: z.object({
|
|
242
|
+
strip: z.boolean().optional()
|
|
243
|
+
}).strict().optional()
|
|
197
244
|
}).strict().optional(),
|
|
198
245
|
/** Named generative-UI card definitions the model can emit as tool calls,
|
|
199
246
|
* rendered in the thread. Top-level (not a capability): a card is
|
|
@@ -240,109 +287,245 @@ const ConstructSchema = z.object({
|
|
|
240
287
|
/** Seed Dock's open state at mount. Uncontrolled — never steals focus (Dock's
|
|
241
288
|
* own focus contract, dock.tsx). Omitted keeps Dock's own default (closed). */
|
|
242
289
|
defaultOpen: z.boolean().optional()
|
|
290
|
+
}).strict().optional(),
|
|
291
|
+
/** Layout-scoped aside geometry, `layout: 'aside'` only (superRefine
|
|
292
|
+
* below, mirroring `widget`'s scoping exactly). `position` picks the
|
|
293
|
+
* docked inline edge (default 'end', today's hardcoded behavior);
|
|
294
|
+
* `width` overrides codegen's 380px default. `width` is construct-
|
|
295
|
+
* authored untrusted text: it lands as a JSON.stringify'd VALUE inside
|
|
296
|
+
* the emitted Solid `style={{ }}` object — property assignment, the
|
|
297
|
+
* same no-CSS-text-interpolation guarantee as setProperty — never
|
|
298
|
+
* concatenated into a CSS string (B-2). */
|
|
299
|
+
aside: z.object({
|
|
300
|
+
position: z.enum(["start", "end"]).optional(),
|
|
301
|
+
width: z.string().min(1).optional()
|
|
302
|
+
}).strict().optional(),
|
|
303
|
+
/** Construct-wide shell chrome (10a): both members reuse REAL kit
|
|
304
|
+
* pieces — command.tsx's CommandList behind a codegen-emitted overlay
|
|
305
|
+
* (opened on Mod+K; entries DERIVE from what this construct enables —
|
|
306
|
+
* menu-honesty against dead entries), and the documented Dropdown+
|
|
307
|
+
* Avatar user-menu recipe. `name`/`plan` are construct-authored
|
|
308
|
+
* untrusted text, JSON.stringify'd at emit like every sibling.
|
|
309
|
+
* `commandPalette` is presence-only `z.literal(true)`, matching
|
|
310
|
+
* `conversations`' pattern. */
|
|
311
|
+
shell: z.object({
|
|
312
|
+
commandPalette: z.literal(true).optional(),
|
|
313
|
+
userMenu: z.object({
|
|
314
|
+
name: z.string().min(1),
|
|
315
|
+
plan: z.string().min(1).optional()
|
|
316
|
+
}).strict().optional()
|
|
317
|
+
}).strict().optional(),
|
|
318
|
+
/** Composer chrome — NOT a capability: chrome on the medium, like
|
|
319
|
+
* `header` (B-5). `triggers` maps onto ChatThread's real, shipped
|
|
320
|
+
* `triggers` prop: `slash` → `{ char: '/', kind: 'command', items }`,
|
|
321
|
+
* `mention` → `{ char: '@', kind: 'mention', items }` at emit. */
|
|
322
|
+
composer: z.object({
|
|
323
|
+
triggers: z.object({
|
|
324
|
+
slash: z.array(TriggerEntrySchema).min(1).optional(),
|
|
325
|
+
mention: z.array(TriggerEntrySchema).min(1).optional()
|
|
326
|
+
}).strict().optional()
|
|
243
327
|
}).strict().optional()
|
|
244
328
|
}).strict().superRefine((construct, ctx) => {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
329
|
+
for (const rule of CROSS_FIELD_RULES) rule.check(construct, ctx);
|
|
330
|
+
});
|
|
331
|
+
const CROSS_FIELD_RULES = [
|
|
332
|
+
{
|
|
333
|
+
id: "slots-unique",
|
|
334
|
+
paths: ["slots"],
|
|
335
|
+
check: (construct, ctx) => {
|
|
336
|
+
if (!construct.slots) return;
|
|
337
|
+
const seen = /* @__PURE__ */ new Set();
|
|
338
|
+
construct.slots.forEach((name, i) => {
|
|
339
|
+
if (seen.has(name)) {
|
|
340
|
+
ctx.addIssue({
|
|
341
|
+
code: z.ZodIssueCode.custom,
|
|
342
|
+
path: ["slots", i],
|
|
343
|
+
message: `duplicate slot name "${name}"`
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
seen.add(name);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
id: "custom-layout-needs-slots",
|
|
352
|
+
paths: ["layout", "slots"],
|
|
353
|
+
check: (construct, ctx) => {
|
|
354
|
+
if (construct.layout === "custom" && (!construct.slots || construct.slots.length === 0)) {
|
|
249
355
|
ctx.addIssue({
|
|
250
356
|
code: z.ZodIssueCode.custom,
|
|
251
|
-
path: ["slots"
|
|
252
|
-
message:
|
|
357
|
+
path: ["slots"],
|
|
358
|
+
message: '"custom" layout requires at least one declared slot — custom IS the slots grain'
|
|
253
359
|
});
|
|
254
360
|
}
|
|
255
|
-
seen.add(name);
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (construct.layout === "custom" && (!construct.slots || construct.slots.length === 0)) {
|
|
259
|
-
ctx.addIssue({
|
|
260
|
-
code: z.ZodIssueCode.custom,
|
|
261
|
-
path: ["slots"],
|
|
262
|
-
message: '"custom" layout requires at least one declared slot — custom IS the slots grain'
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
if (construct.layout === "split" && construct.slots) {
|
|
266
|
-
const i = construct.slots.indexOf("pane");
|
|
267
|
-
if (i !== -1) {
|
|
268
|
-
ctx.addIssue({
|
|
269
|
-
code: z.ZodIssueCode.custom,
|
|
270
|
-
path: ["slots", i],
|
|
271
|
-
message: `"pane" collides with the "split" layout's own fixed <slot name="pane"> — choose a different slot name`
|
|
272
|
-
});
|
|
273
361
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (construct.empty?.icon && !isSafeUrl(construct.empty.icon)) {
|
|
290
|
-
ctx.addIssue({
|
|
291
|
-
code: z.ZodIssueCode.custom,
|
|
292
|
-
path: ["empty", "icon"],
|
|
293
|
-
message: "icon must be an http(s)/mailto or relative URL — no javascript:/data: schemes"
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
const reasoning = construct.capabilities?.reasoning;
|
|
297
|
-
const reasoningOpen = construct.capabilities?.reasoningOpen;
|
|
298
|
-
if (reasoningOpen !== void 0 && (reasoning === "compact" || reasoning === "off")) {
|
|
299
|
-
ctx.addIssue({
|
|
300
|
-
code: z.ZodIssueCode.custom,
|
|
301
|
-
path: ["capabilities", "reasoningOpen"],
|
|
302
|
-
message: '"reasoningOpen" only applies when reasoning is "full" or omitted — "compact"/"off" have no disclosure to open'
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
const history = construct.capabilities?.history;
|
|
306
|
-
if (construct.capabilities?.conversations && (!history || history.persistence === "none")) {
|
|
307
|
-
ctx.addIssue({
|
|
308
|
-
code: z.ZodIssueCode.custom,
|
|
309
|
-
path: ["capabilities", "conversations"],
|
|
310
|
-
message: '"conversations" requires capabilities.history.persistence to be "local" or "endpoint" — a conversation list needs somewhere to persist conversations'
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
const URL_SHAPED = /^[a-zA-Z][a-zA-Z0-9+.-]*:|^\/\//;
|
|
314
|
-
for (const [i, link] of (construct.home?.links ?? []).entries()) {
|
|
315
|
-
if (link.href && !isSafeUrl(link.href)) {
|
|
316
|
-
ctx.addIssue({
|
|
317
|
-
code: z.ZodIssueCode.custom,
|
|
318
|
-
path: ["home", "links", i, "href"],
|
|
319
|
-
message: "href must be an http(s)/mailto or relative URL — no javascript:/data: schemes"
|
|
320
|
-
});
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: "split-pane-slot-collision",
|
|
365
|
+
paths: ["layout", "slots"],
|
|
366
|
+
check: (construct, ctx) => {
|
|
367
|
+
if (construct.layout === "split" && construct.slots) {
|
|
368
|
+
const i = construct.slots.indexOf("pane");
|
|
369
|
+
if (i !== -1) {
|
|
370
|
+
ctx.addIssue({
|
|
371
|
+
code: z.ZodIssueCode.custom,
|
|
372
|
+
path: ["slots", i],
|
|
373
|
+
message: `"pane" collides with the "split" layout's own fixed <slot name="pane"> — choose a different slot name`
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
321
377
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
id: "widget-layout-scope",
|
|
381
|
+
paths: ["layout", "widget"],
|
|
382
|
+
check: (construct, ctx) => {
|
|
383
|
+
if (construct.widget && construct.layout !== "widget") {
|
|
384
|
+
ctx.addIssue({
|
|
385
|
+
code: z.ZodIssueCode.custom,
|
|
386
|
+
path: ["widget"],
|
|
387
|
+
message: '"widget" is only valid on layout: "widget"'
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
id: "aside-layout-scope",
|
|
394
|
+
paths: ["layout", "aside"],
|
|
395
|
+
check: (construct, ctx) => {
|
|
396
|
+
if (construct.aside && construct.layout !== "aside") {
|
|
397
|
+
ctx.addIssue({
|
|
398
|
+
code: z.ZodIssueCode.custom,
|
|
399
|
+
path: ["aside"],
|
|
400
|
+
message: '"aside" is only valid on layout: "aside"'
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: "message-actions-unique",
|
|
407
|
+
paths: ["capabilities.messageActions.user", "capabilities.messageActions.assistant"],
|
|
408
|
+
check: (construct, ctx) => {
|
|
409
|
+
const messageActions = construct.capabilities?.messageActions;
|
|
410
|
+
if (!messageActions) return;
|
|
411
|
+
for (const role of ["user", "assistant"]) {
|
|
412
|
+
const list = messageActions[role];
|
|
413
|
+
if (!list) continue;
|
|
414
|
+
const seen = /* @__PURE__ */ new Set();
|
|
415
|
+
list.forEach((id, i) => {
|
|
416
|
+
if (seen.has(id)) {
|
|
417
|
+
ctx.addIssue({
|
|
418
|
+
code: z.ZodIssueCode.custom,
|
|
419
|
+
path: ["capabilities", "messageActions", role, i],
|
|
420
|
+
message: `duplicate action id "${id}"`
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
seen.add(id);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
id: "launcher-icon-url",
|
|
430
|
+
paths: ["widget.launcherIcon"],
|
|
431
|
+
check: (construct, ctx) => {
|
|
432
|
+
if (construct.widget?.launcherIcon && !isSafeUrl(construct.widget.launcherIcon)) {
|
|
433
|
+
ctx.addIssue({
|
|
434
|
+
code: z.ZodIssueCode.custom,
|
|
435
|
+
path: ["widget", "launcherIcon"],
|
|
436
|
+
message: "launcherIcon must be an http(s)/mailto or relative URL — no javascript:/data: schemes"
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
id: "empty-icon-url",
|
|
443
|
+
paths: ["empty.icon"],
|
|
444
|
+
check: (construct, ctx) => {
|
|
445
|
+
if (construct.empty?.icon && !isSafeUrl(construct.empty.icon)) {
|
|
446
|
+
ctx.addIssue({
|
|
447
|
+
code: z.ZodIssueCode.custom,
|
|
448
|
+
path: ["empty", "icon"],
|
|
449
|
+
message: "icon must be an http(s)/mailto or relative URL — no javascript:/data: schemes"
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
id: "reasoning-open-scope",
|
|
456
|
+
paths: ["capabilities.reasoning", "capabilities.reasoningOpen"],
|
|
457
|
+
check: (construct, ctx) => {
|
|
458
|
+
const reasoning = construct.capabilities?.reasoning;
|
|
459
|
+
const reasoningOpen = construct.capabilities?.reasoningOpen;
|
|
460
|
+
if (reasoningOpen !== void 0 && (reasoning === "compact" || reasoning === "off")) {
|
|
461
|
+
ctx.addIssue({
|
|
462
|
+
code: z.ZodIssueCode.custom,
|
|
463
|
+
path: ["capabilities", "reasoningOpen"],
|
|
464
|
+
message: '"reasoningOpen" only applies when reasoning is "full" or omitted — "compact"/"off" have no disclosure to open'
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
id: "conversations-need-history",
|
|
471
|
+
paths: ["capabilities.conversations", "capabilities.history"],
|
|
472
|
+
check: (construct, ctx) => {
|
|
473
|
+
const history = construct.capabilities?.history;
|
|
474
|
+
if (construct.capabilities?.conversations && (!history || history.persistence === "none")) {
|
|
475
|
+
ctx.addIssue({
|
|
476
|
+
code: z.ZodIssueCode.custom,
|
|
477
|
+
path: ["capabilities", "conversations"],
|
|
478
|
+
message: '"conversations" requires capabilities.history.persistence to be "local" or "endpoint" — a conversation list needs somewhere to persist conversations'
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
id: "home-link-urls",
|
|
485
|
+
paths: ["home.links"],
|
|
486
|
+
check: (construct, ctx) => {
|
|
487
|
+
const URL_SHAPED = /^[a-zA-Z][a-zA-Z0-9+.-]*:|^\/\//;
|
|
488
|
+
for (const [i, link] of (construct.home?.links ?? []).entries()) {
|
|
489
|
+
if (link.href && !isSafeUrl(link.href)) {
|
|
490
|
+
ctx.addIssue({
|
|
491
|
+
code: z.ZodIssueCode.custom,
|
|
492
|
+
path: ["home", "links", i, "href"],
|
|
493
|
+
message: "href must be an http(s)/mailto or relative URL — no javascript:/data: schemes"
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
if (link.icon && URL_SHAPED.test(link.icon) && !isSafeUrl(link.icon)) {
|
|
497
|
+
ctx.addIssue({
|
|
498
|
+
code: z.ZodIssueCode.custom,
|
|
499
|
+
path: ["home", "links", i, "icon"],
|
|
500
|
+
message: "icon must be a kit icon name or an http(s)/relative URL — no javascript:/data: schemes"
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
id: "history-endpoint-url",
|
|
508
|
+
paths: ["capabilities.history.persistence", "capabilities.history.url"],
|
|
509
|
+
check: (construct, ctx) => {
|
|
510
|
+
const history = construct.capabilities?.history;
|
|
511
|
+
if (!history) return;
|
|
512
|
+
if (history.persistence === "endpoint" && !history.url) {
|
|
513
|
+
ctx.addIssue({
|
|
514
|
+
code: z.ZodIssueCode.custom,
|
|
515
|
+
path: ["capabilities", "history", "url"],
|
|
516
|
+
message: '"endpoint" persistence requires a url'
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (history.persistence !== "endpoint" && history.url !== void 0) {
|
|
520
|
+
ctx.addIssue({
|
|
521
|
+
code: z.ZodIssueCode.custom,
|
|
522
|
+
path: ["capabilities", "history", "url"],
|
|
523
|
+
message: 'url is only valid with "endpoint" persistence'
|
|
524
|
+
});
|
|
525
|
+
}
|
|
328
526
|
}
|
|
329
527
|
}
|
|
330
|
-
|
|
331
|
-
if (history.persistence === "endpoint" && !history.url) {
|
|
332
|
-
ctx.addIssue({
|
|
333
|
-
code: z.ZodIssueCode.custom,
|
|
334
|
-
path: ["capabilities", "history", "url"],
|
|
335
|
-
message: '"endpoint" persistence requires a url'
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
if (history.persistence !== "endpoint" && history.url !== void 0) {
|
|
339
|
-
ctx.addIssue({
|
|
340
|
-
code: z.ZodIssueCode.custom,
|
|
341
|
-
path: ["capabilities", "history", "url"],
|
|
342
|
-
message: 'url is only valid with "endpoint" persistence'
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
});
|
|
528
|
+
];
|
|
346
529
|
function validateConstruct(input) {
|
|
347
530
|
const parsed = ConstructSchema.safeParse(input);
|
|
348
531
|
if (parsed.success) return { ok: true, construct: parsed.data };
|
|
@@ -639,8 +822,12 @@ function emitIndexHtml(c) {
|
|
|
639
822
|
function emitElement(c) {
|
|
640
823
|
const accent = c.theme?.accent;
|
|
641
824
|
const unreadColor = c.theme?.unreadColor;
|
|
825
|
+
const usesCtx = !!accent || !!unreadColor || needsHost(c);
|
|
826
|
+
const appJsx = needsHost(c) ? "<App host={ctx.element} />" : "<App />";
|
|
642
827
|
if (!accent && !unreadColor) {
|
|
643
|
-
const facade2 =
|
|
828
|
+
const facade2 = usesCtx ? `(_props, ctx) => {
|
|
829
|
+
return ${appJsx};
|
|
830
|
+
}` : "() => <App />";
|
|
644
831
|
return `import { defineWebComponent } from '@kitn.ai/ui/define';
|
|
645
832
|
import { App } from './App';
|
|
646
833
|
|
|
@@ -653,7 +840,7 @@ defineWebComponent('${c.name}', { theme: '${themeMode(c)}' as 'light' | 'dark' |
|
|
|
653
840
|
ctx.element.style.setProperty('--kai-color-unread', ${JSON.stringify(unreadColor)});` : "";
|
|
654
841
|
if (!accent) {
|
|
655
842
|
const facade2 = `(_props, ctx) => {${unreadColorSetProperty}
|
|
656
|
-
return
|
|
843
|
+
return ${appJsx};
|
|
657
844
|
}`;
|
|
658
845
|
return `import { defineWebComponent } from '@kitn.ai/ui/define';
|
|
659
846
|
import { App } from './App';
|
|
@@ -682,7 +869,7 @@ defineWebComponent('${c.name}', { theme: '${themeMode(c)}' as 'light' | 'dark' |
|
|
|
682
869
|
return (
|
|
683
870
|
<>
|
|
684
871
|
<style>{${JSON.stringify(styleText)}}</style>
|
|
685
|
-
|
|
872
|
+
${appJsx}
|
|
686
873
|
</>
|
|
687
874
|
);
|
|
688
875
|
}`;
|
|
@@ -696,7 +883,7 @@ defineWebComponent('${c.name}', { theme: '${themeMode(c)}' as 'light' | 'dark' |
|
|
|
696
883
|
}
|
|
697
884
|
function emitApp(c) {
|
|
698
885
|
if (c.layout === "custom") return emitCustomApp(c);
|
|
699
|
-
return `${
|
|
886
|
+
return `${emitSolidJsImports(c)}import { ChatThread, createKaiChat${emitLayoutImport(c)}${emitCardComponentImport(c)}${emitEmptyComponentImport(c)}${emitChromeImports(c)} } from '@kitn.ai/ui/solid';
|
|
700
887
|
import type { AttachmentData${emitHistoryTypeImport(c)}${emitConversationsResetTypeImport(c)} } from '@kitn.ai/ui/solid';
|
|
701
888
|
${emitProviderImports(c)}
|
|
702
889
|
${emitCardsImport(c)}
|
|
@@ -786,10 +973,10 @@ ${emitHistorySetup(c)}
|
|
|
786
973
|
// list view is open must not leave it there for the next open — see
|
|
787
974
|
// widgetHasConversationsChrome/emitDockOnOpenChangeProp's docs. Wired only
|
|
788
975
|
// for \`widget\`, the one layout with something that closes/reopens at all.
|
|
789
|
-
export function App() {
|
|
790
|
-
${emitDockCloseVar(c, " ")}${emitChatControllerVar(c, " ")}${emitConversationsSignalsVar(c, " ")} return (
|
|
791
|
-
${emitLayoutOpen(c)}${emitSlots(c.slots, " ")} <ChatThread messages={chat.messages()} loading={chat.loading()} placeholder="Ask anything" onSubmit={submit} webSearch={false} voice={false}${emitHeaderProp(c)}${emitHeaderEndContentProp(c)}${emitAttachProps(c)}${emitStartersProp(c)}${emitReasoningProp(c)}${emitReasoningOpenProp(c)}${emitEmptyContentProp(c)}${emitCardTypesProp(c)}${emitHomeProp(c)}${emitConversationsProps(c)}${emitChatControllerRefProp(c)}${emitChatThreadUnreadProps(c)} />
|
|
792
|
-
${emitLayoutClose(c)} );
|
|
976
|
+
${emitChromeComment(c)}export function App(${needsHost(c) ? "props: { host: HTMLElement }" : ""}) {
|
|
977
|
+
${emitToggleThemeVar(c, " ")}${emitDockCloseVar(c, " ")}${emitChatControllerVar(c, " ")}${emitConversationsSignalsVar(c, " ")}${emitShellPaletteVars(c, " ")} return (
|
|
978
|
+
${hasShellPalette(c) ? " <>\n" : ""}${emitLayoutOpen(c)}${emitSlots(c.slots, " ")} <ChatThread messages={chat.messages()} loading={chat.loading()} placeholder="Ask anything" onSubmit={submit} webSearch={false} voice={false}${emitHeaderProp(c)}${emitHeaderEndContentProp(c)}${emitAttachProps(c)}${emitStartersProp(c)}${emitReasoningProp(c)}${emitReasoningOpenProp(c)}${emitMessageActionsProps(c)}${emitHideSourcesProp(c)}${emitTriggersProp(c)}${emitEmptyContentProp(c)}${emitCardTypesProp(c)}${emitHomeProp(c)}${emitConversationsProps(c)}${emitChatControllerRefProp(c)}${emitChatThreadUnreadProps(c)} />
|
|
979
|
+
${emitLayoutClose(c)}${emitShellPaletteOverlay(c)}${hasShellPalette(c) ? " </>\n" : ""} );
|
|
793
980
|
}
|
|
794
981
|
`;
|
|
795
982
|
}
|
|
@@ -811,7 +998,8 @@ ${emitHistorySetup(c)}
|
|
|
811
998
|
// spine (Thread + PromptInput) plus the declared slots, positioned by hand so
|
|
812
999
|
// YOU own the surrounding DOM. Capabilities beyond the spine (starters,
|
|
813
1000
|
// attachments, reasoning display-mode, reasoningOpen, header.title, empty,
|
|
814
|
-
// conversations
|
|
1001
|
+
// conversations, header.themeToggle/actions, composer.triggers, shell) are
|
|
1002
|
+
// NOT wired here in v1 — this file is the eject artifact; add them the
|
|
815
1003
|
// way ChatThread composes them (components/chat-thread.tsx in the kit's own
|
|
816
1004
|
// source) if this construct needs them on a custom layout.
|
|
817
1005
|
export function App() {
|
|
@@ -867,22 +1055,68 @@ function widgetHasHeaderClose(c) {
|
|
|
867
1055
|
function widgetHasConversationsChrome(c) {
|
|
868
1056
|
return c.layout === "widget" && (!!c.capabilities?.conversations || !!c.home);
|
|
869
1057
|
}
|
|
1058
|
+
function hasShellPalette(c) {
|
|
1059
|
+
return c.layout !== "custom" && c.shell?.commandPalette === true;
|
|
1060
|
+
}
|
|
1061
|
+
function emitShellPaletteVars(c, indent) {
|
|
1062
|
+
if (!hasShellPalette(c)) return "";
|
|
1063
|
+
const entries = [
|
|
1064
|
+
`{ id: 'focus-composer', label: 'Focus composer' }`,
|
|
1065
|
+
...c.capabilities?.conversations ? [`{ id: 'new-conversation', label: 'New conversation' }`] : [],
|
|
1066
|
+
...c.header?.themeToggle ? [`{ id: 'toggle-theme', label: 'Toggle theme' }`] : []
|
|
1067
|
+
];
|
|
1068
|
+
const newConversationLine = c.capabilities?.conversations ? `
|
|
1069
|
+
${indent} if (id === 'new-conversation') chatController?.startNewConversation();` : "";
|
|
1070
|
+
const toggleThemeLine = c.header?.themeToggle ? `
|
|
1071
|
+
${indent} if (id === 'toggle-theme') toggleTheme();` : "";
|
|
1072
|
+
return `${indent}// Command palette (shell.commandPalette): Mod+K toggles; Escape/backdrop
|
|
1073
|
+
${indent}// close; entries DERIVE from what this construct enables — no dead rows.
|
|
1074
|
+
${indent}const [paletteOpen, setPaletteOpen] = createSignal(false);
|
|
1075
|
+
${indent}const [paletteQuery, setPaletteQuery] = createSignal('');
|
|
1076
|
+
${indent}const PALETTE_COMMANDS = [${entries.join(", ")}];
|
|
1077
|
+
${indent}const paletteGroups = () => {
|
|
1078
|
+
${indent} const q = paletteQuery().trim().toLowerCase();
|
|
1079
|
+
${indent} const items = q ? PALETTE_COMMANDS.filter((i) => i.label.toLowerCase().includes(q)) : PALETTE_COMMANDS;
|
|
1080
|
+
${indent} return [{ items }];
|
|
1081
|
+
${indent}};
|
|
1082
|
+
${indent}const runPaletteCommand = (id: string) => {
|
|
1083
|
+
${indent} setPaletteOpen(false);
|
|
1084
|
+
${indent} if (id === 'focus-composer') chatController?.focus();${newConversationLine}${toggleThemeLine}
|
|
1085
|
+
${indent}};
|
|
1086
|
+
${indent}onMount(() => {
|
|
1087
|
+
${indent} const onKey = (e: KeyboardEvent) => {
|
|
1088
|
+
${indent} if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); setPaletteOpen((v) => !v); }
|
|
1089
|
+
${indent} if (e.key === 'Escape') setPaletteOpen(false);
|
|
1090
|
+
${indent} };
|
|
1091
|
+
${indent} window.addEventListener('keydown', onKey);
|
|
1092
|
+
${indent} onCleanup(() => window.removeEventListener('keydown', onKey));
|
|
1093
|
+
${indent}});
|
|
1094
|
+
`;
|
|
1095
|
+
}
|
|
1096
|
+
function emitShellPaletteOverlay(c) {
|
|
1097
|
+
if (!hasShellPalette(c)) return "";
|
|
1098
|
+
return ` <Show when={paletteOpen()}>
|
|
1099
|
+
<div style={{ position: 'fixed', inset: '0', 'z-index': '50', display: 'flex', 'align-items': 'flex-start', 'justify-content': 'center', 'padding-block-start': '14vh', background: 'rgb(0 0 0 / 0.5)' }} onClick={() => setPaletteOpen(false)}>
|
|
1100
|
+
<div style={{ width: '100%', 'max-width': '32rem', overflow: 'hidden', 'border-radius': '0.75rem', border: '1px solid var(--color-border)', background: 'var(--color-popover)' }} onClick={(e) => e.stopPropagation()}>
|
|
1101
|
+
<Input value={paletteQuery()} onValueInput={setPaletteQuery} placeholder="Search commands..." autofocus />
|
|
1102
|
+
<CommandList groups={paletteGroups()} onSelect={runPaletteCommand} />
|
|
1103
|
+
</div>
|
|
1104
|
+
</div>
|
|
1105
|
+
</Show>
|
|
1106
|
+
`;
|
|
1107
|
+
}
|
|
870
1108
|
function emitChatControllerVar(c, indent) {
|
|
871
|
-
return widgetHasConversationsChrome(c) ? `${indent}let chatController: ChatThreadController | undefined;
|
|
1109
|
+
return widgetHasConversationsChrome(c) || hasShellPalette(c) ? `${indent}let chatController: ChatThreadController | undefined;
|
|
872
1110
|
` : "";
|
|
873
1111
|
}
|
|
874
1112
|
function emitChatControllerRefProp(c) {
|
|
875
|
-
return widgetHasConversationsChrome(c) ? " controllerRef={(api) => (chatController = api)}" : "";
|
|
1113
|
+
return widgetHasConversationsChrome(c) || hasShellPalette(c) ? " controllerRef={(api) => (chatController = api)}" : "";
|
|
876
1114
|
}
|
|
877
1115
|
function emitDockOnOpenChangeProp(c) {
|
|
878
1116
|
return widgetHasConversationsChrome(c) ? " onOpenChange={(open) => { setDockOpen(open); if (!open) chatController?.closeConversationsList(); }}" : "";
|
|
879
1117
|
}
|
|
880
1118
|
function emitConversationsResetTypeImport(c) {
|
|
881
|
-
return widgetHasConversationsChrome(c) ? ", ChatThreadController" : "";
|
|
882
|
-
}
|
|
883
|
-
function emitConversationsSignalsImport(c) {
|
|
884
|
-
return widgetHasConversationsChrome(c) ? `import { createSignal } from 'solid-js';
|
|
885
|
-
` : "";
|
|
1119
|
+
return widgetHasConversationsChrome(c) || hasShellPalette(c) ? ", ChatThreadController" : "";
|
|
886
1120
|
}
|
|
887
1121
|
function emitConversationsSignalsVar(c, indent) {
|
|
888
1122
|
if (!widgetHasConversationsChrome(c)) return "";
|
|
@@ -908,12 +1142,96 @@ function emitDockControllerRef(c) {
|
|
|
908
1142
|
function emitDockHideClose(c) {
|
|
909
1143
|
return widgetHasHeaderClose(c) ? " hideClose={true}" : "";
|
|
910
1144
|
}
|
|
1145
|
+
function emitToggleThemeVar(c, indent) {
|
|
1146
|
+
if (c.layout === "custom" || !c.header?.themeToggle) return "";
|
|
1147
|
+
return `${indent}const toggleTheme = () => props.host.setAttribute('theme', props.host.getAttribute('theme') === 'dark' ? 'light' : 'dark');
|
|
1148
|
+
`;
|
|
1149
|
+
}
|
|
1150
|
+
function hasHeaderActionsChrome(c) {
|
|
1151
|
+
return c.layout !== "custom" && !!c.header?.actions?.length;
|
|
1152
|
+
}
|
|
1153
|
+
function hasThemeToggleChrome(c) {
|
|
1154
|
+
return c.layout !== "custom" && c.header?.themeToggle === true;
|
|
1155
|
+
}
|
|
1156
|
+
function hasUserMenuChrome(c) {
|
|
1157
|
+
return c.layout !== "custom" && !!c.shell?.userMenu;
|
|
1158
|
+
}
|
|
911
1159
|
function emitHeaderEndContentProp(c) {
|
|
912
|
-
|
|
913
|
-
|
|
1160
|
+
const pieces = [];
|
|
1161
|
+
if (hasHeaderActionsChrome(c)) {
|
|
1162
|
+
for (const a of c.header.actions) {
|
|
1163
|
+
const variant = a.variant ? ` variant="${a.variant}"` : "";
|
|
1164
|
+
pieces.push(
|
|
1165
|
+
`<Button${variant} size="sm" onClick={() => props.host.dispatchEvent(new CustomEvent('kai-header-action', { detail: { label: ${JSON.stringify(a.label)} } }))}>{${JSON.stringify(a.label)}}</Button>`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (hasThemeToggleChrome(c)) {
|
|
1170
|
+
pieces.push(`<Button variant="ghost" size="sm" aria-label="Toggle theme" onClick={toggleTheme}>Theme</Button>`);
|
|
1171
|
+
}
|
|
1172
|
+
if (hasUserMenuChrome(c)) {
|
|
1173
|
+
const m = c.shell.userMenu;
|
|
1174
|
+
const menuLabel = JSON.stringify(`${m.name}${m.plan ? ` — ${m.plan}` : ""} account menu`);
|
|
1175
|
+
const initials = JSON.stringify(m.name.slice(0, 2).toUpperCase());
|
|
1176
|
+
const item = (id, label) => `<DropdownItem onSelect={() => props.host.dispatchEvent(new CustomEvent('kai-user-menu', { detail: { item: '${id}' } }))}>${label}</DropdownItem>`;
|
|
1177
|
+
pieces.push(
|
|
1178
|
+
`<Dropdown><DropdownTrigger aria-label={${menuLabel}}><Avatar fallback={${initials}} size="sm" /></DropdownTrigger><DropdownContent>${item("settings", "Settings")}${item("help", "Get help")}<DropdownSeparator />${item("log-out", "Log out")}</DropdownContent></Dropdown>`
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
if (widgetHasHeaderClose(c)) {
|
|
1182
|
+
pieces.push(
|
|
1183
|
+
`<Button variant="ghost" size="icon-sm" aria-label="Close ${c.name}" onClick={() => dockClose?.()}><DockCloseGlyph /></Button>`
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
if (pieces.length === 0) return "";
|
|
1187
|
+
const content = pieces.length > 1 ? `<>${pieces.join("")}</>` : pieces[0];
|
|
1188
|
+
return ` headerEndContent={${content}}`;
|
|
1189
|
+
}
|
|
1190
|
+
function emitChromeImports(c) {
|
|
1191
|
+
let names = "";
|
|
1192
|
+
if (hasHeaderActionsChrome(c) || hasThemeToggleChrome(c) || widgetHasHeaderClose(c)) names += ", Button";
|
|
1193
|
+
if (hasUserMenuChrome(c)) {
|
|
1194
|
+
names += ", Dropdown, DropdownTrigger, DropdownContent, DropdownItem, DropdownSeparator, Avatar";
|
|
1195
|
+
}
|
|
1196
|
+
if (hasShellPalette(c)) names += ", CommandList, Input";
|
|
1197
|
+
if (widgetHasHeaderClose(c)) names += ", DockCloseGlyph";
|
|
1198
|
+
return names;
|
|
1199
|
+
}
|
|
1200
|
+
function emitChromeComment(c) {
|
|
1201
|
+
const lines = [];
|
|
1202
|
+
if (hasHeaderActionsChrome(c)) {
|
|
1203
|
+
lines.push("// header.actions dispatch 'kai-header-action' on the host, detail: { label }.");
|
|
1204
|
+
}
|
|
1205
|
+
if (hasUserMenuChrome(c)) {
|
|
1206
|
+
lines.push("// shell.userMenu dispatches 'kai-user-menu' on the host, detail: { item }.");
|
|
1207
|
+
}
|
|
1208
|
+
if (hasShellPalette(c)) {
|
|
1209
|
+
lines.push("// shell.commandPalette: Mod+K opens the command palette; entries derive from");
|
|
1210
|
+
lines.push("// what this construct enables (menu-honesty — no dead rows).");
|
|
1211
|
+
}
|
|
1212
|
+
return lines.length ? `${lines.join("\n")}
|
|
1213
|
+
` : "";
|
|
914
1214
|
}
|
|
915
|
-
function
|
|
916
|
-
|
|
1215
|
+
function emitMessageActionsProps(c) {
|
|
1216
|
+
const actions = c.capabilities?.messageActions;
|
|
1217
|
+
if (!actions) return "";
|
|
1218
|
+
let out = "";
|
|
1219
|
+
if (actions.user) out += ` userActions={${JSON.stringify(actions.user)}}`;
|
|
1220
|
+
if (actions.assistant) out += ` assistantActions={${JSON.stringify(actions.assistant)}}`;
|
|
1221
|
+
return out;
|
|
1222
|
+
}
|
|
1223
|
+
function emitHideSourcesProp(c) {
|
|
1224
|
+
return c.capabilities?.sources?.strip === false ? " hideSources={true}" : "";
|
|
1225
|
+
}
|
|
1226
|
+
function emitTriggersProp(c) {
|
|
1227
|
+
const triggers = c.composer?.triggers;
|
|
1228
|
+
if (!triggers) return "";
|
|
1229
|
+
const defs = [
|
|
1230
|
+
...triggers.slash ? [{ char: "/", kind: "command", items: triggers.slash }] : [],
|
|
1231
|
+
...triggers.mention ? [{ char: "@", kind: "mention", items: triggers.mention }] : []
|
|
1232
|
+
];
|
|
1233
|
+
if (defs.length === 0) return "";
|
|
1234
|
+
return ` triggers={${JSON.stringify(defs)}}`;
|
|
917
1235
|
}
|
|
918
1236
|
function emitAttachProps(c) {
|
|
919
1237
|
const attachments = c.capabilities?.attachments;
|
|
@@ -948,10 +1266,17 @@ function emitConversationsImport(c) {
|
|
|
948
1266
|
const name = c.capabilities.history?.persistence === "endpoint" ? "fetchStore" : "localStorageStore";
|
|
949
1267
|
return `import { ${name} } from '@kitn.ai/ui/solid';`;
|
|
950
1268
|
}
|
|
951
|
-
function
|
|
1269
|
+
function needsCreateEffect(c) {
|
|
952
1270
|
const history = c.capabilities?.history;
|
|
953
|
-
|
|
954
|
-
|
|
1271
|
+
return !!history && history.persistence !== "none" && !(c.layout !== "custom" && c.capabilities?.conversations);
|
|
1272
|
+
}
|
|
1273
|
+
function emitSolidJsImports(c) {
|
|
1274
|
+
const names = [];
|
|
1275
|
+
if (needsCreateEffect(c)) names.push("createEffect");
|
|
1276
|
+
if (widgetHasConversationsChrome(c) || hasShellPalette(c)) names.push("createSignal");
|
|
1277
|
+
if (hasShellPalette(c)) names.push("Show", "onMount", "onCleanup");
|
|
1278
|
+
if (names.length === 0) return "";
|
|
1279
|
+
return `import { ${names.join(", ")} } from 'solid-js';
|
|
955
1280
|
`;
|
|
956
1281
|
}
|
|
957
1282
|
function emitHistoryTypeImport(c) {
|
|
@@ -1119,6 +1444,9 @@ function emitSlots(slots, indent) {
|
|
|
1119
1444
|
return slots.map((name) => `${indent}<slot name="${name}" />
|
|
1120
1445
|
`).join("");
|
|
1121
1446
|
}
|
|
1447
|
+
function needsHost(c) {
|
|
1448
|
+
return hasThemeToggleChrome(c) || hasHeaderActionsChrome(c) || hasUserMenuChrome(c);
|
|
1449
|
+
}
|
|
1122
1450
|
function emitDockPosition(c) {
|
|
1123
1451
|
const w = c.layout === "widget" ? c.widget : void 0;
|
|
1124
1452
|
return w?.position ? ` position="${w.position}"` : "";
|
|
@@ -1143,14 +1471,19 @@ function emitLayoutOpen(c) {
|
|
|
1143
1471
|
case "fullscreen":
|
|
1144
1472
|
return ` <div style={{ height: '100dvh', display: 'flex', 'flex-direction': 'column' }}>
|
|
1145
1473
|
`;
|
|
1146
|
-
case "aside":
|
|
1147
|
-
|
|
1474
|
+
case "aside": {
|
|
1475
|
+
const position = c.aside?.position ?? "end";
|
|
1476
|
+
const width = JSON.stringify(c.aside?.width ?? "380px");
|
|
1477
|
+
const inset = position === "start" ? "'inset-inline-start': '0'" : "'inset-inline-end': '0'";
|
|
1478
|
+
const borderSide = position === "start" ? "border-inline-end" : "border-inline-start";
|
|
1479
|
+
return ` <aside data-kai-layout="aside" style={{ position: 'fixed', 'inset-block': '0', ${inset}, width: ${width}, display: 'flex', 'flex-direction': 'column', '${borderSide}': '1px solid var(--kai-color-border)' }}>
|
|
1148
1480
|
{/* Mirrors Dock's own narrow-viewport full-bleed rule (ui/dock.tsx:229-240)
|
|
1149
1481
|
— aside has no dedicated kit component (see the emitLayoutOpen doc
|
|
1150
1482
|
comment above), so this is the honest hand-rolled equivalent, not a
|
|
1151
1483
|
new responsive strategy. */}
|
|
1152
|
-
<style>{\`@media (max-width: 480px) { [data-kai-layout="aside"] { inset: 0; width: auto; height: auto;
|
|
1484
|
+
<style>{\`@media (max-width: 480px) { [data-kai-layout="aside"] { inset: 0; width: auto; height: auto; ${borderSide}: 0; } }\`}</style>
|
|
1153
1485
|
`;
|
|
1486
|
+
}
|
|
1154
1487
|
case "split":
|
|
1155
1488
|
return ` <div style={{ height: '100dvh' }}>
|
|
1156
1489
|
<WorkspaceShell class="h-full" drawerBelow={480} end={
|
|
@@ -1216,6 +1549,7 @@ const USAGE = `usage: npx @kitn.ai/ui <command> (or \`kai <command>\` once @ki
|
|
|
1216
1549
|
kai validate <construct.json> check a construct, print problems with paths
|
|
1217
1550
|
kai eject <construct.json> <outDir> write the generated Solid project (it's yours)
|
|
1218
1551
|
kai dev <construct.json> live preview with reload-on-edit
|
|
1552
|
+
kai dev --builder [construct.json] visual builder + live preview (no file = start from a template)
|
|
1219
1553
|
kai compile <construct.json> [outDir] one self-registering .js
|
|
1220
1554
|
`;
|
|
1221
1555
|
function homeRecentConversationWarning(construct) {
|
|
@@ -1254,6 +1588,12 @@ function parseUiFlag(rest) {
|
|
|
1254
1588
|
const positional = uiFlag >= 0 ? rest.filter((_, i) => i !== uiFlag && i !== uiFlag + 1) : rest;
|
|
1255
1589
|
return { uiSpec, positional };
|
|
1256
1590
|
}
|
|
1591
|
+
function parseDevArgs(rest) {
|
|
1592
|
+
const { uiSpec, positional } = parseUiFlag(rest);
|
|
1593
|
+
const builder = positional.includes("--builder");
|
|
1594
|
+
const path = positional.filter((a) => a !== "--builder")[0];
|
|
1595
|
+
return { uiSpec, builder, path };
|
|
1596
|
+
}
|
|
1257
1597
|
async function runCli(argv, io = defaultIo) {
|
|
1258
1598
|
const [command, ...rest] = argv;
|
|
1259
1599
|
switch (command) {
|
|
@@ -1284,13 +1624,17 @@ async function runCli(argv, io = defaultIo) {
|
|
|
1284
1624
|
return 0;
|
|
1285
1625
|
}
|
|
1286
1626
|
case "dev": {
|
|
1287
|
-
const { uiSpec,
|
|
1288
|
-
|
|
1627
|
+
const { uiSpec, builder, path } = parseDevArgs(rest);
|
|
1628
|
+
if (builder) {
|
|
1629
|
+
const { devBuilder } = await import("./assets/dev-DwzHsqYD.js");
|
|
1630
|
+
await devBuilder(path, { io, uiSpec });
|
|
1631
|
+
return 0;
|
|
1632
|
+
}
|
|
1289
1633
|
if (!path) {
|
|
1290
1634
|
io.error(USAGE);
|
|
1291
1635
|
return 2;
|
|
1292
1636
|
}
|
|
1293
|
-
const { dev } = await import("./assets/dev-
|
|
1637
|
+
const { dev } = await import("./assets/dev-DwzHsqYD.js");
|
|
1294
1638
|
await dev(path, { io, uiSpec });
|
|
1295
1639
|
return 0;
|
|
1296
1640
|
}
|
|
@@ -1304,7 +1648,7 @@ async function runCli(argv, io = defaultIo) {
|
|
|
1304
1648
|
const construct = loadConstruct(path, io);
|
|
1305
1649
|
if (!construct) return 1;
|
|
1306
1650
|
const outDir = resolve(outArg ?? "dist-construct");
|
|
1307
|
-
const { workDirFor, ensureInstalled } = await import("./assets/dev-
|
|
1651
|
+
const { workDirFor, ensureInstalled } = await import("./assets/dev-DwzHsqYD.js");
|
|
1308
1652
|
const dir = workDirFor(construct.name, process.cwd());
|
|
1309
1653
|
const files = generateProject(construct, { uiSpec });
|
|
1310
1654
|
writeProject(files, dir);
|