@json-render/vue 0.18.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.d.mts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +84 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +83 -22
- 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.d.mts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import { PropType, ShallowRef, ComputedRef, VNode, Ref, Component } from 'vue';
|
|
3
|
-
import { StateStore, StateModel, VisibilityCondition, VisibilityContext, ActionHandler, ResolvedAction, ActionBinding, ActionConfirm, ValidationFunction, ValidationResult, ValidationConfig, Catalog, InferCatalogComponents, InferComponentProps, InferCatalogActions, InferActionParams, Spec, FlatElement, SchemaDefinition, ComputedFunction, UIElement } from '@json-render/core';
|
|
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';
|
|
6
|
+
import * as zod from 'zod';
|
|
7
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* State context value
|
|
@@ -708,6 +710,8 @@ interface JSONUIProviderProps {
|
|
|
708
710
|
validationFunctions?: Record<string, (value: unknown, args?: Record<string, unknown>) => boolean>;
|
|
709
711
|
/** Named functions for `$computed` expressions in props */
|
|
710
712
|
functions?: Record<string, ComputedFunction>;
|
|
713
|
+
/** Custom directives for user-defined `$`-prefixed dynamic values */
|
|
714
|
+
directives?: DirectiveDefinition[];
|
|
711
715
|
onStateChange?: (changes: Array<{
|
|
712
716
|
path: string;
|
|
713
717
|
value: unknown;
|
|
@@ -745,6 +749,10 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
745
749
|
type: PropType<Record<string, ComputedFunction>>;
|
|
746
750
|
default: undefined;
|
|
747
751
|
};
|
|
752
|
+
directives: {
|
|
753
|
+
type: PropType<DirectiveDefinition[]>;
|
|
754
|
+
default: undefined;
|
|
755
|
+
};
|
|
748
756
|
onStateChange: {
|
|
749
757
|
type: PropType<(changes: Array<{
|
|
750
758
|
path: string;
|
|
@@ -783,6 +791,10 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
783
791
|
type: PropType<Record<string, ComputedFunction>>;
|
|
784
792
|
default: undefined;
|
|
785
793
|
};
|
|
794
|
+
directives: {
|
|
795
|
+
type: PropType<DirectiveDefinition[]>;
|
|
796
|
+
default: undefined;
|
|
797
|
+
};
|
|
786
798
|
onStateChange: {
|
|
787
799
|
type: PropType<(changes: Array<{
|
|
788
800
|
path: string;
|
|
@@ -800,6 +812,7 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
800
812
|
handlers: Record<string, (params: Record<string, unknown>) => Promise<unknown> | unknown>;
|
|
801
813
|
navigate: (path: string) => void;
|
|
802
814
|
functions: Record<string, ComputedFunction>;
|
|
815
|
+
directives: DirectiveDefinition<zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>[];
|
|
803
816
|
validationFunctions: Record<string, (value: unknown, args?: Record<string, unknown>) => boolean>;
|
|
804
817
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
805
818
|
/**
|
|
@@ -851,6 +864,8 @@ interface CreateRendererProps {
|
|
|
851
864
|
}>) => void;
|
|
852
865
|
/** Named functions for `$computed` expressions in props */
|
|
853
866
|
functions?: Record<string, ComputedFunction>;
|
|
867
|
+
/** Custom directives for user-defined `$`-prefixed dynamic values */
|
|
868
|
+
directives?: DirectiveDefinition[];
|
|
854
869
|
loading?: boolean;
|
|
855
870
|
fallback?: Component;
|
|
856
871
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import { PropType, ShallowRef, ComputedRef, VNode, Ref, Component } from 'vue';
|
|
3
|
-
import { StateStore, StateModel, VisibilityCondition, VisibilityContext, ActionHandler, ResolvedAction, ActionBinding, ActionConfirm, ValidationFunction, ValidationResult, ValidationConfig, Catalog, InferCatalogComponents, InferComponentProps, InferCatalogActions, InferActionParams, Spec, FlatElement, SchemaDefinition, ComputedFunction, UIElement } from '@json-render/core';
|
|
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';
|
|
6
|
+
import * as zod from 'zod';
|
|
7
|
+
import * as zod_v4_core from 'zod/v4/core';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* State context value
|
|
@@ -708,6 +710,8 @@ interface JSONUIProviderProps {
|
|
|
708
710
|
validationFunctions?: Record<string, (value: unknown, args?: Record<string, unknown>) => boolean>;
|
|
709
711
|
/** Named functions for `$computed` expressions in props */
|
|
710
712
|
functions?: Record<string, ComputedFunction>;
|
|
713
|
+
/** Custom directives for user-defined `$`-prefixed dynamic values */
|
|
714
|
+
directives?: DirectiveDefinition[];
|
|
711
715
|
onStateChange?: (changes: Array<{
|
|
712
716
|
path: string;
|
|
713
717
|
value: unknown;
|
|
@@ -745,6 +749,10 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
745
749
|
type: PropType<Record<string, ComputedFunction>>;
|
|
746
750
|
default: undefined;
|
|
747
751
|
};
|
|
752
|
+
directives: {
|
|
753
|
+
type: PropType<DirectiveDefinition[]>;
|
|
754
|
+
default: undefined;
|
|
755
|
+
};
|
|
748
756
|
onStateChange: {
|
|
749
757
|
type: PropType<(changes: Array<{
|
|
750
758
|
path: string;
|
|
@@ -783,6 +791,10 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
783
791
|
type: PropType<Record<string, ComputedFunction>>;
|
|
784
792
|
default: undefined;
|
|
785
793
|
};
|
|
794
|
+
directives: {
|
|
795
|
+
type: PropType<DirectiveDefinition[]>;
|
|
796
|
+
default: undefined;
|
|
797
|
+
};
|
|
786
798
|
onStateChange: {
|
|
787
799
|
type: PropType<(changes: Array<{
|
|
788
800
|
path: string;
|
|
@@ -800,6 +812,7 @@ declare const JSONUIProvider: vue.DefineComponent<vue.ExtractPropTypes<{
|
|
|
800
812
|
handlers: Record<string, (params: Record<string, unknown>) => Promise<unknown> | unknown>;
|
|
801
813
|
navigate: (path: string) => void;
|
|
802
814
|
functions: Record<string, ComputedFunction>;
|
|
815
|
+
directives: DirectiveDefinition<zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>[];
|
|
803
816
|
validationFunctions: Record<string, (value: unknown, args?: Record<string, unknown>) => boolean>;
|
|
804
817
|
}, {}, {}, {}, string, vue.ComponentProvideOptions, true, {}, any>;
|
|
805
818
|
/**
|
|
@@ -851,6 +864,8 @@ interface CreateRendererProps {
|
|
|
851
864
|
}>) => void;
|
|
852
865
|
/** Named functions for `$computed` expressions in props */
|
|
853
866
|
functions?: Record<string, ComputedFunction>;
|
|
867
|
+
/** Custom directives for user-defined `$`-prefixed dynamic values */
|
|
868
|
+
directives?: DirectiveDefinition[];
|
|
854
869
|
loading?: boolean;
|
|
855
870
|
fallback?: Component;
|
|
856
871
|
}
|
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.",
|
|
@@ -1354,6 +1355,29 @@ function useFunctions() {
|
|
|
1354
1355
|
(0, import_vue7.computed)(() => EMPTY_FUNCTIONS)
|
|
1355
1356
|
);
|
|
1356
1357
|
}
|
|
1358
|
+
var DIRECTIVES_KEY = /* @__PURE__ */ Symbol("json-render:directives");
|
|
1359
|
+
var DirectivesProvider = (0, import_vue7.defineComponent)({
|
|
1360
|
+
name: "DirectivesProvider",
|
|
1361
|
+
props: {
|
|
1362
|
+
directives: {
|
|
1363
|
+
type: Array,
|
|
1364
|
+
default: void 0
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1367
|
+
setup(props, { slots }) {
|
|
1368
|
+
const registry = (0, import_vue7.computed)(
|
|
1369
|
+
() => props.directives ? (0, import_core7.createDirectiveRegistry)(props.directives) : void 0
|
|
1370
|
+
);
|
|
1371
|
+
(0, import_vue7.provide)(DIRECTIVES_KEY, registry);
|
|
1372
|
+
return () => slots.default?.();
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
function useDirectives() {
|
|
1376
|
+
return (0, import_vue7.inject)(
|
|
1377
|
+
DIRECTIVES_KEY,
|
|
1378
|
+
(0, import_vue7.computed)(() => void 0)
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1357
1381
|
var ElementErrorBoundary = (0, import_vue7.defineComponent)({
|
|
1358
1382
|
name: "ElementErrorBoundary",
|
|
1359
1383
|
props: {
|
|
@@ -1441,6 +1465,7 @@ var ElementRenderer = (0, import_vue7.defineComponent)({
|
|
|
1441
1465
|
const { execute } = useActions();
|
|
1442
1466
|
const { getSnapshot, state: watchState } = useStateStore();
|
|
1443
1467
|
const functions = useFunctions();
|
|
1468
|
+
const directives = useDirectives();
|
|
1444
1469
|
const fullCtx = (0, import_vue7.computed)(() => {
|
|
1445
1470
|
const base = repeatScope ? {
|
|
1446
1471
|
...visibilityCtx.value,
|
|
@@ -1449,6 +1474,7 @@ var ElementRenderer = (0, import_vue7.defineComponent)({
|
|
|
1449
1474
|
repeatBasePath: repeatScope.basePath
|
|
1450
1475
|
} : { ...visibilityCtx.value };
|
|
1451
1476
|
base.functions = functions.value;
|
|
1477
|
+
base.directives = directives.value;
|
|
1452
1478
|
return base;
|
|
1453
1479
|
});
|
|
1454
1480
|
const emitEvent = async (eventName) => {
|
|
@@ -1607,10 +1633,20 @@ var RepeatChildren = (0, import_vue7.defineComponent)({
|
|
|
1607
1633
|
},
|
|
1608
1634
|
setup(props) {
|
|
1609
1635
|
const { state } = useStateStore();
|
|
1636
|
+
const parentScope = useRepeatScope();
|
|
1610
1637
|
return () => {
|
|
1611
1638
|
const repeat = props.element.repeat;
|
|
1612
1639
|
if (!repeat?.statePath) return null;
|
|
1613
|
-
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
|
+
}
|
|
1614
1650
|
const raw = (0, import_core7.getByPath)(state.value, statePath);
|
|
1615
1651
|
const items = Array.isArray(raw) ? raw : [];
|
|
1616
1652
|
return items.map((itemValue, index) => {
|
|
@@ -1619,7 +1655,12 @@ var RepeatChildren = (0, import_vue7.defineComponent)({
|
|
|
1619
1655
|
) : String(index);
|
|
1620
1656
|
return (0, import_vue7.h)(
|
|
1621
1657
|
RepeatScopeProvider,
|
|
1622
|
-
{
|
|
1658
|
+
{
|
|
1659
|
+
key,
|
|
1660
|
+
item: itemValue,
|
|
1661
|
+
index,
|
|
1662
|
+
basePath: (0, import_core7.resolveRepeatItemStatePath)(statePath, index)
|
|
1663
|
+
},
|
|
1623
1664
|
{
|
|
1624
1665
|
default: () => props.element.children?.map((childKey) => {
|
|
1625
1666
|
const childElement = props.spec.elements[childKey];
|
|
@@ -1728,6 +1769,10 @@ var JSONUIProvider = (0, import_vue7.defineComponent)({
|
|
|
1728
1769
|
type: Object,
|
|
1729
1770
|
default: void 0
|
|
1730
1771
|
},
|
|
1772
|
+
directives: {
|
|
1773
|
+
type: Array,
|
|
1774
|
+
default: void 0
|
|
1775
|
+
},
|
|
1731
1776
|
onStateChange: {
|
|
1732
1777
|
type: Function,
|
|
1733
1778
|
default: void 0
|
|
@@ -1755,10 +1800,16 @@ var JSONUIProvider = (0, import_vue7.defineComponent)({
|
|
|
1755
1800
|
FunctionsProvider,
|
|
1756
1801
|
{ functions: props.functions },
|
|
1757
1802
|
{
|
|
1758
|
-
default: () =>
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1803
|
+
default: () => (0, import_vue7.h)(
|
|
1804
|
+
DirectivesProvider,
|
|
1805
|
+
{ directives: props.directives },
|
|
1806
|
+
{
|
|
1807
|
+
default: () => [
|
|
1808
|
+
slots.default?.(),
|
|
1809
|
+
(0, import_vue7.h)(ConfirmationDialogManager)
|
|
1810
|
+
]
|
|
1811
|
+
}
|
|
1812
|
+
)
|
|
1762
1813
|
}
|
|
1763
1814
|
)
|
|
1764
1815
|
}
|
|
@@ -1864,6 +1915,10 @@ function createRenderer(catalog, components) {
|
|
|
1864
1915
|
type: Object,
|
|
1865
1916
|
default: void 0
|
|
1866
1917
|
},
|
|
1918
|
+
directives: {
|
|
1919
|
+
type: Array,
|
|
1920
|
+
default: void 0
|
|
1921
|
+
},
|
|
1867
1922
|
loading: {
|
|
1868
1923
|
type: Boolean,
|
|
1869
1924
|
default: void 0
|
|
@@ -1902,15 +1957,21 @@ function createRenderer(catalog, components) {
|
|
|
1902
1957
|
FunctionsProvider,
|
|
1903
1958
|
{ functions: rendererProps.functions },
|
|
1904
1959
|
{
|
|
1905
|
-
default: () =>
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1960
|
+
default: () => (0, import_vue7.h)(
|
|
1961
|
+
DirectivesProvider,
|
|
1962
|
+
{ directives: rendererProps.directives },
|
|
1963
|
+
{
|
|
1964
|
+
default: () => [
|
|
1965
|
+
(0, import_vue7.h)(Renderer, {
|
|
1966
|
+
spec: rendererProps.spec,
|
|
1967
|
+
registry,
|
|
1968
|
+
loading: rendererProps.loading,
|
|
1969
|
+
fallback: rendererProps.fallback
|
|
1970
|
+
}),
|
|
1971
|
+
(0, import_vue7.h)(ConfirmationDialogManager)
|
|
1972
|
+
]
|
|
1973
|
+
}
|
|
1974
|
+
)
|
|
1914
1975
|
}
|
|
1915
1976
|
)
|
|
1916
1977
|
}
|