@json-render/vue 0.20.0 → 0.21.1

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 CHANGED
@@ -26,6 +26,7 @@ export const catalog = defineCatalog(schema, {
26
26
  title: z.string(),
27
27
  description: z.string().nullable(),
28
28
  }),
29
+ slots: ["default", "header", "footer"],
29
30
  description: "A card container",
30
31
  },
31
32
  Button: {
@@ -53,7 +54,7 @@ export const catalog = defineCatalog(schema, {
53
54
 
54
55
  ### 2. Define Component Implementations
55
56
 
56
- Components are written using Vue's `h()` render function. `children` is a `VNode | VNode[]` — pass it directly to your container element.
57
+ Components are written using Vue's `h()` render function. The `slots` context uses Vue's native slot functions, so render a region with `slots.header?.()`. For convenience and React parity, `children` contains the already-rendered result of `slots.default?.()`.
57
58
 
58
59
  `defineRegistry` conditionally requires the `actions` field only when the catalog declares actions. Catalogs with `actions: {}` can omit it entirely.
59
60
 
@@ -64,11 +65,12 @@ import { catalog } from "./catalog";
64
65
 
65
66
  export const { registry } = defineRegistry(catalog, {
66
67
  components: {
67
- Card: ({ props, children }) =>
68
+ Card: ({ props, slots }) =>
68
69
  h("div", { class: "card" }, [
69
- h("h3", null, props.title),
70
+ h("header", null, slots.header?.() ?? h("h3", null, props.title)),
70
71
  props.description ? h("p", null, props.description) : null,
71
- children,
72
+ slots.default?.(),
73
+ h("footer", null, slots.footer?.()),
72
74
  ]),
73
75
  Button: ({ props, emit }) =>
74
76
  h("button", { onClick: () => emit("press") }, props.label),
@@ -131,6 +133,7 @@ interface UIElement {
131
133
  type: string; // Component name from catalog
132
134
  props: Record<string, unknown>; // Component props
133
135
  children?: string[]; // Keys of child elements
136
+ slots?: Record<string, string[]>; // Named slots mapped to child keys
134
137
  visible?: VisibilityCondition; // Visibility condition
135
138
  }
136
139
  ```
@@ -163,6 +166,35 @@ Example spec:
163
166
  }
164
167
  ```
165
168
 
169
+ ### Named Slots
170
+
171
+ Use `children` for the default slot and the element's top-level `slots` object for other slot names declared by the catalog:
172
+
173
+ ```typescript
174
+ Layout: ({ children, slots }) =>
175
+ h("div", null, [
176
+ h("header", null, slots.header?.()),
177
+ h("main", null, children),
178
+ h("footer", null, slots.footer?.()),
179
+ ]),
180
+ ```
181
+
182
+ The corresponding spec maps element keys to each region:
183
+
184
+ ```json
185
+ {
186
+ "type": "Layout",
187
+ "props": {},
188
+ "children": ["main-content"],
189
+ "slots": {
190
+ "header": ["page-heading"],
191
+ "footer": ["page-actions"]
192
+ }
193
+ }
194
+ ```
195
+
196
+ Registry components receive Vue-native slot functions and render them with `slots.header?.()`, `slots.footer?.()`, and so on. `slots.default?.()` renders the spec's `children`; `children` is an alias for that rendered result. In the JSON spec, keep default content in `children` rather than adding a `default` entry to `slots`.
197
+
166
198
  ## Providers
167
199
 
168
200
  ### StateProvider
@@ -366,11 +398,12 @@ The `setState`, `pushState`, `removeState`, and `validateForm` actions are built
366
398
  When using `defineRegistry`, components receive these props via their render function:
367
399
 
368
400
  ```typescript
369
- import type { VNode } from "vue";
401
+ import type { Slots, VNode } from "vue";
370
402
 
371
403
  interface ComponentContext<P> {
372
404
  props: P; // Typed props from the catalog (expressions resolved)
373
405
  children?: VNode | VNode[]; // Rendered children (for container components)
406
+ slots: Slots; // Vue-native slot functions
374
407
  emit: (event: string) => void; // Emit a named event (always defined)
375
408
  on: (event: string) => EventHandle; // Get event handle with metadata
376
409
  loading?: boolean; // Whether the parent is loading
@@ -15,6 +15,8 @@ var schema = defineSchema(
15
15
  props: s.propsOf("catalog.components"),
16
16
  /** Child element keys (flat reference) */
17
17
  children: s.array(s.string()),
18
+ /** Named slots mapped to child element keys */
19
+ slots: { ...s.record(s.array(s.string())), ...s.optional() },
18
20
  /** Visibility condition */
19
21
  visible: { ...s.any(), ...s.optional() },
20
22
  /** Repeat children from a state array */
@@ -68,6 +70,7 @@ var schema = defineSchema(
68
70
  "CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.",
69
71
  "SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.",
70
72
  'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
73
+ 'NAMED SLOTS: Use "children" for the default slot. For other slots declared by the component, use a top-level "slots" object that maps each slot name to child element keys, for example {"slots":{"header":["heading"],"footer":["actions"]}}. Never use "slots.default". Every referenced key must exist.',
71
74
  // Field placement
72
75
  'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
73
76
  'CRITICAL: The "on" field goes on the ELEMENT object, NOT inside "props". Use on.press, on.change, on.submit etc. NEVER put action/actionParams inside props.',
@@ -85,4 +88,4 @@ var schema = defineSchema(
85
88
  export {
86
89
  schema
87
90
  };
88
- //# sourceMappingURL=chunk-F4CKBTOP.mjs.map
91
+ //# sourceMappingURL=chunk-34A2QV2X.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema.ts"],"sourcesContent":["import { defineSchema } from \"@json-render/core\";\n\n/**\n * The schema for @json-render/vue\n *\n * Defines:\n * - Spec: A flat tree of elements with keys, types, props, and children references\n * - Catalog: Components with props schemas, and optional actions\n */\nexport const schema = defineSchema(\n (s) => ({\n // What the AI-generated SPEC looks like\n spec: s.object({\n /** Root element key */\n root: s.string(),\n /** Flat map of elements by key */\n elements: s.record(\n s.object({\n /** Component type from catalog */\n type: s.ref(\"catalog.components\"),\n /** Component props */\n props: s.propsOf(\"catalog.components\"),\n /** Child element keys (flat reference) */\n children: s.array(s.string()),\n /** Named slots mapped to child element keys */\n slots: { ...s.record(s.array(s.string())), ...s.optional() },\n /** Visibility condition */\n visible: { ...s.any(), ...s.optional() },\n /** Repeat children from a state array */\n repeat: { ...s.any(), ...s.optional() },\n }),\n ),\n }),\n\n // What the CATALOG must provide\n catalog: s.object({\n /** Component definitions */\n components: s.map({\n /** Zod schema for component props */\n props: s.zod(),\n /** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */\n slots: s.array(s.string()),\n /** Description for AI generation hints */\n description: s.string(),\n /** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */\n example: s.any(),\n }),\n /** Action definitions (optional) */\n actions: s.map({\n /** Zod schema for action params */\n params: s.zod(),\n /** Description for AI generation hints */\n description: s.string(),\n }),\n }),\n }),\n {\n builtInActions: [\n {\n name: \"setState\",\n description:\n \"Update a value in the state model at the given statePath. Params: { statePath: string, value: any }\",\n },\n {\n name: \"pushState\",\n description:\n 'Append an item to an array in state. Params: { statePath: string, value: any, clearStatePath?: string }. Value can contain {\"$state\":\"/path\"} refs and \"$id\" for auto IDs.',\n },\n {\n name: \"removeState\",\n description:\n \"Remove an item from an array in state by index. Params: { statePath: string, index: number }\",\n },\n {\n name: \"validateForm\",\n description:\n \"Validate all registered form fields and write the result to state. Params: { statePath?: string }. Defaults to /formValidation. Result: { valid: boolean, errors: Record<string, string[]> }.\",\n },\n ],\n defaultRules: [\n // Element integrity\n \"CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.\",\n \"SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.\",\n 'REQUIRED FIELDS: Every element MUST include a \"children\" array. Leaf elements (text, badges, inputs, images) use an empty array: \"children\": []. Omitting \"children\" fails validation.',\n 'NAMED SLOTS: Use \"children\" for the default slot. For other slots declared by the component, use a top-level \"slots\" object that maps each slot name to child element keys, for example {\"slots\":{\"header\":[\"heading\"],\"footer\":[\"actions\"]}}. Never use \"slots.default\". Every referenced key must exist.',\n\n // Field placement\n 'CRITICAL: The \"visible\" field goes on the ELEMENT object, NOT inside \"props\". Correct: {\"type\":\"<ComponentName>\",\"props\":{},\"visible\":{\"$state\":\"/tab\",\"eq\":\"home\"},\"children\":[...]}.',\n 'CRITICAL: The \"on\" field goes on the ELEMENT object, NOT inside \"props\". Use on.press, on.change, on.submit etc. NEVER put action/actionParams inside props.',\n\n // State and data\n \"When the user asks for a UI that displays data (e.g. blog posts, products, users), ALWAYS include a state field with realistic sample data. The state field is a top-level field on the spec (sibling of root/elements).\",\n 'When building repeating content backed by a state array (e.g. posts, products, items), use the \"repeat\" field on a container element. Example: { \"type\": \"<ContainerComponent>\", \"props\": {}, \"repeat\": { \"statePath\": \"/posts\", \"key\": \"id\" }, \"children\": [\"post-card\"] }. For a nested list stored on the enclosing item, use \"repeat\": { \"statePath\": { \"$item\": \"comments\" }, \"key\": \"id\" }. The $item statePath form is valid only inside another repeat. Replace <ContainerComponent> with an appropriate component from the AVAILABLE COMPONENTS list. Inside repeated children, use { \"$item\": \"field\" } to read a field from the current item, and { \"$index\": true } for the current array index. For two-way binding to an item field use { \"$bindItem\": \"completed\" }. Do NOT hardcode individual elements for each array item.',\n\n // Design quality\n \"Design with visual hierarchy: use container components to group content, heading components for section titles, proper spacing, and status indicators. ONLY use components from the AVAILABLE COMPONENTS list.\",\n \"For data-rich UIs, use multi-column layout components if available. For forms and single-column content, use vertical layout components. ONLY use components from the AVAILABLE COMPONENTS list.\",\n \"Always include realistic, professional-looking sample data. For blogs include 3-4 posts with varied titles, authors, dates, categories. For products include names, prices, images. Never leave data empty.\",\n ],\n },\n);\n\n/**\n * Type for the Vue schema\n */\nexport type VueSchema = typeof schema;\n\n/**\n * Infer the spec type from a catalog\n */\nexport type VueSpec<TCatalog> = typeof schema extends {\n createCatalog: (catalog: TCatalog) => { _specType: infer S };\n}\n ? S\n : never;\n"],"mappings":";AAAA,SAAS,oBAAoB;AAStB,IAAM,SAAS;AAAA,EACpB,CAAC,OAAO;AAAA;AAAA,IAEN,MAAM,EAAE,OAAO;AAAA;AAAA,MAEb,MAAM,EAAE,OAAO;AAAA;AAAA,MAEf,UAAU,EAAE;AAAA,QACV,EAAE,OAAO;AAAA;AAAA,UAEP,MAAM,EAAE,IAAI,oBAAoB;AAAA;AAAA,UAEhC,OAAO,EAAE,QAAQ,oBAAoB;AAAA;AAAA,UAErC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA;AAAA,UAE5B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,SAAS,EAAE;AAAA;AAAA,UAE3D,SAAS,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,SAAS,EAAE;AAAA;AAAA,UAEvC,QAAQ,EAAE,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,SAAS,EAAE;AAAA,QACxC,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA;AAAA,IAGD,SAAS,EAAE,OAAO;AAAA;AAAA,MAEhB,YAAY,EAAE,IAAI;AAAA;AAAA,QAEhB,OAAO,EAAE,IAAI;AAAA;AAAA,QAEb,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA;AAAA,QAEzB,aAAa,EAAE,OAAO;AAAA;AAAA,QAEtB,SAAS,EAAE,IAAI;AAAA,MACjB,CAAC;AAAA;AAAA,MAED,SAAS,EAAE,IAAI;AAAA;AAAA,QAEb,QAAQ,EAAE,IAAI;AAAA;AAAA,QAEd,aAAa,EAAE,OAAO;AAAA,MACxB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EACA;AAAA,IACE,gBAAgB;AAAA,MACd;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,cAAc;AAAA;AAAA,MAEZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as vue from 'vue';
2
- import { PropType, ShallowRef, ComputedRef, VNode, Ref, Component } from 'vue';
2
+ import { PropType, ShallowRef, ComputedRef, VNode, Slots, Ref, Component } from 'vue';
3
3
  import { StateStore, StateModel, VisibilityCondition, VisibilityContext, ActionHandler, ResolvedAction, ActionBinding, ActionConfirm, ValidationFunction, ValidationResult, ValidationConfig, Catalog, InferCatalogComponents, InferComponentProps, InferCatalogActions, InferActionParams, Spec, FlatElement, SchemaDefinition, ComputedFunction, DirectiveDefinition, UIElement } from '@json-render/core';
4
4
  export { ComputedFunction, Spec, StateModel, StateStore, createStateStore } from '@json-render/core';
5
5
  export { VueSchema, VueSpec, schema } from './schema.mjs';
@@ -384,6 +384,8 @@ interface BaseComponentProps<P = Record<string, unknown>> {
384
384
  props: P;
385
385
  /** Rendered children (from the default slot) */
386
386
  children?: VNode | VNode[];
387
+ /** Vue-native slot functions, including the default slot */
388
+ slots: Slots;
387
389
  /** Simple event emitter (shorthand). Fires the event and returns void. */
388
390
  emit: (event: string) => void;
389
391
  /** Get an event handle with metadata. Use when you need shouldPreventDefault or bound checks. */
@@ -849,7 +851,7 @@ type DefineRegistryOptions<C extends Catalog> = {
849
851
  * });
850
852
  * ```
851
853
  */
852
- declare function defineRegistry<C extends Catalog>(_catalog: C, options: DefineRegistryOptions<C>): DefineRegistryResult;
854
+ declare function defineRegistry<C extends Catalog>(catalog: C, options: DefineRegistryOptions<C>): DefineRegistryResult;
853
855
  /**
854
856
  * Props for renderers created with createRenderer
855
857
  */
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as vue from 'vue';
2
- import { PropType, ShallowRef, ComputedRef, VNode, Ref, Component } from 'vue';
2
+ import { PropType, ShallowRef, ComputedRef, VNode, Slots, Ref, Component } from 'vue';
3
3
  import { StateStore, StateModel, VisibilityCondition, VisibilityContext, ActionHandler, ResolvedAction, ActionBinding, ActionConfirm, ValidationFunction, ValidationResult, ValidationConfig, Catalog, InferCatalogComponents, InferComponentProps, InferCatalogActions, InferActionParams, Spec, FlatElement, SchemaDefinition, ComputedFunction, DirectiveDefinition, UIElement } from '@json-render/core';
4
4
  export { ComputedFunction, Spec, StateModel, StateStore, createStateStore } from '@json-render/core';
5
5
  export { VueSchema, VueSpec, schema } from './schema.js';
@@ -384,6 +384,8 @@ interface BaseComponentProps<P = Record<string, unknown>> {
384
384
  props: P;
385
385
  /** Rendered children (from the default slot) */
386
386
  children?: VNode | VNode[];
387
+ /** Vue-native slot functions, including the default slot */
388
+ slots: Slots;
387
389
  /** Simple event emitter (shorthand). Fires the event and returns void. */
388
390
  emit: (event: string) => void;
389
391
  /** Get an event handle with metadata. Use when you need shouldPreventDefault or bound checks. */
@@ -849,7 +851,7 @@ type DefineRegistryOptions<C extends Catalog> = {
849
851
  * });
850
852
  * ```
851
853
  */
852
- declare function defineRegistry<C extends Catalog>(_catalog: C, options: DefineRegistryOptions<C>): DefineRegistryResult;
854
+ declare function defineRegistry<C extends Catalog>(catalog: C, options: DefineRegistryOptions<C>): DefineRegistryResult;
853
855
  /**
854
856
  * Props for renderers created with createRenderer
855
857
  */
package/dist/index.js CHANGED
@@ -783,6 +783,8 @@ var schema = (0, import_core5.defineSchema)(
783
783
  props: s.propsOf("catalog.components"),
784
784
  /** Child element keys (flat reference) */
785
785
  children: s.array(s.string()),
786
+ /** Named slots mapped to child element keys */
787
+ slots: { ...s.record(s.array(s.string())), ...s.optional() },
786
788
  /** Visibility condition */
787
789
  visible: { ...s.any(), ...s.optional() },
788
790
  /** Repeat children from a state array */
@@ -836,6 +838,7 @@ var schema = (0, import_core5.defineSchema)(
836
838
  "CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.",
837
839
  "SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.",
838
840
  'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
841
+ 'NAMED SLOTS: Use "children" for the default slot. For other slots declared by the component, use a top-level "slots" object that maps each slot name to child element keys, for example {"slots":{"header":["heading"],"footer":["actions"]}}. Never use "slots.default". Every referenced key must exist.',
839
842
  // Field placement
840
843
  'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
841
844
  'CRITICAL: The "on" field goes on the ELEMENT object, NOT inside "props". Use on.press, on.change, on.submit etc. NEVER put action/actionParams inside props.',
@@ -1333,6 +1336,7 @@ function useChatUI({
1333
1336
  // src/renderer.ts
1334
1337
  var import_vue7 = require("vue");
1335
1338
  var import_core7 = require("@json-render/core");
1339
+ var registryMetadata = /* @__PURE__ */ new WeakMap();
1336
1340
  var EMPTY_FUNCTIONS = {};
1337
1341
  var FUNCTIONS_KEY = /* @__PURE__ */ Symbol("json-render:functions");
1338
1342
  var FunctionsProvider = (0, import_vue7.defineComponent)({
@@ -1557,18 +1561,28 @@ var ElementRenderer = (0, import_vue7.defineComponent)({
1557
1561
  );
1558
1562
  return null;
1559
1563
  }
1560
- const childrenVNodes = resolvedElement.repeat ? (0, import_vue7.h)(RepeatChildren, {
1561
- element: resolvedElement,
1562
- spec: props.spec,
1563
- registry: props.registry,
1564
- loading: props.loading,
1565
- fallback: props.fallback
1566
- }) : resolvedElement.children?.map((childKey) => {
1564
+ const metadata = registryMetadata.get(props.registry)?.[resolvedElement.type];
1565
+ if (resolvedElement.slots && metadata?.slots) {
1566
+ const availableSlots = new Set(metadata.slots);
1567
+ for (const slotName of Object.keys(resolvedElement.slots)) {
1568
+ if (slotName === "default") {
1569
+ console.warn(
1570
+ `[json-render] Component "${resolvedElement.type}" uses slots.default. Use "children" for default slot content.`
1571
+ );
1572
+ } else if (!availableSlots.has(slotName)) {
1573
+ console.warn(
1574
+ `[json-render] Unknown slot "${slotName}" on component "${resolvedElement.type}". Available slots: ${metadata.slots.join(", ")}`
1575
+ );
1576
+ }
1577
+ }
1578
+ }
1579
+ const renderChildKeys = (childKeys, slotName) => childKeys.map((childKey) => {
1567
1580
  const childElement = props.spec.elements[childKey];
1568
1581
  if (!childElement) {
1569
1582
  if (!props.loading) {
1583
+ const location = slotName ? `in slot "${slotName}" of "${resolvedElement.type}"` : `as child of "${resolvedElement.type}"`;
1570
1584
  console.warn(
1571
- `[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
1585
+ `[json-render] Missing element "${childKey}" referenced ${location}. This element will not render.`
1572
1586
  );
1573
1587
  }
1574
1588
  return null;
@@ -1582,7 +1596,20 @@ var ElementRenderer = (0, import_vue7.defineComponent)({
1582
1596
  loading: props.loading,
1583
1597
  fallback: props.fallback
1584
1598
  });
1585
- }).filter((n) => n !== null) ?? void 0;
1599
+ }).filter((node) => node !== null);
1600
+ const childrenVNodes = resolvedElement.repeat ? (0, import_vue7.h)(RepeatChildren, {
1601
+ element: resolvedElement,
1602
+ spec: props.spec,
1603
+ registry: props.registry,
1604
+ loading: props.loading,
1605
+ fallback: props.fallback
1606
+ }) : resolvedElement.children ? renderChildKeys(resolvedElement.children) : void 0;
1607
+ const namedSlots = resolvedElement.slots ? Object.fromEntries(
1608
+ Object.entries(resolvedElement.slots).filter(([slotName]) => slotName !== "default").map(([slotName, childKeys]) => [
1609
+ slotName,
1610
+ () => renderChildKeys(childKeys, slotName)
1611
+ ])
1612
+ ) : {};
1586
1613
  const componentVNode = (0, import_vue7.h)(
1587
1614
  Component,
1588
1615
  {
@@ -1592,7 +1619,7 @@ var ElementRenderer = (0, import_vue7.defineComponent)({
1592
1619
  bindings: elementBindings,
1593
1620
  loading: props.loading
1594
1621
  },
1595
- { default: () => childrenVNodes }
1622
+ { default: () => childrenVNodes, ...namedSlots }
1596
1623
  );
1597
1624
  const tagged = devtoolsActive.value && props.elementKey ? (0, import_vue7.h)(
1598
1625
  "span",
@@ -1821,7 +1848,7 @@ var JSONUIProvider = (0, import_vue7.defineComponent)({
1821
1848
  );
1822
1849
  }
1823
1850
  });
1824
- function defineRegistry(_catalog, options) {
1851
+ function defineRegistry(catalog, options) {
1825
1852
  const registry = {};
1826
1853
  if (options.components) {
1827
1854
  for (const [name, componentFn] of Object.entries(options.components)) {
@@ -1853,6 +1880,7 @@ function defineRegistry(_catalog, options) {
1853
1880
  return () => componentFn({
1854
1881
  props: registryProps.element.props,
1855
1882
  children: slots.default?.(),
1883
+ slots,
1856
1884
  emit: registryProps.emit,
1857
1885
  on: registryProps.on,
1858
1886
  bindings: registryProps.bindings,
@@ -1862,6 +1890,10 @@ function defineRegistry(_catalog, options) {
1862
1890
  });
1863
1891
  }
1864
1892
  }
1893
+ const catalogComponents = catalog.data?.components;
1894
+ if (catalogComponents) {
1895
+ registryMetadata.set(registry, catalogComponents);
1896
+ }
1865
1897
  const actionMap = options.actions ? Object.entries(options.actions) : [];
1866
1898
  const handlers = (getSetState, getState) => {
1867
1899
  const result = {};
@@ -1888,6 +1920,10 @@ function defineRegistry(_catalog, options) {
1888
1920
  }
1889
1921
  function createRenderer(catalog, components) {
1890
1922
  const registry = components;
1923
+ const catalogComponents = catalog.data.components;
1924
+ if (catalogComponents) {
1925
+ registryMetadata.set(registry, catalogComponents);
1926
+ }
1891
1927
  return (0, import_vue7.defineComponent)({
1892
1928
  name: "CatalogRenderer",
1893
1929
  props: {