@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/LICENSE +201 -0
- package/README.md +496 -0
- package/dist/chunk-WIPZLAF7.mjs +85 -0
- package/dist/chunk-WIPZLAF7.mjs.map +1 -0
- package/dist/index.d.mts +885 -0
- package/dist/index.d.ts +885 -0
- package/dist/index.js +1913 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1849 -0
- package/dist/index.mjs.map +1 -0
- package/dist/schema.d.mts +60 -0
- package/dist/schema.d.ts +60 -0
- package/dist/schema.js +109 -0
- package/dist/schema.js.map +1 -0
- package/dist/schema.mjs +7 -0
- package/dist/schema.mjs.map +1 -0
- package/package.json +66 -0
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
|