ngx-json-render 0.7.8 → 0.8.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.
@@ -0,0 +1,107 @@
1
+ import { defineSchema } from '@json-render/core';
2
+
3
+ /**
4
+ * The schema for ngx-json-render.
5
+ *
6
+ * Defines:
7
+ * - Spec: A flat tree of elements with keys, types, props, and children references
8
+ * - Catalog: Components with props schemas, and optional actions
9
+ *
10
+ * This is the same spec grammar the other json-render renderers use, so
11
+ * catalogs and specs are portable across frameworks.
12
+ */
13
+ const schema = defineSchema((s) => ({
14
+ // What the AI-generated SPEC looks like
15
+ spec: s.object({
16
+ /** Root element key */
17
+ root: s.string(),
18
+ /** Flat map of elements by key */
19
+ elements: s.record(s.object({
20
+ /** Component type from catalog */
21
+ type: s.ref('catalog.components'),
22
+ /** Component props */
23
+ props: s.propsOf('catalog.components'),
24
+ /** Child element keys (flat reference) */
25
+ children: s.array(s.string()),
26
+ /** Visibility condition */
27
+ visible: { ...s.any(), ...s.optional() },
28
+ /** Repeat children from a state array */
29
+ repeat: { ...s.any(), ...s.optional() },
30
+ })),
31
+ }),
32
+ // What the CATALOG must provide
33
+ catalog: s.object({
34
+ /** Component definitions */
35
+ components: s.map({
36
+ /** Zod schema for component props */
37
+ props: s.zod(),
38
+ /** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */
39
+ slots: s.array(s.string()),
40
+ /** Description for AI generation hints */
41
+ description: s.string(),
42
+ /** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
43
+ example: s.any(),
44
+ }),
45
+ /** Action definitions (optional) */
46
+ actions: s.map({
47
+ /** Zod schema for action params */
48
+ params: s.zod(),
49
+ /** Description for AI generation hints */
50
+ description: s.string(),
51
+ }),
52
+ }),
53
+ }), {
54
+ builtInActions: [
55
+ {
56
+ name: 'setState',
57
+ description: 'Update a value in the state model at the given statePath. Params: { statePath: string, value: any }',
58
+ },
59
+ {
60
+ name: 'pushState',
61
+ description: '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.',
62
+ },
63
+ {
64
+ name: 'removeState',
65
+ description: 'Remove an item from an array in state by index. Params: { statePath: string, index: number }',
66
+ },
67
+ {
68
+ name: 'validateForm',
69
+ description: 'Validate all registered form fields and write the result to state. Params: { statePath?: string }. Defaults to /formValidation. Result: { valid: boolean, errors: Record<string, string[]> }.',
70
+ },
71
+ {
72
+ name: 'submitForm',
73
+ description: "Validate all registered form fields and, only if every one passes, dispatch another action. Params: { action: string, params?: object, statePath?: string }. Writes the same { valid, errors } result as validateForm. Use this on a form's submit button: it is validateForm and the submit in one binding, and the submit is skipped when the form is invalid.",
74
+ },
75
+ ],
76
+ defaultRules: [
77
+ // Element integrity
78
+ "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.",
79
+ '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.',
80
+ 'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
81
+ // Field placement
82
+ 'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
83
+ '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.',
84
+ // State and data
85
+ '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).',
86
+ '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. 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.',
87
+ // Design quality
88
+ '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.',
89
+ '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.',
90
+ '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.',
91
+ ],
92
+ });
93
+
94
+ /*
95
+ * Public API Surface of ngx-json-render/schema
96
+ *
97
+ * The spec grammar alone, with nothing but `@json-render/core` behind it, so
98
+ * a server can define a catalog without loading Angular. The primary entry
99
+ * point re-exports it.
100
+ */
101
+
102
+ /**
103
+ * Generated bundle index. Do not edit.
104
+ */
105
+
106
+ export { schema };
107
+ //# sourceMappingURL=ngx-json-render-schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ngx-json-render-schema.mjs","sources":["../../../projects/ngx-json-render/schema/src/schema.ts","../../../projects/ngx-json-render/schema/src/public-api.ts","../../../projects/ngx-json-render/schema/src/ngx-json-render-schema.ts"],"sourcesContent":["import { defineSchema } from '@json-render/core';\n\n/**\n * The schema for ngx-json-render.\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 *\n * This is the same spec grammar the other json-render renderers use, so\n * catalogs and specs are portable across frameworks.\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 name: 'submitForm',\n description:\n \"Validate all registered form fields and, only if every one passes, dispatch another action. Params: { action: string, params?: object, statePath?: string }. Writes the same { valid, errors } result as validateForm. Use this on a form's submit button: it is validateForm and the submit in one binding, and the submit is skipped when the form is invalid.\",\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. 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 Angular schema\n */\nexport type AngularSchema = typeof schema;\n\n/**\n * Infer the spec type from a catalog\n */\nexport type AngularSpec<TCatalog> = typeof schema extends {\n createCatalog: (catalog: TCatalog) => { _specType: infer S };\n}\n ? S\n : never;\n","/*\n * Public API Surface of ngx-json-render/schema\n *\n * The spec grammar alone, with nothing but `@json-render/core` behind it, so\n * a server can define a catalog without loading Angular. The primary entry\n * point re-exports it.\n */\n\nexport { schema, type AngularSchema, type AngularSpec } from './schema';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;AAEA;;;;;;;;;AASG;AACI,MAAM,MAAM,GAAG,YAAY,CAChC,CAAC,CAAC,MAAM;;AAEN,IAAA,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;;AAEb,QAAA,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;;QAEhB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAChB,CAAC,CAAC,MAAM,CAAC;;AAEP,YAAA,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,oBAAoB,CAAC;;AAEjC,YAAA,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;;YAEtC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;;AAE7B,YAAA,OAAO,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;;AAExC,YAAA,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;AACxC,SAAA,CAAC,CACH;KACF,CAAC;;AAGF,IAAA,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;;AAEhB,QAAA,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC;;AAEhB,YAAA,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE;;YAEd,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;;AAE1B,YAAA,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;;AAEvB,YAAA,OAAO,EAAE,CAAC,CAAC,GAAG,EAAE;SACjB,CAAC;;AAEF,QAAA,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC;;AAEb,YAAA,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE;;AAEf,YAAA,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;SACxB,CAAC;KACH,CAAC;AACH,CAAA,CAAC,EACF;AACE,IAAA,cAAc,EAAE;AACd,QAAA;AACE,YAAA,IAAI,EAAE,UAAU;AAChB,YAAA,WAAW,EACT,qGAAqG;AACxG,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,WAAW;AACjB,YAAA,WAAW,EACT,4KAA4K;AAC/K,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,aAAa;AACnB,YAAA,WAAW,EACT,8FAA8F;AACjG,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,cAAc;AACpB,YAAA,WAAW,EACT,+LAA+L;AAClM,SAAA;AACD,QAAA;AACE,YAAA,IAAI,EAAE,YAAY;AAClB,YAAA,WAAW,EACT,kWAAkW;AACrW,SAAA;AACF,KAAA;AACD,IAAA,YAAY,EAAE;;QAEZ,2TAA2T;QAC3T,8MAA8M;QAC9M,wLAAwL;;QAGxL,wLAAwL;QACxL,8JAA8J;;QAG9J,0NAA0N;QAC1N,+sBAA+sB;;QAG/sB,gNAAgN;QAChN,kMAAkM;QAClM,6MAA6M;AAC9M,KAAA;AACF,CAAA;;ACxGH;;;;;;AAMG;;ACNH;;AAEG;;"}
@@ -1,8 +1,9 @@
1
1
  import { NgComponentOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
3
  import { signal, Injectable, isDevMode, inject, computed, untracked, effect, DestroyRef, InjectionToken, input, output, viewChild, afterNextRender, ChangeDetectionStrategy, Component, Injector, forwardRef } from '@angular/core';
4
- import { createStateStore, getByPath, runValidation, resolveAction, nextActionDispatchId, notifyActionDispatch, notifyActionSettle, executeAction, isDevtoolsActive, subscribeDevtoolsActive, resolveRepeatStatePath, evaluateVisibility, resolveElementProps, resolveBindings, resolveActionParam, autoFixSpec, validateSpec, createDirectiveRegistry, resolveRepeatItemStatePath, removeByPath, addByPath, setByPath, SPEC_DATA_PART_TYPE, nestedToFlat, createMixedStreamParser, defineSchema } from '@json-render/core';
4
+ import { createStateStore, getByPath, runValidation, resolveAction, nextActionDispatchId, notifyActionDispatch, notifyActionSettle, executeAction, isDevtoolsActive, subscribeDevtoolsActive, resolveRepeatStatePath, evaluateVisibility, resolveElementProps, resolveBindings, resolveActionParam, autoFixSpec, validateSpec, createDirectiveRegistry, resolveRepeatItemStatePath, removeByPath, addByPath, setByPath, SPEC_DATA_PART_TYPE, nestedToFlat, createMixedStreamParser } from '@json-render/core';
5
5
  export { autoFixSpec, createStateStore, formatSpecIssues, nestedToFlat, validateSpec } from '@json-render/core';
6
+ export { schema } from 'ngx-json-render/schema';
6
7
 
7
8
  const EMPTY = signal(undefined, ...(ngDevMode ? [{ debugName: "EMPTY" }] : /* istanbul ignore next */ []));
8
9
  /**
@@ -3138,6 +3139,34 @@ function applyPatch(spec, patch) {
3138
3139
  return newSpec;
3139
3140
  }
3140
3141
 
3142
+ const observers = new Set();
3143
+ /**
3144
+ * Register an observer for every stream in the app. Returns the function that
3145
+ * removes it. Private API for `ngx-json-render/devtools`.
3146
+ */
3147
+ function ɵregisterStreamObserver(observer) {
3148
+ observers.add(observer);
3149
+ return () => {
3150
+ observers.delete(observer);
3151
+ };
3152
+ }
3153
+ /**
3154
+ * Tell every observer. An observer that throws is a devtools bug, and it must
3155
+ * not become a failed generation in the app it is inspecting.
3156
+ */
3157
+ function notifyStreamObservers(event, ...args) {
3158
+ if (observers.size === 0)
3159
+ return;
3160
+ for (const observer of observers) {
3161
+ try {
3162
+ observer[event]?.(...args);
3163
+ }
3164
+ catch (error) {
3165
+ console.error('[ngx-json-render] A stream observer threw:', error);
3166
+ }
3167
+ }
3168
+ }
3169
+
3141
3170
  /**
3142
3171
  * Convert a flat element list to a Spec.
3143
3172
  * Input elements use key/parentKey to establish identity and relationships.
@@ -3461,6 +3490,8 @@ function injectUIStream(options) {
3461
3490
  ? { ...previousSpec, elements: { ...previousSpec.elements } }
3462
3491
  : { root: '', elements: {} };
3463
3492
  spec.set(currentSpec);
3493
+ notifyStreamObservers('onStart');
3494
+ let failed = false;
3464
3495
  const handleLine = (line) => {
3465
3496
  const parsed = parseLine(line);
3466
3497
  if (!parsed)
@@ -3468,11 +3499,13 @@ function injectUIStream(options) {
3468
3499
  gate.commit(() => {
3469
3500
  if (parsed.type === 'usage') {
3470
3501
  usage.set(parsed.usage);
3502
+ notifyStreamObservers('onUsage', parsed.usage);
3471
3503
  return;
3472
3504
  }
3473
3505
  rawLines.update((prev) => [...prev, line]);
3474
3506
  currentSpec = applyPatch(currentSpec, parsed.patch);
3475
3507
  spec.set(currentSpec);
3508
+ notifyStreamObservers('onPatch', parsed.patch);
3476
3509
  });
3477
3510
  };
3478
3511
  try {
@@ -3503,6 +3536,7 @@ function injectUIStream(options) {
3503
3536
  // not want rendered.
3504
3537
  if (check.blocked || (validate === 'strict' && check.hasErrors)) {
3505
3538
  const invalid = new Error(`Generated spec failed validation:\n${formatSpecCheckIssues(check.issues)}`);
3539
+ failed = true;
3506
3540
  error.set(invalid);
3507
3541
  options.onError?.(invalid);
3508
3542
  return;
@@ -3513,6 +3547,7 @@ function injectUIStream(options) {
3513
3547
  catch (err) {
3514
3548
  if (isAbortError(err))
3515
3549
  return;
3550
+ failed = true;
3516
3551
  const resolvedError = err instanceof Error ? err : new Error(String(err));
3517
3552
  gate.commit(() => {
3518
3553
  error.set(resolvedError);
@@ -3521,6 +3556,7 @@ function injectUIStream(options) {
3521
3556
  }
3522
3557
  finally {
3523
3558
  gate.commit(() => isStreaming.set(false));
3559
+ notifyStreamObservers('onEnd', !failed);
3524
3560
  }
3525
3561
  };
3526
3562
  return {
@@ -3621,6 +3657,8 @@ function injectChatUI(options) {
3621
3657
  messages.update((prev) => [...prev, userMessage, assistantMessage]);
3622
3658
  isStreaming.set(true);
3623
3659
  error.set(null);
3660
+ notifyStreamObservers('onStart');
3661
+ let failed = false;
3624
3662
  // Mutable state for accumulating the assistant response
3625
3663
  let accumulatedText = '';
3626
3664
  let currentSpec = { root: '', elements: {} };
@@ -3632,12 +3670,18 @@ function injectChatUI(options) {
3632
3670
  // touch, so the message can hold it directly — no snapshot copy.
3633
3671
  currentSpec = applyPatch(currentSpec, patch);
3634
3672
  const snapshot = currentSpec;
3635
- gate.commit(() => messages.update((prev) => prev.map((m) => m.id === assistantId ? { ...m, spec: snapshot } : m)));
3673
+ gate.commit(() => {
3674
+ messages.update((prev) => prev.map((m) => m.id === assistantId ? { ...m, spec: snapshot } : m));
3675
+ notifyStreamObservers('onPatch', patch);
3676
+ });
3636
3677
  },
3637
3678
  onText(line) {
3638
3679
  accumulatedText += (accumulatedText ? '\n' : '') + line;
3639
3680
  const snapshot = accumulatedText;
3640
- gate.commit(() => messages.update((prev) => prev.map((m) => m.id === assistantId ? { ...m, text: snapshot } : m)));
3681
+ gate.commit(() => {
3682
+ messages.update((prev) => prev.map((m) => m.id === assistantId ? { ...m, text: snapshot } : m));
3683
+ notifyStreamObservers('onText', line);
3684
+ });
3641
3685
  },
3642
3686
  });
3643
3687
  try {
@@ -3671,6 +3715,7 @@ function injectChatUI(options) {
3671
3715
  // not want rendered.
3672
3716
  if (check.blocked || (validate === 'strict' && check.hasErrors)) {
3673
3717
  const invalid = new Error(`Generated spec failed validation:\n${formatSpecCheckIssues(check.issues)}`);
3718
+ failed = true;
3674
3719
  error.set(invalid);
3675
3720
  options.onError?.(invalid);
3676
3721
  return;
@@ -3686,6 +3731,7 @@ function injectChatUI(options) {
3686
3731
  dropEmptyPlaceholder();
3687
3732
  if (isAbortError(err))
3688
3733
  return;
3734
+ failed = true;
3689
3735
  const resolvedError = err instanceof Error ? err : new Error(String(err));
3690
3736
  gate.commit(() => {
3691
3737
  error.set(resolvedError);
@@ -3694,6 +3740,7 @@ function injectChatUI(options) {
3694
3740
  }
3695
3741
  finally {
3696
3742
  gate.commit(() => isStreaming.set(false));
3743
+ notifyStreamObservers('onEnd', !failed);
3697
3744
  }
3698
3745
  };
3699
3746
  return {
@@ -3716,97 +3763,6 @@ function injectChatUI(options) {
3716
3763
  * - `ui-stream` / `chat-ui` — the two hooks, composed from the above.
3717
3764
  */
3718
3765
 
3719
- /**
3720
- * The schema for ngx-json-render.
3721
- *
3722
- * Defines:
3723
- * - Spec: A flat tree of elements with keys, types, props, and children references
3724
- * - Catalog: Components with props schemas, and optional actions
3725
- *
3726
- * This is the same spec grammar the other json-render renderers use, so
3727
- * catalogs and specs are portable across frameworks.
3728
- */
3729
- const schema = defineSchema((s) => ({
3730
- // What the AI-generated SPEC looks like
3731
- spec: s.object({
3732
- /** Root element key */
3733
- root: s.string(),
3734
- /** Flat map of elements by key */
3735
- elements: s.record(s.object({
3736
- /** Component type from catalog */
3737
- type: s.ref('catalog.components'),
3738
- /** Component props */
3739
- props: s.propsOf('catalog.components'),
3740
- /** Child element keys (flat reference) */
3741
- children: s.array(s.string()),
3742
- /** Visibility condition */
3743
- visible: { ...s.any(), ...s.optional() },
3744
- /** Repeat children from a state array */
3745
- repeat: { ...s.any(), ...s.optional() },
3746
- })),
3747
- }),
3748
- // What the CATALOG must provide
3749
- catalog: s.object({
3750
- /** Component definitions */
3751
- components: s.map({
3752
- /** Zod schema for component props */
3753
- props: s.zod(),
3754
- /** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */
3755
- slots: s.array(s.string()),
3756
- /** Description for AI generation hints */
3757
- description: s.string(),
3758
- /** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
3759
- example: s.any(),
3760
- }),
3761
- /** Action definitions (optional) */
3762
- actions: s.map({
3763
- /** Zod schema for action params */
3764
- params: s.zod(),
3765
- /** Description for AI generation hints */
3766
- description: s.string(),
3767
- }),
3768
- }),
3769
- }), {
3770
- builtInActions: [
3771
- {
3772
- name: 'setState',
3773
- description: 'Update a value in the state model at the given statePath. Params: { statePath: string, value: any }',
3774
- },
3775
- {
3776
- name: 'pushState',
3777
- description: '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.',
3778
- },
3779
- {
3780
- name: 'removeState',
3781
- description: 'Remove an item from an array in state by index. Params: { statePath: string, index: number }',
3782
- },
3783
- {
3784
- name: 'validateForm',
3785
- description: 'Validate all registered form fields and write the result to state. Params: { statePath?: string }. Defaults to /formValidation. Result: { valid: boolean, errors: Record<string, string[]> }.',
3786
- },
3787
- {
3788
- name: 'submitForm',
3789
- description: "Validate all registered form fields and, only if every one passes, dispatch another action. Params: { action: string, params?: object, statePath?: string }. Writes the same { valid, errors } result as validateForm. Use this on a form's submit button: it is validateForm and the submit in one binding, and the submit is skipped when the form is invalid.",
3790
- },
3791
- ],
3792
- defaultRules: [
3793
- // Element integrity
3794
- "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.",
3795
- '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.',
3796
- 'REQUIRED FIELDS: Every element MUST include a "children" array. Leaf elements (text, badges, inputs, images) use an empty array: "children": []. Omitting "children" fails validation.',
3797
- // Field placement
3798
- 'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/tab","eq":"home"},"children":[...]}.',
3799
- '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.',
3800
- // State and data
3801
- '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).',
3802
- '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. 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.',
3803
- // Design quality
3804
- '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.',
3805
- '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.',
3806
- '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.',
3807
- ],
3808
- });
3809
-
3810
3766
  /*
3811
3767
  * Public API Surface of ngx-json-render
3812
3768
  */
@@ -3816,5 +3772,5 @@ const schema = defineSchema((s) => ({
3816
3772
  * Generated bundle index. Do not edit.
3817
3773
  */
3818
3774
 
3819
- export { CONFIRM_CONTEXT, ELEMENT_KEY, JR_CONFIRM_DIALOG, JR_CONFIRM_LABELS, JrChildren, JrConfirmDialog, JrElement, JrRepeatScope, JsonRenderActionsService, JsonRenderRootContext, JsonRenderStateService, JsonRenderValidationService, JsonRenderer, RENDER_CONTEXT, REPEAT_SCOPE, applyPatch, buildSpecFromParts, checkSpec, createStoreSetState, defineRegistry, flatToTree, formatSpecCheckIssues, getTextFromParts, injectAction, injectActions, injectBoundProp, injectChatUI, injectConfirmContext, injectDevtoolsActive, injectElementKey, injectFieldValidation, injectRenderContext, injectRepeatScope, injectStateBinding, injectStateStore, injectStateValue, injectUIStream, injectValidation, isActionCancelled, jsonRenderMessage, schema };
3775
+ export { CONFIRM_CONTEXT, ELEMENT_KEY, JR_CONFIRM_DIALOG, JR_CONFIRM_LABELS, JrChildren, JrConfirmDialog, JrElement, JrRepeatScope, JsonRenderActionsService, JsonRenderRootContext, JsonRenderStateService, JsonRenderValidationService, JsonRenderer, RENDER_CONTEXT, REPEAT_SCOPE, applyPatch, buildSpecFromParts, checkSpec, createStoreSetState, defineRegistry, flatToTree, formatSpecCheckIssues, getTextFromParts, injectAction, injectActions, injectBoundProp, injectChatUI, injectConfirmContext, injectDevtoolsActive, injectElementKey, injectFieldValidation, injectRenderContext, injectRepeatScope, injectStateBinding, injectStateStore, injectStateValue, injectUIStream, injectValidation, isActionCancelled, jsonRenderMessage, ɵregisterStreamObserver };
3820
3776
  //# sourceMappingURL=ngx-json-render.mjs.map