@json-render/vue 0.9.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/dist/index.mjs ADDED
@@ -0,0 +1,1849 @@
1
+ import {
2
+ schema
3
+ } from "./chunk-WIPZLAF7.mjs";
4
+
5
+ // src/composables/state.ts
6
+ import {
7
+ computed,
8
+ defineComponent,
9
+ inject,
10
+ onUnmounted,
11
+ provide,
12
+ ref,
13
+ shallowRef,
14
+ watch
15
+ } from "vue";
16
+ import {
17
+ createStateStore,
18
+ getByPath
19
+ } from "@json-render/core";
20
+ import { flattenToPointers } from "@json-render/core/store-utils";
21
+ var STATE_KEY = /* @__PURE__ */ Symbol("json-render:state");
22
+ var StateProvider = defineComponent({
23
+ name: "StateProvider",
24
+ props: {
25
+ store: {
26
+ type: Object,
27
+ default: void 0
28
+ },
29
+ initialState: {
30
+ type: Object,
31
+ default: void 0
32
+ },
33
+ onStateChange: {
34
+ type: Function,
35
+ default: void 0
36
+ }
37
+ },
38
+ setup(props, { slots }) {
39
+ const isControlled = !!props.store;
40
+ const internalStore = !isControlled ? createStateStore(props.initialState ?? {}) : null;
41
+ const store = props.store ?? internalStore;
42
+ const state = shallowRef(store.getSnapshot());
43
+ const unsubscribe = store.subscribe(() => {
44
+ state.value = store.getSnapshot();
45
+ });
46
+ onUnmounted(unsubscribe);
47
+ if (!isControlled) {
48
+ let prevFlat = props.initialState && Object.keys(props.initialState).length > 0 ? flattenToPointers(props.initialState) : {};
49
+ watch(
50
+ () => props.initialState,
51
+ (newInitialState) => {
52
+ if (!newInitialState) return;
53
+ const nextFlat = Object.keys(newInitialState).length > 0 ? flattenToPointers(newInitialState) : {};
54
+ const allKeys = /* @__PURE__ */ new Set([
55
+ ...Object.keys(prevFlat),
56
+ ...Object.keys(nextFlat)
57
+ ]);
58
+ const updates = {};
59
+ for (const key of allKeys) {
60
+ if (prevFlat[key] !== nextFlat[key]) {
61
+ updates[key] = key in nextFlat ? nextFlat[key] : void 0;
62
+ }
63
+ }
64
+ prevFlat = nextFlat;
65
+ if (Object.keys(updates).length > 0) {
66
+ store.update(updates);
67
+ }
68
+ }
69
+ );
70
+ }
71
+ const onStateChangeRef = ref(props.onStateChange);
72
+ watch(
73
+ () => props.onStateChange,
74
+ (fn) => {
75
+ onStateChangeRef.value = fn;
76
+ }
77
+ );
78
+ const get = (path) => store.get(path);
79
+ const getSnapshot = () => store.getSnapshot();
80
+ const set = (path, value) => {
81
+ const prev = store.getSnapshot();
82
+ store.set(path, value);
83
+ if (!isControlled && store.getSnapshot() !== prev) {
84
+ onStateChangeRef.value?.([{ path, value }]);
85
+ }
86
+ };
87
+ const update = (updates) => {
88
+ const prev = store.getSnapshot();
89
+ store.update(updates);
90
+ if (!isControlled && store.getSnapshot() !== prev) {
91
+ const changes = [];
92
+ for (const [path, value] of Object.entries(updates)) {
93
+ if (getByPath(prev, path) !== value) {
94
+ changes.push({ path, value });
95
+ }
96
+ }
97
+ if (changes.length > 0) {
98
+ onStateChangeRef.value?.(changes);
99
+ }
100
+ }
101
+ };
102
+ provide(STATE_KEY, {
103
+ state,
104
+ get,
105
+ set,
106
+ update,
107
+ getSnapshot
108
+ });
109
+ return () => slots.default?.();
110
+ }
111
+ });
112
+ function useStateStore() {
113
+ const ctx = inject(STATE_KEY);
114
+ if (!ctx) {
115
+ throw new Error("useStateStore must be used within a StateProvider");
116
+ }
117
+ return ctx;
118
+ }
119
+ function useStateValue(path) {
120
+ const { state } = useStateStore();
121
+ return computed(() => getByPath(state.value, path));
122
+ }
123
+ function useStateBinding(path) {
124
+ const { state, set } = useStateStore();
125
+ const value = computed(() => getByPath(state.value, path));
126
+ const setValue = (newValue) => set(path, newValue);
127
+ return [value, setValue];
128
+ }
129
+
130
+ // src/composables/visibility.ts
131
+ import {
132
+ computed as computed2,
133
+ defineComponent as defineComponent2,
134
+ inject as inject2,
135
+ provide as provide2
136
+ } from "vue";
137
+ import {
138
+ evaluateVisibility
139
+ } from "@json-render/core";
140
+ var VISIBILITY_KEY = /* @__PURE__ */ Symbol("json-render:visibility");
141
+ var VisibilityProvider = defineComponent2({
142
+ name: "VisibilityProvider",
143
+ setup(_, { slots }) {
144
+ const { state } = useStateStore();
145
+ const ctx = computed2(() => ({
146
+ stateModel: state.value
147
+ }));
148
+ const isVisible = (condition) => evaluateVisibility(condition, ctx.value);
149
+ provide2(VISIBILITY_KEY, { isVisible, ctx });
150
+ return () => slots.default?.();
151
+ }
152
+ });
153
+ function useVisibility() {
154
+ const ctx = inject2(VISIBILITY_KEY);
155
+ if (!ctx) {
156
+ throw new Error("useVisibility must be used within a VisibilityProvider");
157
+ }
158
+ return ctx;
159
+ }
160
+ function useIsVisible(condition) {
161
+ const { ctx } = useVisibility();
162
+ return computed2(() => evaluateVisibility(condition, ctx.value));
163
+ }
164
+
165
+ // src/composables/actions.ts
166
+ import {
167
+ computed as computed4,
168
+ defineComponent as defineComponent4,
169
+ h,
170
+ inject as inject4,
171
+ provide as provide4,
172
+ ref as ref3,
173
+ watch as watch2
174
+ } from "vue";
175
+ import {
176
+ resolveAction,
177
+ executeAction
178
+ } from "@json-render/core";
179
+
180
+ // src/composables/validation.ts
181
+ import {
182
+ computed as computed3,
183
+ defineComponent as defineComponent3,
184
+ inject as inject3,
185
+ onMounted,
186
+ onUnmounted as onUnmounted2,
187
+ provide as provide3,
188
+ ref as ref2
189
+ } from "vue";
190
+ import {
191
+ runValidation
192
+ } from "@json-render/core";
193
+ var VALIDATION_KEY = /* @__PURE__ */ Symbol("json-render:validation");
194
+ function dynamicArgsEqual(a, b) {
195
+ if (a === b) return true;
196
+ if (!a || !b) return false;
197
+ const keysA = Object.keys(a);
198
+ const keysB = Object.keys(b);
199
+ if (keysA.length !== keysB.length) return false;
200
+ for (const key of keysA) {
201
+ const va = a[key];
202
+ const vb = b[key];
203
+ if (va === vb) continue;
204
+ if (typeof va === "object" && va !== null && typeof vb === "object" && vb !== null) {
205
+ const sa = va.$state;
206
+ const sb = vb.$state;
207
+ if (typeof sa === "string" && sa === sb) continue;
208
+ }
209
+ return false;
210
+ }
211
+ return true;
212
+ }
213
+ function validationConfigEqual(a, b) {
214
+ if (a === b) return true;
215
+ if (a.validateOn !== b.validateOn) return false;
216
+ const ac = a.checks ?? [];
217
+ const bc = b.checks ?? [];
218
+ if (ac.length !== bc.length) return false;
219
+ for (let i = 0; i < ac.length; i++) {
220
+ const ca = ac[i];
221
+ const cb = bc[i];
222
+ if (ca.type !== cb.type) return false;
223
+ if (ca.message !== cb.message) return false;
224
+ if (!dynamicArgsEqual(ca.args, cb.args)) return false;
225
+ }
226
+ return true;
227
+ }
228
+ var ValidationProvider = defineComponent3({
229
+ name: "ValidationProvider",
230
+ props: {
231
+ customFunctions: {
232
+ type: Object,
233
+ default: () => ({})
234
+ }
235
+ },
236
+ setup(props, { slots }) {
237
+ const { state } = useStateStore();
238
+ const fieldStates = ref2({});
239
+ const fieldConfigs = ref2({});
240
+ const registerField = (path, config) => {
241
+ const existing = fieldConfigs.value[path];
242
+ if (existing && validationConfigEqual(existing, config)) return;
243
+ fieldConfigs.value = { ...fieldConfigs.value, [path]: config };
244
+ };
245
+ const validate = (path, config) => {
246
+ const currentState = state.value;
247
+ const segments = path.split("/").filter(Boolean);
248
+ let value = currentState;
249
+ for (const seg of segments) {
250
+ if (value != null && typeof value === "object") {
251
+ value = value[seg];
252
+ } else {
253
+ value = void 0;
254
+ break;
255
+ }
256
+ }
257
+ const result = runValidation(config, {
258
+ value,
259
+ stateModel: currentState,
260
+ customFunctions: props.customFunctions
261
+ });
262
+ fieldStates.value = {
263
+ ...fieldStates.value,
264
+ [path]: {
265
+ touched: fieldStates.value[path]?.touched ?? true,
266
+ validated: true,
267
+ result
268
+ }
269
+ };
270
+ return result;
271
+ };
272
+ const touch = (path) => {
273
+ fieldStates.value = {
274
+ ...fieldStates.value,
275
+ [path]: {
276
+ ...fieldStates.value[path],
277
+ touched: true,
278
+ validated: fieldStates.value[path]?.validated ?? false,
279
+ result: fieldStates.value[path]?.result ?? null
280
+ }
281
+ };
282
+ };
283
+ const clear = (path) => {
284
+ const { [path]: _, ...rest } = fieldStates.value;
285
+ fieldStates.value = rest;
286
+ };
287
+ const validateAll = () => {
288
+ let allValid = true;
289
+ for (const [path, config] of Object.entries(fieldConfigs.value)) {
290
+ const result = validate(path, config);
291
+ if (!result.valid) {
292
+ allValid = false;
293
+ }
294
+ }
295
+ return allValid;
296
+ };
297
+ provide3(VALIDATION_KEY, {
298
+ get customFunctions() {
299
+ return props.customFunctions;
300
+ },
301
+ get fieldStates() {
302
+ return fieldStates.value;
303
+ },
304
+ validate,
305
+ touch,
306
+ clear,
307
+ validateAll,
308
+ registerField
309
+ });
310
+ return () => slots.default?.();
311
+ }
312
+ });
313
+ function useOptionalValidation() {
314
+ return inject3(
315
+ VALIDATION_KEY,
316
+ null
317
+ ) ?? null;
318
+ }
319
+ function useValidation() {
320
+ const ctx = inject3(VALIDATION_KEY);
321
+ if (!ctx) {
322
+ throw new Error("useValidation must be used within a ValidationProvider");
323
+ }
324
+ return ctx;
325
+ }
326
+ function useFieldValidation(path, config) {
327
+ const ctx = useValidation();
328
+ onMounted(() => {
329
+ if (path && config) {
330
+ ctx.registerField(path, config);
331
+ }
332
+ });
333
+ onUnmounted2(() => {
334
+ ctx.clear(path);
335
+ });
336
+ const defaultState = {
337
+ touched: false,
338
+ validated: false,
339
+ result: null
340
+ };
341
+ return {
342
+ state: computed3(() => ctx.fieldStates[path] ?? defaultState),
343
+ validate: () => ctx.validate(path, config ?? { checks: [] }),
344
+ touch: () => ctx.touch(path),
345
+ clear: () => ctx.clear(path),
346
+ errors: computed3(() => ctx.fieldStates[path]?.result?.errors ?? []),
347
+ isValid: computed3(() => ctx.fieldStates[path]?.result?.valid ?? true)
348
+ };
349
+ }
350
+
351
+ // src/composables/actions.ts
352
+ var idCounter = 0;
353
+ function generateUniqueId() {
354
+ idCounter += 1;
355
+ return `${Date.now()}-${idCounter}`;
356
+ }
357
+ function deepResolveValue(value, get) {
358
+ if (value === null || value === void 0) return value;
359
+ if (value === "$id") {
360
+ return generateUniqueId();
361
+ }
362
+ if (typeof value === "object" && !Array.isArray(value)) {
363
+ const obj = value;
364
+ const keys = Object.keys(obj);
365
+ if (keys.length === 1 && typeof obj.$state === "string") {
366
+ return get(obj.$state);
367
+ }
368
+ if (keys.length === 1 && "$id" in obj) {
369
+ return generateUniqueId();
370
+ }
371
+ }
372
+ if (Array.isArray(value)) {
373
+ return value.map((item) => deepResolveValue(item, get));
374
+ }
375
+ if (typeof value === "object") {
376
+ const resolved = {};
377
+ for (const [key, val] of Object.entries(value)) {
378
+ resolved[key] = deepResolveValue(val, get);
379
+ }
380
+ return resolved;
381
+ }
382
+ return value;
383
+ }
384
+ var ACTIONS_KEY = /* @__PURE__ */ Symbol("json-render:actions");
385
+ var ActionProvider = defineComponent4({
386
+ name: "ActionProvider",
387
+ props: {
388
+ handlers: {
389
+ type: Object,
390
+ default: () => ({})
391
+ },
392
+ navigate: {
393
+ type: Function,
394
+ default: void 0
395
+ }
396
+ },
397
+ setup(props, { slots }) {
398
+ const { get, set, getSnapshot } = useStateStore();
399
+ const validation = useOptionalValidation();
400
+ const handlers = ref3(props.handlers ?? {});
401
+ const loadingActions = ref3(/* @__PURE__ */ new Set());
402
+ const pendingConfirmation = ref3(null);
403
+ watch2(
404
+ () => props.handlers,
405
+ (newHandlers) => {
406
+ if (newHandlers) handlers.value = newHandlers;
407
+ }
408
+ );
409
+ const registerHandler = (name, handler) => {
410
+ handlers.value = { ...handlers.value, [name]: handler };
411
+ };
412
+ const execute = async (binding) => {
413
+ const resolved = resolveAction(binding, getSnapshot());
414
+ if (resolved.action === "setState" && resolved.params) {
415
+ const statePath = resolved.params.statePath;
416
+ const value = resolved.params.value;
417
+ if (statePath) {
418
+ set(statePath, value);
419
+ }
420
+ return;
421
+ }
422
+ if (resolved.action === "pushState" && resolved.params) {
423
+ const statePath = resolved.params.statePath;
424
+ const rawValue = resolved.params.value;
425
+ if (statePath) {
426
+ const resolvedValue = deepResolveValue(rawValue, get);
427
+ const arr = get(statePath) ?? [];
428
+ set(statePath, [...arr, resolvedValue]);
429
+ const clearStatePath = resolved.params.clearStatePath;
430
+ if (clearStatePath) {
431
+ set(clearStatePath, "");
432
+ }
433
+ }
434
+ return;
435
+ }
436
+ if (resolved.action === "removeState" && resolved.params) {
437
+ const statePath = resolved.params.statePath;
438
+ const index = resolved.params.index;
439
+ if (statePath !== void 0 && index !== void 0) {
440
+ const arr = get(statePath) ?? [];
441
+ set(
442
+ statePath,
443
+ arr.filter((_, i) => i !== index)
444
+ );
445
+ }
446
+ return;
447
+ }
448
+ if (resolved.action === "validateForm") {
449
+ const validateAll = validation?.validateAll;
450
+ if (!validateAll) {
451
+ console.warn(
452
+ "validateForm action was dispatched but no ValidationProvider is connected. Ensure ValidationProvider is rendered inside the provider tree."
453
+ );
454
+ return;
455
+ }
456
+ const valid = validateAll();
457
+ const errors = {};
458
+ for (const [path, fs] of Object.entries(validation.fieldStates)) {
459
+ if (fs.result && !fs.result.valid) {
460
+ errors[path] = fs.result.errors;
461
+ }
462
+ }
463
+ const statePath = resolved.params?.statePath || "/formValidation";
464
+ set(statePath, { valid, errors });
465
+ return;
466
+ }
467
+ if (resolved.action === "push" && resolved.params) {
468
+ const screen = resolved.params.screen;
469
+ if (screen) {
470
+ const currentScreen = get("/currentScreen");
471
+ const navStack = get("/navStack") ?? [];
472
+ if (currentScreen) {
473
+ set("/navStack", [...navStack, currentScreen]);
474
+ } else {
475
+ set("/navStack", [...navStack, ""]);
476
+ }
477
+ set("/currentScreen", screen);
478
+ }
479
+ return;
480
+ }
481
+ if (resolved.action === "pop") {
482
+ const navStack = get("/navStack") ?? [];
483
+ if (navStack.length > 0) {
484
+ const previousScreen = navStack[navStack.length - 1];
485
+ set("/navStack", navStack.slice(0, -1));
486
+ if (previousScreen) {
487
+ set("/currentScreen", previousScreen);
488
+ } else {
489
+ set("/currentScreen", void 0);
490
+ }
491
+ }
492
+ return;
493
+ }
494
+ const handler = handlers.value[resolved.action];
495
+ if (!handler) {
496
+ console.warn(`No handler registered for action: ${resolved.action}`);
497
+ return;
498
+ }
499
+ if (resolved.confirm) {
500
+ await new Promise((resolve, reject) => {
501
+ pendingConfirmation.value = {
502
+ action: resolved,
503
+ handler,
504
+ resolve: () => {
505
+ pendingConfirmation.value = null;
506
+ resolve();
507
+ },
508
+ reject: () => {
509
+ pendingConfirmation.value = null;
510
+ reject(new Error("Action cancelled"));
511
+ }
512
+ };
513
+ });
514
+ const addLoading2 = new Set(loadingActions.value);
515
+ addLoading2.add(resolved.action);
516
+ loadingActions.value = addLoading2;
517
+ try {
518
+ await executeAction({
519
+ action: resolved,
520
+ handler,
521
+ setState: set,
522
+ navigate: props.navigate,
523
+ executeAction: async (name) => {
524
+ const subBinding = { action: name };
525
+ await execute(subBinding);
526
+ }
527
+ });
528
+ } finally {
529
+ const removeLoading = new Set(loadingActions.value);
530
+ removeLoading.delete(resolved.action);
531
+ loadingActions.value = removeLoading;
532
+ }
533
+ return;
534
+ }
535
+ const addLoading = new Set(loadingActions.value);
536
+ addLoading.add(resolved.action);
537
+ loadingActions.value = addLoading;
538
+ try {
539
+ await executeAction({
540
+ action: resolved,
541
+ handler,
542
+ setState: set,
543
+ navigate: props.navigate,
544
+ executeAction: async (name) => {
545
+ const subBinding = { action: name };
546
+ await execute(subBinding);
547
+ }
548
+ });
549
+ } finally {
550
+ const removeLoading = new Set(loadingActions.value);
551
+ removeLoading.delete(resolved.action);
552
+ loadingActions.value = removeLoading;
553
+ }
554
+ };
555
+ const confirm = () => pendingConfirmation.value?.resolve();
556
+ const cancel = () => pendingConfirmation.value?.reject();
557
+ provide4(ACTIONS_KEY, {
558
+ get handlers() {
559
+ return handlers.value;
560
+ },
561
+ get loadingActions() {
562
+ return loadingActions.value;
563
+ },
564
+ get pendingConfirmation() {
565
+ return pendingConfirmation.value;
566
+ },
567
+ execute,
568
+ confirm,
569
+ cancel,
570
+ registerHandler
571
+ });
572
+ return () => slots.default?.();
573
+ }
574
+ });
575
+ function useActions() {
576
+ const ctx = inject4(ACTIONS_KEY);
577
+ if (!ctx) {
578
+ throw new Error("useActions must be used within an ActionProvider");
579
+ }
580
+ return ctx;
581
+ }
582
+ function useAction(binding) {
583
+ const ctx = useActions();
584
+ return {
585
+ execute: () => ctx.execute(binding),
586
+ isLoading: computed4(() => ctx.loadingActions.has(binding.action))
587
+ };
588
+ }
589
+ var ConfirmDialog = defineComponent4({
590
+ name: "ConfirmDialog",
591
+ props: {
592
+ confirm: {
593
+ type: Object,
594
+ required: true
595
+ },
596
+ onConfirm: {
597
+ type: Function,
598
+ required: true
599
+ },
600
+ onCancel: {
601
+ type: Function,
602
+ required: true
603
+ }
604
+ },
605
+ setup(props) {
606
+ return () => {
607
+ const isDanger = props.confirm.variant === "danger";
608
+ return h(
609
+ "div",
610
+ {
611
+ style: {
612
+ position: "fixed",
613
+ inset: "0",
614
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
615
+ display: "flex",
616
+ alignItems: "center",
617
+ justifyContent: "center",
618
+ zIndex: "50"
619
+ },
620
+ onClick: props.onCancel
621
+ },
622
+ [
623
+ h(
624
+ "div",
625
+ {
626
+ style: {
627
+ backgroundColor: "white",
628
+ borderRadius: "8px",
629
+ padding: "24px",
630
+ maxWidth: "400px",
631
+ width: "100%",
632
+ boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
633
+ },
634
+ onClick: (e) => e.stopPropagation()
635
+ },
636
+ [
637
+ h(
638
+ "h3",
639
+ {
640
+ style: {
641
+ margin: "0 0 8px 0",
642
+ fontSize: "18px",
643
+ fontWeight: "600"
644
+ }
645
+ },
646
+ props.confirm.title
647
+ ),
648
+ h(
649
+ "p",
650
+ {
651
+ style: { margin: "0 0 24px 0", color: "#6b7280" }
652
+ },
653
+ props.confirm.message
654
+ ),
655
+ h(
656
+ "div",
657
+ {
658
+ style: {
659
+ display: "flex",
660
+ gap: "12px",
661
+ justifyContent: "flex-end"
662
+ }
663
+ },
664
+ [
665
+ h(
666
+ "button",
667
+ {
668
+ style: {
669
+ padding: "8px 16px",
670
+ borderRadius: "6px",
671
+ border: "1px solid #d1d5db",
672
+ backgroundColor: "white",
673
+ cursor: "pointer"
674
+ },
675
+ onClick: props.onCancel
676
+ },
677
+ props.confirm.cancelLabel ?? "Cancel"
678
+ ),
679
+ h(
680
+ "button",
681
+ {
682
+ style: {
683
+ padding: "8px 16px",
684
+ borderRadius: "6px",
685
+ border: "none",
686
+ backgroundColor: isDanger ? "#dc2626" : "#3b82f6",
687
+ color: "white",
688
+ cursor: "pointer"
689
+ },
690
+ onClick: props.onConfirm
691
+ },
692
+ props.confirm.confirmLabel ?? "Confirm"
693
+ )
694
+ ]
695
+ )
696
+ ]
697
+ )
698
+ ]
699
+ );
700
+ };
701
+ }
702
+ });
703
+
704
+ // src/composables/repeat-scope.ts
705
+ import { defineComponent as defineComponent5, inject as inject5, provide as provide5 } from "vue";
706
+ var REPEAT_SCOPE_KEY = /* @__PURE__ */ Symbol("json-render:repeat-scope");
707
+ var RepeatScopeProvider = defineComponent5({
708
+ name: "RepeatScopeProvider",
709
+ props: {
710
+ item: {
711
+ required: true
712
+ },
713
+ index: {
714
+ type: Number,
715
+ required: true
716
+ },
717
+ basePath: {
718
+ type: String,
719
+ required: true
720
+ }
721
+ },
722
+ setup(props, { slots }) {
723
+ provide5(REPEAT_SCOPE_KEY, props);
724
+ return () => slots.default?.();
725
+ }
726
+ });
727
+ function useRepeatScope() {
728
+ return inject5(
729
+ REPEAT_SCOPE_KEY,
730
+ null
731
+ ) ?? null;
732
+ }
733
+
734
+ // src/index.ts
735
+ import { createStateStore as createStateStore2 } from "@json-render/core";
736
+
737
+ // src/hooks.ts
738
+ import {
739
+ ref as ref4,
740
+ shallowRef as shallowRef2,
741
+ computed as computed5,
742
+ onUnmounted as onUnmounted3,
743
+ isRef
744
+ } from "vue";
745
+ import {
746
+ setByPath,
747
+ getByPath as getByPath2,
748
+ removeByPath,
749
+ createMixedStreamParser,
750
+ applySpecPatch,
751
+ nestedToFlat,
752
+ SPEC_DATA_PART_TYPE
753
+ } from "@json-render/core";
754
+ function parseLine(line) {
755
+ try {
756
+ const trimmed = line.trim();
757
+ if (!trimmed || trimmed.startsWith("//")) {
758
+ return null;
759
+ }
760
+ const parsed = JSON.parse(trimmed);
761
+ if (parsed.__meta === "usage") {
762
+ return {
763
+ type: "usage",
764
+ usage: {
765
+ promptTokens: parsed.promptTokens ?? 0,
766
+ completionTokens: parsed.completionTokens ?? 0,
767
+ totalTokens: parsed.totalTokens ?? 0
768
+ }
769
+ };
770
+ }
771
+ return { type: "patch", patch: parsed };
772
+ } catch {
773
+ return null;
774
+ }
775
+ }
776
+ function setSpecValue(newSpec, path, value) {
777
+ if (path === "/root") {
778
+ newSpec.root = value;
779
+ return;
780
+ }
781
+ if (path === "/state") {
782
+ newSpec.state = value;
783
+ return;
784
+ }
785
+ if (path.startsWith("/state/")) {
786
+ if (!newSpec.state) newSpec.state = {};
787
+ const statePath = path.slice("/state".length);
788
+ setByPath(newSpec.state, statePath, value);
789
+ return;
790
+ }
791
+ if (path.startsWith("/elements/")) {
792
+ const pathParts = path.slice("/elements/".length).split("/");
793
+ const elementKey = pathParts[0];
794
+ if (!elementKey) return;
795
+ if (pathParts.length === 1) {
796
+ newSpec.elements[elementKey] = value;
797
+ } else {
798
+ const element = newSpec.elements[elementKey];
799
+ if (element) {
800
+ const propPath = "/" + pathParts.slice(1).join("/");
801
+ const newElement = { ...element };
802
+ setByPath(
803
+ newElement,
804
+ propPath,
805
+ value
806
+ );
807
+ newSpec.elements[elementKey] = newElement;
808
+ }
809
+ }
810
+ }
811
+ }
812
+ function removeSpecValue(newSpec, path) {
813
+ if (path === "/state") {
814
+ delete newSpec.state;
815
+ return;
816
+ }
817
+ if (path.startsWith("/state/") && newSpec.state) {
818
+ const statePath = path.slice("/state".length);
819
+ removeByPath(newSpec.state, statePath);
820
+ return;
821
+ }
822
+ if (path.startsWith("/elements/")) {
823
+ const pathParts = path.slice("/elements/".length).split("/");
824
+ const elementKey = pathParts[0];
825
+ if (!elementKey) return;
826
+ if (pathParts.length === 1) {
827
+ const { [elementKey]: _, ...rest } = newSpec.elements;
828
+ newSpec.elements = rest;
829
+ } else {
830
+ const element = newSpec.elements[elementKey];
831
+ if (element) {
832
+ const propPath = "/" + pathParts.slice(1).join("/");
833
+ const newElement = { ...element };
834
+ removeByPath(
835
+ newElement,
836
+ propPath
837
+ );
838
+ newSpec.elements[elementKey] = newElement;
839
+ }
840
+ }
841
+ }
842
+ }
843
+ function getSpecValue(spec, path) {
844
+ if (path === "/root") return spec.root;
845
+ if (path === "/state") return spec.state;
846
+ if (path.startsWith("/state/") && spec.state) {
847
+ const statePath = path.slice("/state".length);
848
+ return getByPath2(spec.state, statePath);
849
+ }
850
+ return getByPath2(spec, path);
851
+ }
852
+ function applyPatch(spec, patch) {
853
+ const newSpec = {
854
+ ...spec,
855
+ elements: { ...spec.elements },
856
+ ...spec.state ? { state: { ...spec.state } } : {}
857
+ };
858
+ switch (patch.op) {
859
+ case "add":
860
+ case "replace": {
861
+ setSpecValue(newSpec, patch.path, patch.value);
862
+ break;
863
+ }
864
+ case "remove": {
865
+ removeSpecValue(newSpec, patch.path);
866
+ break;
867
+ }
868
+ case "move": {
869
+ if (!patch.from) break;
870
+ const moveValue = getSpecValue(newSpec, patch.from);
871
+ removeSpecValue(newSpec, patch.from);
872
+ setSpecValue(newSpec, patch.path, moveValue);
873
+ break;
874
+ }
875
+ case "copy": {
876
+ if (!patch.from) break;
877
+ const copyValue = getSpecValue(newSpec, patch.from);
878
+ setSpecValue(newSpec, patch.path, copyValue);
879
+ break;
880
+ }
881
+ case "test": {
882
+ break;
883
+ }
884
+ }
885
+ return newSpec;
886
+ }
887
+ function useUIStream({
888
+ api,
889
+ onComplete,
890
+ onError
891
+ }) {
892
+ const spec = shallowRef2(null);
893
+ const isStreaming = ref4(false);
894
+ const error = ref4(null);
895
+ const usage = ref4(null);
896
+ const rawLines = ref4([]);
897
+ const onCompleteRef = ref4(onComplete);
898
+ const onErrorRef = ref4(onError);
899
+ let abortController = null;
900
+ const clear = () => {
901
+ spec.value = null;
902
+ error.value = null;
903
+ usage.value = null;
904
+ rawLines.value = [];
905
+ };
906
+ const send = async (prompt, context) => {
907
+ abortController?.abort();
908
+ abortController = new AbortController();
909
+ isStreaming.value = true;
910
+ error.value = null;
911
+ usage.value = null;
912
+ rawLines.value = [];
913
+ const previousSpec = context?.previousSpec;
914
+ let currentSpec = previousSpec && previousSpec.root ? { ...previousSpec, elements: { ...previousSpec.elements } } : { root: "", elements: {} };
915
+ spec.value = currentSpec;
916
+ try {
917
+ const response = await fetch(api, {
918
+ method: "POST",
919
+ headers: { "Content-Type": "application/json" },
920
+ body: JSON.stringify({
921
+ prompt,
922
+ context,
923
+ currentSpec
924
+ }),
925
+ signal: abortController.signal
926
+ });
927
+ if (!response.ok) {
928
+ let errorMessage = `HTTP error: ${response.status}`;
929
+ try {
930
+ const errorData = await response.json();
931
+ if (errorData.message) {
932
+ errorMessage = errorData.message;
933
+ } else if (errorData.error) {
934
+ errorMessage = errorData.error;
935
+ }
936
+ } catch {
937
+ }
938
+ throw new Error(errorMessage);
939
+ }
940
+ const reader = response.body?.getReader();
941
+ if (!reader) {
942
+ throw new Error("No response body");
943
+ }
944
+ const decoder = new TextDecoder();
945
+ let buffer = "";
946
+ while (true) {
947
+ const { done, value } = await reader.read();
948
+ if (done) break;
949
+ buffer += decoder.decode(value, { stream: true });
950
+ const lines = buffer.split("\n");
951
+ buffer = lines.pop() ?? "";
952
+ for (const line of lines) {
953
+ const trimmed = line.trim();
954
+ if (!trimmed) continue;
955
+ const result = parseLine(trimmed);
956
+ if (!result) continue;
957
+ if (result.type === "usage") {
958
+ usage.value = result.usage;
959
+ } else {
960
+ rawLines.value = [...rawLines.value, trimmed];
961
+ currentSpec = applyPatch(currentSpec, result.patch);
962
+ spec.value = { ...currentSpec };
963
+ }
964
+ }
965
+ }
966
+ if (buffer.trim()) {
967
+ const trimmed = buffer.trim();
968
+ const result = parseLine(trimmed);
969
+ if (result) {
970
+ if (result.type === "usage") {
971
+ usage.value = result.usage;
972
+ } else {
973
+ rawLines.value = [...rawLines.value, trimmed];
974
+ currentSpec = applyPatch(currentSpec, result.patch);
975
+ spec.value = { ...currentSpec };
976
+ }
977
+ }
978
+ }
979
+ onCompleteRef.value?.(currentSpec);
980
+ } catch (err) {
981
+ if (err.name === "AbortError") {
982
+ return;
983
+ }
984
+ const resolvedError = err instanceof Error ? err : new Error(String(err));
985
+ error.value = resolvedError;
986
+ onErrorRef.value?.(resolvedError);
987
+ } finally {
988
+ isStreaming.value = false;
989
+ }
990
+ };
991
+ onUnmounted3(() => {
992
+ abortController?.abort();
993
+ });
994
+ return {
995
+ spec,
996
+ isStreaming,
997
+ error,
998
+ usage,
999
+ rawLines,
1000
+ send,
1001
+ clear
1002
+ };
1003
+ }
1004
+ function flatToTree(elements) {
1005
+ const elementMap = {};
1006
+ let root = "";
1007
+ for (const element of elements) {
1008
+ elementMap[element.key] = {
1009
+ type: element.type,
1010
+ props: element.props,
1011
+ children: [],
1012
+ visible: element.visible
1013
+ };
1014
+ }
1015
+ for (const element of elements) {
1016
+ if (element.parentKey) {
1017
+ const parent = elementMap[element.parentKey];
1018
+ if (parent) {
1019
+ if (!parent.children) {
1020
+ parent.children = [];
1021
+ }
1022
+ parent.children.push(element.key);
1023
+ }
1024
+ } else {
1025
+ root = element.key;
1026
+ }
1027
+ }
1028
+ return { root, elements: elementMap };
1029
+ }
1030
+ function useBoundProp(propValue, bindingPath) {
1031
+ const { set } = useStateStore();
1032
+ return [
1033
+ propValue,
1034
+ (value) => {
1035
+ if (bindingPath) set(bindingPath, value);
1036
+ }
1037
+ ];
1038
+ }
1039
+ function isSpecDataPart(data) {
1040
+ if (typeof data !== "object" || data === null) return false;
1041
+ const obj = data;
1042
+ switch (obj.type) {
1043
+ case "patch":
1044
+ return typeof obj.patch === "object" && obj.patch !== null;
1045
+ case "flat":
1046
+ case "nested":
1047
+ return typeof obj.spec === "object" && obj.spec !== null;
1048
+ default:
1049
+ return false;
1050
+ }
1051
+ }
1052
+ function buildSpecFromParts(parts) {
1053
+ const spec = { root: "", elements: {} };
1054
+ let hasSpec = false;
1055
+ for (const part of parts) {
1056
+ if (part.type === SPEC_DATA_PART_TYPE) {
1057
+ if (!isSpecDataPart(part.data)) continue;
1058
+ const payload = part.data;
1059
+ if (payload.type === "patch") {
1060
+ hasSpec = true;
1061
+ applySpecPatch(spec, payload.patch);
1062
+ } else if (payload.type === "flat") {
1063
+ hasSpec = true;
1064
+ Object.assign(spec, payload.spec);
1065
+ } else if (payload.type === "nested") {
1066
+ hasSpec = true;
1067
+ const flat = nestedToFlat(payload.spec);
1068
+ Object.assign(spec, flat);
1069
+ }
1070
+ }
1071
+ }
1072
+ return hasSpec ? spec : null;
1073
+ }
1074
+ function getTextFromParts(parts) {
1075
+ return parts.filter(
1076
+ (p) => p.type === "text" && typeof p.text === "string"
1077
+ ).map((p) => p.text.trim()).filter(Boolean).join("\n\n");
1078
+ }
1079
+ function useJsonRenderMessage(parts) {
1080
+ const partsRef = isRef(parts) ? parts : ref4(parts);
1081
+ const spec = computed5(() => buildSpecFromParts(partsRef.value));
1082
+ const text = computed5(() => getTextFromParts(partsRef.value));
1083
+ const hasSpec = computed5(
1084
+ () => spec.value !== null && Object.keys(spec.value.elements || {}).length > 0
1085
+ );
1086
+ return { spec, text, hasSpec };
1087
+ }
1088
+ var chatMessageIdCounter = 0;
1089
+ function generateChatId() {
1090
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1091
+ return crypto.randomUUID();
1092
+ }
1093
+ chatMessageIdCounter += 1;
1094
+ return `msg-${Date.now()}-${chatMessageIdCounter}`;
1095
+ }
1096
+ function useChatUI({
1097
+ api,
1098
+ onComplete,
1099
+ onError
1100
+ }) {
1101
+ const messages = ref4([]);
1102
+ const isStreaming = ref4(false);
1103
+ const error = ref4(null);
1104
+ const onCompleteRef = ref4(onComplete);
1105
+ const onErrorRef = ref4(onError);
1106
+ let abortController = null;
1107
+ const clear = () => {
1108
+ messages.value = [];
1109
+ error.value = null;
1110
+ };
1111
+ const send = async (text) => {
1112
+ if (!text.trim()) return;
1113
+ abortController?.abort();
1114
+ abortController = new AbortController();
1115
+ const userMessage = {
1116
+ id: generateChatId(),
1117
+ role: "user",
1118
+ text: text.trim(),
1119
+ spec: null
1120
+ };
1121
+ const assistantId = generateChatId();
1122
+ const assistantMessage = {
1123
+ id: assistantId,
1124
+ role: "assistant",
1125
+ text: "",
1126
+ spec: null
1127
+ };
1128
+ messages.value = [...messages.value, userMessage, assistantMessage];
1129
+ isStreaming.value = true;
1130
+ error.value = null;
1131
+ const historyForApi = [
1132
+ ...messages.value.filter((m) => m.id !== assistantId).map((m) => ({ role: m.role, content: m.text })),
1133
+ { role: "user", content: text.trim() }
1134
+ ];
1135
+ let accumulatedText = "";
1136
+ let currentSpec = { root: "", elements: {} };
1137
+ let hasSpec = false;
1138
+ try {
1139
+ const response = await fetch(api, {
1140
+ method: "POST",
1141
+ headers: { "Content-Type": "application/json" },
1142
+ body: JSON.stringify({ messages: historyForApi }),
1143
+ signal: abortController.signal
1144
+ });
1145
+ if (!response.ok) {
1146
+ let errorMessage = `HTTP error: ${response.status}`;
1147
+ try {
1148
+ const errorData = await response.json();
1149
+ if (errorData.message) {
1150
+ errorMessage = errorData.message;
1151
+ } else if (errorData.error) {
1152
+ errorMessage = errorData.error;
1153
+ }
1154
+ } catch {
1155
+ }
1156
+ throw new Error(errorMessage);
1157
+ }
1158
+ const reader = response.body?.getReader();
1159
+ if (!reader) {
1160
+ throw new Error("No response body");
1161
+ }
1162
+ const decoder = new TextDecoder();
1163
+ const parser = createMixedStreamParser({
1164
+ onPatch(patch) {
1165
+ hasSpec = true;
1166
+ applySpecPatch(currentSpec, patch);
1167
+ messages.value = messages.value.map(
1168
+ (m) => m.id === assistantId ? {
1169
+ ...m,
1170
+ spec: {
1171
+ root: currentSpec.root,
1172
+ elements: { ...currentSpec.elements },
1173
+ ...currentSpec.state ? { state: { ...currentSpec.state } } : {}
1174
+ }
1175
+ } : m
1176
+ );
1177
+ },
1178
+ onText(line) {
1179
+ accumulatedText += (accumulatedText ? "\n" : "") + line;
1180
+ messages.value = messages.value.map(
1181
+ (m) => m.id === assistantId ? { ...m, text: accumulatedText } : m
1182
+ );
1183
+ }
1184
+ });
1185
+ while (true) {
1186
+ const { done, value } = await reader.read();
1187
+ if (done) break;
1188
+ parser.push(decoder.decode(value, { stream: true }));
1189
+ }
1190
+ parser.flush();
1191
+ const finalMessage = {
1192
+ id: assistantId,
1193
+ role: "assistant",
1194
+ text: accumulatedText,
1195
+ spec: hasSpec ? {
1196
+ root: currentSpec.root,
1197
+ elements: { ...currentSpec.elements },
1198
+ ...currentSpec.state ? { state: { ...currentSpec.state } } : {}
1199
+ } : null
1200
+ };
1201
+ onCompleteRef.value?.(finalMessage);
1202
+ } catch (err) {
1203
+ if (err.name === "AbortError") {
1204
+ return;
1205
+ }
1206
+ const resolvedError = err instanceof Error ? err : new Error(String(err));
1207
+ error.value = resolvedError;
1208
+ messages.value = messages.value.filter(
1209
+ (m) => m.id !== assistantId || m.text.length > 0
1210
+ );
1211
+ onErrorRef.value?.(resolvedError);
1212
+ } finally {
1213
+ isStreaming.value = false;
1214
+ }
1215
+ };
1216
+ onUnmounted3(() => {
1217
+ abortController?.abort();
1218
+ });
1219
+ return {
1220
+ messages,
1221
+ isStreaming,
1222
+ error,
1223
+ send,
1224
+ clear
1225
+ };
1226
+ }
1227
+
1228
+ // src/renderer.ts
1229
+ import {
1230
+ computed as computed6,
1231
+ defineComponent as defineComponent6,
1232
+ h as h2,
1233
+ inject as inject6,
1234
+ onErrorCaptured,
1235
+ provide as provide6,
1236
+ ref as ref5,
1237
+ watch as watch3
1238
+ } from "vue";
1239
+ import {
1240
+ resolveElementProps,
1241
+ resolveBindings,
1242
+ resolveActionParam,
1243
+ evaluateVisibility as evaluateVisibility2,
1244
+ getByPath as getByPath3
1245
+ } from "@json-render/core";
1246
+ var EMPTY_FUNCTIONS = {};
1247
+ var FUNCTIONS_KEY = /* @__PURE__ */ Symbol("json-render:functions");
1248
+ var FunctionsProvider = defineComponent6({
1249
+ name: "FunctionsProvider",
1250
+ props: {
1251
+ functions: {
1252
+ type: Object,
1253
+ default: void 0
1254
+ }
1255
+ },
1256
+ setup(props, { slots }) {
1257
+ const fns = computed6(() => props.functions ?? EMPTY_FUNCTIONS);
1258
+ provide6(FUNCTIONS_KEY, fns);
1259
+ return () => slots.default?.();
1260
+ }
1261
+ });
1262
+ function useFunctions() {
1263
+ return inject6(
1264
+ FUNCTIONS_KEY,
1265
+ computed6(() => EMPTY_FUNCTIONS)
1266
+ );
1267
+ }
1268
+ var ElementErrorBoundary = defineComponent6({
1269
+ name: "ElementErrorBoundary",
1270
+ props: {
1271
+ elementType: {
1272
+ type: String,
1273
+ required: true
1274
+ }
1275
+ },
1276
+ setup(props, { slots }) {
1277
+ const hasError = ref5(false);
1278
+ onErrorCaptured((error) => {
1279
+ console.error(
1280
+ `[json-render] Rendering error in <${props.elementType}>:`,
1281
+ error
1282
+ );
1283
+ hasError.value = true;
1284
+ return false;
1285
+ });
1286
+ return () => {
1287
+ if (hasError.value) return null;
1288
+ return slots.default?.();
1289
+ };
1290
+ }
1291
+ });
1292
+ async function resolveAndExecuteBindings(actionBindings, ctx, getSnapshot, execute, cancelled) {
1293
+ for (const b of actionBindings) {
1294
+ if (cancelled?.()) break;
1295
+ if (!b.params) {
1296
+ await execute(b);
1297
+ if (cancelled?.()) break;
1298
+ continue;
1299
+ }
1300
+ const liveCtx = {
1301
+ ...ctx,
1302
+ stateModel: getSnapshot()
1303
+ };
1304
+ const resolved = {};
1305
+ for (const [key, val] of Object.entries(b.params)) {
1306
+ resolved[key] = resolveActionParam(val, liveCtx);
1307
+ }
1308
+ await execute({ ...b, params: resolved });
1309
+ if (cancelled?.()) break;
1310
+ }
1311
+ }
1312
+ var ElementRenderer = defineComponent6({
1313
+ name: "JsonRenderElement",
1314
+ props: {
1315
+ element: {
1316
+ type: Object,
1317
+ required: true
1318
+ },
1319
+ spec: {
1320
+ type: Object,
1321
+ required: true
1322
+ },
1323
+ registry: {
1324
+ type: Object,
1325
+ required: true
1326
+ },
1327
+ loading: {
1328
+ type: Boolean,
1329
+ default: void 0
1330
+ },
1331
+ fallback: {
1332
+ type: Object,
1333
+ default: void 0
1334
+ }
1335
+ },
1336
+ setup(props) {
1337
+ const repeatScope = useRepeatScope();
1338
+ const { ctx: visibilityCtx } = useVisibility();
1339
+ const { execute } = useActions();
1340
+ const { getSnapshot, state: watchState } = useStateStore();
1341
+ const functions = useFunctions();
1342
+ const fullCtx = computed6(() => {
1343
+ const base = repeatScope ? {
1344
+ ...visibilityCtx.value,
1345
+ repeatItem: repeatScope.item,
1346
+ repeatIndex: repeatScope.index,
1347
+ repeatBasePath: repeatScope.basePath
1348
+ } : { ...visibilityCtx.value };
1349
+ base.functions = functions.value;
1350
+ return base;
1351
+ });
1352
+ const emitEvent = async (eventName) => {
1353
+ const binding = props.element.on?.[eventName];
1354
+ if (!binding) return;
1355
+ const actionBindings = Array.isArray(binding) ? binding : [binding];
1356
+ await resolveAndExecuteBindings(
1357
+ actionBindings,
1358
+ fullCtx.value,
1359
+ getSnapshot,
1360
+ execute
1361
+ );
1362
+ };
1363
+ const onEvent = (eventName) => {
1364
+ const binding = props.element.on?.[eventName];
1365
+ if (!binding) {
1366
+ return { emit: () => {
1367
+ }, shouldPreventDefault: false, bound: false };
1368
+ }
1369
+ const actionBindings = Array.isArray(binding) ? binding : [binding];
1370
+ const shouldPreventDefault = actionBindings.some((b) => b.preventDefault);
1371
+ return {
1372
+ emit: () => {
1373
+ void emitEvent(eventName);
1374
+ },
1375
+ shouldPreventDefault,
1376
+ bound: true
1377
+ };
1378
+ };
1379
+ const watchedValues = computed6(() => {
1380
+ const cfg = props.element.watch;
1381
+ if (!cfg) return void 0;
1382
+ const values = {};
1383
+ for (const path of Object.keys(cfg)) {
1384
+ values[path] = getByPath3(watchState.value, path);
1385
+ }
1386
+ return values;
1387
+ });
1388
+ watch3(
1389
+ watchedValues,
1390
+ (current, prev, onCleanup) => {
1391
+ const cfg = props.element.watch;
1392
+ if (!cfg || !current) return;
1393
+ let cancelled = false;
1394
+ onCleanup(() => {
1395
+ cancelled = true;
1396
+ });
1397
+ const paths = Object.keys(cfg);
1398
+ void (async () => {
1399
+ for (const path of paths) {
1400
+ if (cancelled) break;
1401
+ if (prev && current[path] === prev[path]) continue;
1402
+ const binding = cfg[path];
1403
+ if (!binding) continue;
1404
+ const bindings = Array.isArray(binding) ? binding : [binding];
1405
+ await resolveAndExecuteBindings(
1406
+ bindings,
1407
+ fullCtx.value,
1408
+ getSnapshot,
1409
+ execute,
1410
+ () => cancelled
1411
+ );
1412
+ }
1413
+ })().catch(console.error);
1414
+ },
1415
+ { deep: true }
1416
+ );
1417
+ return () => {
1418
+ const ctx = fullCtx.value;
1419
+ const isVisible = props.element.visible === void 0 ? true : evaluateVisibility2(props.element.visible, ctx);
1420
+ if (!isVisible) return null;
1421
+ const rawProps = props.element.props;
1422
+ const elementBindings = resolveBindings(rawProps, ctx);
1423
+ const resolvedProps = resolveElementProps(rawProps, ctx);
1424
+ const resolvedElement = resolvedProps !== props.element.props ? { ...props.element, props: resolvedProps } : props.element;
1425
+ const Component = props.registry[resolvedElement.type] ?? props.fallback;
1426
+ if (!Component) {
1427
+ console.warn(
1428
+ `[json-render] No renderer for component type: ${resolvedElement.type}`
1429
+ );
1430
+ return null;
1431
+ }
1432
+ const childrenVNodes = resolvedElement.repeat ? h2(RepeatChildren, {
1433
+ element: resolvedElement,
1434
+ spec: props.spec,
1435
+ registry: props.registry,
1436
+ loading: props.loading,
1437
+ fallback: props.fallback
1438
+ }) : resolvedElement.children?.map((childKey) => {
1439
+ const childElement = props.spec.elements[childKey];
1440
+ if (!childElement) {
1441
+ if (!props.loading) {
1442
+ console.warn(
1443
+ `[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
1444
+ );
1445
+ }
1446
+ return null;
1447
+ }
1448
+ return h2(ElementRenderer, {
1449
+ key: childKey,
1450
+ element: childElement,
1451
+ spec: props.spec,
1452
+ registry: props.registry,
1453
+ loading: props.loading,
1454
+ fallback: props.fallback
1455
+ });
1456
+ }).filter((n) => n !== null) ?? void 0;
1457
+ return h2(
1458
+ ElementErrorBoundary,
1459
+ { elementType: resolvedElement.type },
1460
+ {
1461
+ default: () => h2(
1462
+ Component,
1463
+ {
1464
+ element: resolvedElement,
1465
+ emit: emitEvent,
1466
+ on: onEvent,
1467
+ bindings: elementBindings,
1468
+ loading: props.loading
1469
+ },
1470
+ { default: () => childrenVNodes }
1471
+ )
1472
+ }
1473
+ );
1474
+ };
1475
+ }
1476
+ });
1477
+ var RepeatChildren = defineComponent6({
1478
+ name: "JsonRenderRepeatChildren",
1479
+ props: {
1480
+ element: {
1481
+ type: Object,
1482
+ required: true
1483
+ },
1484
+ spec: {
1485
+ type: Object,
1486
+ required: true
1487
+ },
1488
+ registry: {
1489
+ type: Object,
1490
+ required: true
1491
+ },
1492
+ loading: {
1493
+ type: Boolean,
1494
+ default: void 0
1495
+ },
1496
+ fallback: {
1497
+ type: Object,
1498
+ default: void 0
1499
+ }
1500
+ },
1501
+ setup(props) {
1502
+ const { state } = useStateStore();
1503
+ return () => {
1504
+ const repeat = props.element.repeat;
1505
+ if (!repeat?.statePath) return null;
1506
+ const statePath = repeat.statePath;
1507
+ const raw = getByPath3(state.value, statePath);
1508
+ const items = Array.isArray(raw) ? raw : [];
1509
+ return items.map((itemValue, index) => {
1510
+ const key = repeat.key && typeof itemValue === "object" && itemValue !== null ? String(
1511
+ itemValue[repeat.key] ?? index
1512
+ ) : String(index);
1513
+ return h2(
1514
+ RepeatScopeProvider,
1515
+ { key, item: itemValue, index, basePath: `${statePath}/${index}` },
1516
+ {
1517
+ default: () => props.element.children?.map((childKey) => {
1518
+ const childElement = props.spec.elements[childKey];
1519
+ if (!childElement) {
1520
+ if (!props.loading) {
1521
+ console.warn(
1522
+ `[json-render] Missing element "${childKey}" referenced as child of "${props.element.type}" (repeat). This element will not render.`
1523
+ );
1524
+ }
1525
+ return null;
1526
+ }
1527
+ return h2(ElementRenderer, {
1528
+ key: childKey,
1529
+ element: childElement,
1530
+ spec: props.spec,
1531
+ registry: props.registry,
1532
+ loading: props.loading,
1533
+ fallback: props.fallback
1534
+ });
1535
+ }).filter((n) => n !== null) ?? null
1536
+ }
1537
+ );
1538
+ });
1539
+ };
1540
+ }
1541
+ });
1542
+ var Renderer = defineComponent6({
1543
+ name: "JsonRenderer",
1544
+ props: {
1545
+ spec: {
1546
+ type: Object,
1547
+ default: null
1548
+ },
1549
+ registry: {
1550
+ type: Object,
1551
+ required: true
1552
+ },
1553
+ loading: {
1554
+ type: Boolean,
1555
+ default: void 0
1556
+ },
1557
+ fallback: {
1558
+ type: Object,
1559
+ default: void 0
1560
+ }
1561
+ },
1562
+ setup(props) {
1563
+ return () => {
1564
+ if (!props.spec?.root) return null;
1565
+ const rootElement = props.spec.elements[props.spec.root];
1566
+ if (!rootElement) return null;
1567
+ return h2(ElementRenderer, {
1568
+ element: rootElement,
1569
+ spec: props.spec,
1570
+ registry: props.registry,
1571
+ loading: props.loading,
1572
+ fallback: props.fallback
1573
+ });
1574
+ };
1575
+ }
1576
+ });
1577
+ var ConfirmationDialogManager = defineComponent6({
1578
+ name: "ConfirmationDialogManager",
1579
+ setup() {
1580
+ const { pendingConfirmation, confirm, cancel } = useActions();
1581
+ return () => {
1582
+ if (!pendingConfirmation?.action.confirm) return null;
1583
+ return h2(ConfirmDialog, {
1584
+ confirm: pendingConfirmation.action.confirm,
1585
+ onConfirm: confirm,
1586
+ onCancel: cancel
1587
+ });
1588
+ };
1589
+ }
1590
+ });
1591
+ var JSONUIProvider = defineComponent6({
1592
+ name: "JSONUIProvider",
1593
+ props: {
1594
+ registry: {
1595
+ type: Object,
1596
+ required: true
1597
+ },
1598
+ store: {
1599
+ type: Object,
1600
+ default: void 0
1601
+ },
1602
+ initialState: {
1603
+ type: Object,
1604
+ default: void 0
1605
+ },
1606
+ handlers: {
1607
+ type: Object,
1608
+ default: void 0
1609
+ },
1610
+ navigate: {
1611
+ type: Function,
1612
+ default: void 0
1613
+ },
1614
+ validationFunctions: {
1615
+ type: Object,
1616
+ default: void 0
1617
+ },
1618
+ functions: {
1619
+ type: Object,
1620
+ default: void 0
1621
+ },
1622
+ onStateChange: {
1623
+ type: Function,
1624
+ default: void 0
1625
+ }
1626
+ },
1627
+ setup(props, { slots }) {
1628
+ return () => h2(
1629
+ StateProvider,
1630
+ {
1631
+ store: props.store,
1632
+ initialState: props.initialState,
1633
+ onStateChange: props.onStateChange
1634
+ },
1635
+ {
1636
+ default: () => h2(VisibilityProvider, null, {
1637
+ default: () => h2(
1638
+ ValidationProvider,
1639
+ { customFunctions: props.validationFunctions },
1640
+ {
1641
+ default: () => h2(
1642
+ ActionProvider,
1643
+ { handlers: props.handlers, navigate: props.navigate },
1644
+ {
1645
+ default: () => h2(
1646
+ FunctionsProvider,
1647
+ { functions: props.functions },
1648
+ {
1649
+ default: () => [
1650
+ slots.default?.(),
1651
+ h2(ConfirmationDialogManager)
1652
+ ]
1653
+ }
1654
+ )
1655
+ }
1656
+ )
1657
+ }
1658
+ )
1659
+ })
1660
+ }
1661
+ );
1662
+ }
1663
+ });
1664
+ function defineRegistry(_catalog, options) {
1665
+ const registry = {};
1666
+ if (options.components) {
1667
+ for (const [name, componentFn] of Object.entries(options.components)) {
1668
+ registry[name] = defineComponent6({
1669
+ name: `JsonRenderRegistry_${name}`,
1670
+ props: {
1671
+ element: {
1672
+ type: Object,
1673
+ required: true
1674
+ },
1675
+ emit: {
1676
+ type: Function,
1677
+ required: true
1678
+ },
1679
+ on: {
1680
+ type: Function,
1681
+ required: true
1682
+ },
1683
+ bindings: {
1684
+ type: Object,
1685
+ default: void 0
1686
+ },
1687
+ loading: {
1688
+ type: Boolean,
1689
+ default: void 0
1690
+ }
1691
+ },
1692
+ setup(registryProps, { slots }) {
1693
+ return () => componentFn({
1694
+ props: registryProps.element.props,
1695
+ children: slots.default?.(),
1696
+ emit: registryProps.emit,
1697
+ on: registryProps.on,
1698
+ bindings: registryProps.bindings,
1699
+ loading: registryProps.loading
1700
+ });
1701
+ }
1702
+ });
1703
+ }
1704
+ }
1705
+ const actionMap = options.actions ? Object.entries(options.actions) : [];
1706
+ const handlers = (getSetState, getState) => {
1707
+ const result = {};
1708
+ for (const [name, actionFn] of actionMap) {
1709
+ result[name] = async (params) => {
1710
+ const setState = getSetState();
1711
+ const state = getState();
1712
+ if (setState) {
1713
+ await actionFn(params, setState, state);
1714
+ }
1715
+ };
1716
+ }
1717
+ return result;
1718
+ };
1719
+ const executeAction2 = async (actionName, params, setState, state = {}) => {
1720
+ const entry = actionMap.find(([name]) => name === actionName);
1721
+ if (entry) {
1722
+ await entry[1](params, setState, state);
1723
+ } else {
1724
+ console.warn(`Unknown action: ${actionName}`);
1725
+ }
1726
+ };
1727
+ return { registry, handlers, executeAction: executeAction2 };
1728
+ }
1729
+ function createRenderer(catalog, components) {
1730
+ const registry = components;
1731
+ return defineComponent6({
1732
+ name: "CatalogRenderer",
1733
+ props: {
1734
+ spec: {
1735
+ type: Object,
1736
+ default: null
1737
+ },
1738
+ store: {
1739
+ type: Object,
1740
+ default: void 0
1741
+ },
1742
+ state: {
1743
+ type: Object,
1744
+ default: void 0
1745
+ },
1746
+ onAction: {
1747
+ type: Function,
1748
+ default: void 0
1749
+ },
1750
+ onStateChange: {
1751
+ type: Function,
1752
+ default: void 0
1753
+ },
1754
+ functions: {
1755
+ type: Object,
1756
+ default: void 0
1757
+ },
1758
+ loading: {
1759
+ type: Boolean,
1760
+ default: void 0
1761
+ },
1762
+ fallback: {
1763
+ type: Object,
1764
+ default: void 0
1765
+ }
1766
+ },
1767
+ setup(rendererProps) {
1768
+ return () => {
1769
+ const actionHandlers = rendererProps.onAction ? new Proxy(
1770
+ {},
1771
+ {
1772
+ get: (_target, prop) => {
1773
+ return (params) => rendererProps.onAction(prop, params);
1774
+ },
1775
+ has: () => true
1776
+ }
1777
+ ) : void 0;
1778
+ return h2(
1779
+ StateProvider,
1780
+ {
1781
+ store: rendererProps.store,
1782
+ initialState: rendererProps.state,
1783
+ onStateChange: rendererProps.onStateChange
1784
+ },
1785
+ {
1786
+ default: () => h2(VisibilityProvider, null, {
1787
+ default: () => h2(ValidationProvider, null, {
1788
+ default: () => h2(
1789
+ ActionProvider,
1790
+ { handlers: actionHandlers },
1791
+ {
1792
+ default: () => h2(
1793
+ FunctionsProvider,
1794
+ { functions: rendererProps.functions },
1795
+ {
1796
+ default: () => [
1797
+ h2(Renderer, {
1798
+ spec: rendererProps.spec,
1799
+ registry,
1800
+ loading: rendererProps.loading,
1801
+ fallback: rendererProps.fallback
1802
+ }),
1803
+ h2(ConfirmationDialogManager)
1804
+ ]
1805
+ }
1806
+ )
1807
+ }
1808
+ )
1809
+ })
1810
+ })
1811
+ }
1812
+ );
1813
+ };
1814
+ }
1815
+ });
1816
+ }
1817
+ export {
1818
+ ActionProvider,
1819
+ ConfirmDialog,
1820
+ JSONUIProvider,
1821
+ Renderer,
1822
+ RepeatScopeProvider,
1823
+ StateProvider,
1824
+ ValidationProvider,
1825
+ VisibilityProvider,
1826
+ buildSpecFromParts,
1827
+ createRenderer,
1828
+ createStateStore2 as createStateStore,
1829
+ defineRegistry,
1830
+ flatToTree,
1831
+ getTextFromParts,
1832
+ schema,
1833
+ useAction,
1834
+ useActions,
1835
+ useBoundProp,
1836
+ useChatUI,
1837
+ useFieldValidation,
1838
+ useIsVisible,
1839
+ useJsonRenderMessage,
1840
+ useOptionalValidation,
1841
+ useRepeatScope,
1842
+ useStateBinding,
1843
+ useStateStore,
1844
+ useStateValue,
1845
+ useUIStream,
1846
+ useValidation,
1847
+ useVisibility
1848
+ };
1849
+ //# sourceMappingURL=index.mjs.map