@json-render/vue 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{chunk-WIPZLAF7.mjs → chunk-F4CKBTOP.mjs} +6 -3
- package/dist/chunk-F4CKBTOP.mjs.map +1 -0
- package/dist/index.js +26 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -9
- package/dist/index.mjs.map +1 -1
- package/dist/schema.d.mts +11 -1
- package/dist/schema.d.ts +11 -1
- package/dist/schema.js +5 -2
- package/dist/schema.js.map +1 -1
- package/dist/schema.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-WIPZLAF7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -488,6 +488,8 @@ const spec = {
|
|
|
488
488
|
|
|
489
489
|
## Differences from `@json-render/react`
|
|
490
490
|
|
|
491
|
+
Nested lists can set `repeat.statePath` to `{ "$item": "field" }` to iterate an array on the enclosing repeat item.
|
|
492
|
+
|
|
491
493
|
| API | React | Vue | Note |
|
|
492
494
|
|-----|-------|-----|------|
|
|
493
495
|
| `useStateStore().state` | `StateModel` | `ShallowRef<StateModel>` | Vue reactivity; use `state.value` |
|
|
@@ -16,7 +16,9 @@ var schema = defineSchema(
|
|
|
16
16
|
/** Child element keys (flat reference) */
|
|
17
17
|
children: s.array(s.string()),
|
|
18
18
|
/** Visibility condition */
|
|
19
|
-
visible: s.any()
|
|
19
|
+
visible: { ...s.any(), ...s.optional() },
|
|
20
|
+
/** Repeat children from a state array */
|
|
21
|
+
repeat: { ...s.any(), ...s.optional() }
|
|
20
22
|
})
|
|
21
23
|
)
|
|
22
24
|
}),
|
|
@@ -65,12 +67,13 @@ var schema = defineSchema(
|
|
|
65
67
|
// Element integrity
|
|
66
68
|
"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.",
|
|
67
69
|
"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
|
+
'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
|
|
68
71
|
// Field placement
|
|
69
72
|
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
|
|
70
73
|
'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.',
|
|
71
74
|
// State and data
|
|
72
75
|
"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).",
|
|
73
|
-
'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"] }. 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.',
|
|
76
|
+
'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.',
|
|
74
77
|
// Design quality
|
|
75
78
|
"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.",
|
|
76
79
|
"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.",
|
|
@@ -82,4 +85,4 @@ var schema = defineSchema(
|
|
|
82
85
|
export {
|
|
83
86
|
schema
|
|
84
87
|
};
|
|
85
|
-
//# sourceMappingURL=chunk-
|
|
88
|
+
//# sourceMappingURL=chunk-F4CKBTOP.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 /** 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\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,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;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.js
CHANGED
|
@@ -542,9 +542,8 @@ var ActionProvider = (0, import_vue4.defineComponent)({
|
|
|
542
542
|
handler,
|
|
543
543
|
setState: set,
|
|
544
544
|
navigate: props.navigate,
|
|
545
|
-
executeAction: async (
|
|
546
|
-
|
|
547
|
-
await execute(subBinding);
|
|
545
|
+
executeAction: async (binding2) => {
|
|
546
|
+
await execute(binding2);
|
|
548
547
|
}
|
|
549
548
|
});
|
|
550
549
|
} finally {
|
|
@@ -563,9 +562,8 @@ var ActionProvider = (0, import_vue4.defineComponent)({
|
|
|
563
562
|
handler,
|
|
564
563
|
setState: set,
|
|
565
564
|
navigate: props.navigate,
|
|
566
|
-
executeAction: async (
|
|
567
|
-
|
|
568
|
-
await execute(subBinding);
|
|
565
|
+
executeAction: async (binding2) => {
|
|
566
|
+
await execute(binding2);
|
|
569
567
|
}
|
|
570
568
|
});
|
|
571
569
|
} finally {
|
|
@@ -786,7 +784,9 @@ var schema = (0, import_core5.defineSchema)(
|
|
|
786
784
|
/** Child element keys (flat reference) */
|
|
787
785
|
children: s.array(s.string()),
|
|
788
786
|
/** Visibility condition */
|
|
789
|
-
visible: s.any()
|
|
787
|
+
visible: { ...s.any(), ...s.optional() },
|
|
788
|
+
/** Repeat children from a state array */
|
|
789
|
+
repeat: { ...s.any(), ...s.optional() }
|
|
790
790
|
})
|
|
791
791
|
)
|
|
792
792
|
}),
|
|
@@ -835,12 +835,13 @@ var schema = (0, import_core5.defineSchema)(
|
|
|
835
835
|
// Element integrity
|
|
836
836
|
"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
837
|
"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
|
+
'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
|
|
838
839
|
// Field placement
|
|
839
840
|
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
|
|
840
841
|
'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.',
|
|
841
842
|
// State and data
|
|
842
843
|
"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).",
|
|
843
|
-
'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"] }. 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.',
|
|
844
|
+
'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.',
|
|
844
845
|
// Design quality
|
|
845
846
|
"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.",
|
|
846
847
|
"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.",
|
|
@@ -1632,10 +1633,20 @@ var RepeatChildren = (0, import_vue7.defineComponent)({
|
|
|
1632
1633
|
},
|
|
1633
1634
|
setup(props) {
|
|
1634
1635
|
const { state } = useStateStore();
|
|
1636
|
+
const parentScope = useRepeatScope();
|
|
1635
1637
|
return () => {
|
|
1636
1638
|
const repeat = props.element.repeat;
|
|
1637
1639
|
if (!repeat?.statePath) return null;
|
|
1638
|
-
const statePath =
|
|
1640
|
+
const statePath = (0, import_core7.resolveRepeatStatePath)(
|
|
1641
|
+
repeat.statePath,
|
|
1642
|
+
parentScope?.basePath
|
|
1643
|
+
);
|
|
1644
|
+
if (statePath === void 0) {
|
|
1645
|
+
console.warn(
|
|
1646
|
+
"[json-render/vue] $item in repeat.statePath used outside of a repeat scope"
|
|
1647
|
+
);
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1639
1650
|
const raw = (0, import_core7.getByPath)(state.value, statePath);
|
|
1640
1651
|
const items = Array.isArray(raw) ? raw : [];
|
|
1641
1652
|
return items.map((itemValue, index) => {
|
|
@@ -1644,7 +1655,12 @@ var RepeatChildren = (0, import_vue7.defineComponent)({
|
|
|
1644
1655
|
) : String(index);
|
|
1645
1656
|
return (0, import_vue7.h)(
|
|
1646
1657
|
RepeatScopeProvider,
|
|
1647
|
-
{
|
|
1658
|
+
{
|
|
1659
|
+
key,
|
|
1660
|
+
item: itemValue,
|
|
1661
|
+
index,
|
|
1662
|
+
basePath: (0, import_core7.resolveRepeatItemStatePath)(statePath, index)
|
|
1663
|
+
},
|
|
1648
1664
|
{
|
|
1649
1665
|
default: () => props.element.children?.map((childKey) => {
|
|
1650
1666
|
const childElement = props.spec.elements[childKey];
|