@json-render/react-native 0.4.3

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,2232 @@
1
+ import {
2
+ standardActionDefinitions,
3
+ standardComponentDefinitions
4
+ } from "./chunk-MP3Y6GEF.mjs";
5
+ import {
6
+ elementTreeSchema,
7
+ schema
8
+ } from "./chunk-VBNJFRLI.mjs";
9
+
10
+ // src/contexts/state.tsx
11
+ import {
12
+ createContext,
13
+ useContext,
14
+ useState,
15
+ useCallback,
16
+ useMemo,
17
+ useEffect,
18
+ useRef
19
+ } from "react";
20
+ import {
21
+ getByPath,
22
+ setByPath
23
+ } from "@json-render/core";
24
+ import { jsx } from "react/jsx-runtime";
25
+ var StateContext = createContext(null);
26
+ function StateProvider({
27
+ initialState = {},
28
+ authState,
29
+ onStateChange,
30
+ children
31
+ }) {
32
+ const [state, setStateInternal] = useState(initialState);
33
+ const initialStateJsonRef = useRef(JSON.stringify(initialState));
34
+ useEffect(() => {
35
+ const newJson = JSON.stringify(initialState);
36
+ if (newJson !== initialStateJsonRef.current) {
37
+ initialStateJsonRef.current = newJson;
38
+ if (initialState && Object.keys(initialState).length > 0) {
39
+ setStateInternal((prev) => ({ ...prev, ...initialState }));
40
+ }
41
+ }
42
+ }, [initialState]);
43
+ const get = useCallback((path) => getByPath(state, path), [state]);
44
+ const set = useCallback(
45
+ (path, value2) => {
46
+ setStateInternal((prev) => {
47
+ const next = { ...prev };
48
+ setByPath(next, path, value2);
49
+ return next;
50
+ });
51
+ onStateChange?.(path, value2);
52
+ },
53
+ [onStateChange]
54
+ );
55
+ const update = useCallback(
56
+ (updates) => {
57
+ setStateInternal((prev) => {
58
+ const next = { ...prev };
59
+ for (const [path, value2] of Object.entries(updates)) {
60
+ setByPath(next, path, value2);
61
+ onStateChange?.(path, value2);
62
+ }
63
+ return next;
64
+ });
65
+ },
66
+ [onStateChange]
67
+ );
68
+ const value = useMemo(
69
+ () => ({
70
+ state,
71
+ authState,
72
+ get,
73
+ set,
74
+ update
75
+ }),
76
+ [state, authState, get, set, update]
77
+ );
78
+ return /* @__PURE__ */ jsx(StateContext.Provider, { value, children });
79
+ }
80
+ function useStateStore() {
81
+ const ctx = useContext(StateContext);
82
+ if (!ctx) {
83
+ throw new Error("useStateStore must be used within a StateProvider");
84
+ }
85
+ return ctx;
86
+ }
87
+ function useStateValue(path) {
88
+ const { state } = useStateStore();
89
+ return getByPath(state, path);
90
+ }
91
+ function useStateBinding(path) {
92
+ const { state, set } = useStateStore();
93
+ const value = getByPath(state, path);
94
+ const setValue = useCallback(
95
+ (newValue) => set(path, newValue),
96
+ [path, set]
97
+ );
98
+ return [value, setValue];
99
+ }
100
+
101
+ // src/contexts/visibility.tsx
102
+ import {
103
+ createContext as createContext2,
104
+ useContext as useContext2,
105
+ useMemo as useMemo2
106
+ } from "react";
107
+ import {
108
+ evaluateVisibility
109
+ } from "@json-render/core";
110
+ import { jsx as jsx2 } from "react/jsx-runtime";
111
+ var VisibilityContext = createContext2(null);
112
+ function VisibilityProvider({ children }) {
113
+ const { state, authState } = useStateStore();
114
+ const ctx = useMemo2(
115
+ () => ({
116
+ stateModel: state,
117
+ authState
118
+ }),
119
+ [state, authState]
120
+ );
121
+ const isVisible = useMemo2(
122
+ () => (condition) => evaluateVisibility(condition, ctx),
123
+ [ctx]
124
+ );
125
+ const value = useMemo2(
126
+ () => ({ isVisible, ctx }),
127
+ [isVisible, ctx]
128
+ );
129
+ return /* @__PURE__ */ jsx2(VisibilityContext.Provider, { value, children });
130
+ }
131
+ function useVisibility() {
132
+ const ctx = useContext2(VisibilityContext);
133
+ if (!ctx) {
134
+ throw new Error("useVisibility must be used within a VisibilityProvider");
135
+ }
136
+ return ctx;
137
+ }
138
+ function useIsVisible(condition) {
139
+ const { isVisible } = useVisibility();
140
+ return isVisible(condition);
141
+ }
142
+
143
+ // src/contexts/actions.tsx
144
+ import {
145
+ createContext as createContext3,
146
+ useContext as useContext3,
147
+ useState as useState2,
148
+ useCallback as useCallback2,
149
+ useMemo as useMemo3
150
+ } from "react";
151
+ import { Modal, View, Text, Pressable, StyleSheet } from "react-native";
152
+ import {
153
+ resolveAction,
154
+ executeAction
155
+ } from "@json-render/core";
156
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
157
+ var idCounter = 0;
158
+ function generateUniqueId() {
159
+ idCounter += 1;
160
+ return `${Date.now()}-${idCounter}`;
161
+ }
162
+ function deepResolveValue(value, get) {
163
+ if (value === null || value === void 0) return value;
164
+ if (value === "$id") {
165
+ return generateUniqueId();
166
+ }
167
+ if (typeof value === "object" && !Array.isArray(value)) {
168
+ const obj = value;
169
+ const keys = Object.keys(obj);
170
+ if (keys.length === 1 && typeof obj.path === "string") {
171
+ return get(obj.path);
172
+ }
173
+ if (keys.length === 1 && "$id" in obj) {
174
+ return generateUniqueId();
175
+ }
176
+ }
177
+ if (Array.isArray(value)) {
178
+ return value.map((item) => deepResolveValue(item, get));
179
+ }
180
+ if (typeof value === "object") {
181
+ const resolved = {};
182
+ for (const [key, val] of Object.entries(value)) {
183
+ resolved[key] = deepResolveValue(val, get);
184
+ }
185
+ return resolved;
186
+ }
187
+ return value;
188
+ }
189
+ var ActionContext = createContext3(null);
190
+ function ActionProvider({
191
+ handlers: initialHandlers = {},
192
+ navigate,
193
+ children
194
+ }) {
195
+ const { state, get, set } = useStateStore();
196
+ const [handlers, setHandlers] = useState2(initialHandlers);
197
+ const [loadingActions, setLoadingActions] = useState2(/* @__PURE__ */ new Set());
198
+ const [pendingConfirmation, setPendingConfirmation] = useState2(null);
199
+ const registerHandler = useCallback2(
200
+ (name, handler) => {
201
+ setHandlers((prev) => ({ ...prev, [name]: handler }));
202
+ },
203
+ []
204
+ );
205
+ const execute = useCallback2(
206
+ async (binding) => {
207
+ const resolved = resolveAction(binding, state);
208
+ if (resolved.action === "setState" && resolved.params) {
209
+ const path = resolved.params.path;
210
+ const value2 = resolved.params.value;
211
+ if (path) {
212
+ set(path, value2);
213
+ }
214
+ return;
215
+ }
216
+ if (resolved.action === "pushState" && resolved.params) {
217
+ const path = resolved.params.path;
218
+ const rawValue = resolved.params.value;
219
+ if (path) {
220
+ const resolvedValue = deepResolveValue(rawValue, get);
221
+ const arr = get(path) ?? [];
222
+ set(path, [...arr, resolvedValue]);
223
+ const clearPath = resolved.params.clearPath;
224
+ if (clearPath) {
225
+ set(clearPath, "");
226
+ }
227
+ }
228
+ return;
229
+ }
230
+ if (resolved.action === "removeState" && resolved.params) {
231
+ const path = resolved.params.path;
232
+ const index = resolved.params.index;
233
+ if (path !== void 0 && index !== void 0) {
234
+ const arr = get(path) ?? [];
235
+ set(
236
+ path,
237
+ arr.filter((_, i) => i !== index)
238
+ );
239
+ }
240
+ return;
241
+ }
242
+ if (resolved.action === "push" && resolved.params) {
243
+ const screen = resolved.params.screen;
244
+ if (screen) {
245
+ const currentScreen = get("/currentScreen");
246
+ const navStack = get("/navStack") ?? [];
247
+ if (currentScreen) {
248
+ set("/navStack", [...navStack, currentScreen]);
249
+ } else {
250
+ set("/navStack", [...navStack, ""]);
251
+ }
252
+ set("/currentScreen", screen);
253
+ }
254
+ return;
255
+ }
256
+ if (resolved.action === "pop") {
257
+ const navStack = get("/navStack") ?? [];
258
+ if (navStack.length > 0) {
259
+ const previousScreen = navStack[navStack.length - 1];
260
+ set("/navStack", navStack.slice(0, -1));
261
+ if (previousScreen) {
262
+ set("/currentScreen", previousScreen);
263
+ } else {
264
+ set("/currentScreen", void 0);
265
+ }
266
+ }
267
+ return;
268
+ }
269
+ const handler = handlers[resolved.action];
270
+ if (!handler) {
271
+ console.warn(`No handler registered for action: ${resolved.action}`);
272
+ return;
273
+ }
274
+ if (resolved.confirm) {
275
+ return new Promise((resolve, reject) => {
276
+ setPendingConfirmation({
277
+ action: resolved,
278
+ handler,
279
+ resolve: () => {
280
+ setPendingConfirmation(null);
281
+ resolve();
282
+ },
283
+ reject: () => {
284
+ setPendingConfirmation(null);
285
+ reject(new Error("Action cancelled"));
286
+ }
287
+ });
288
+ }).then(async () => {
289
+ setLoadingActions((prev) => new Set(prev).add(resolved.action));
290
+ try {
291
+ await executeAction({
292
+ action: resolved,
293
+ handler,
294
+ setState: set,
295
+ navigate,
296
+ executeAction: async (name) => {
297
+ const subBinding = { action: name };
298
+ await execute(subBinding);
299
+ }
300
+ });
301
+ } finally {
302
+ setLoadingActions((prev) => {
303
+ const next = new Set(prev);
304
+ next.delete(resolved.action);
305
+ return next;
306
+ });
307
+ }
308
+ });
309
+ }
310
+ setLoadingActions((prev) => new Set(prev).add(resolved.action));
311
+ try {
312
+ await executeAction({
313
+ action: resolved,
314
+ handler,
315
+ setState: set,
316
+ navigate,
317
+ executeAction: async (name) => {
318
+ const subBinding = { action: name };
319
+ await execute(subBinding);
320
+ }
321
+ });
322
+ } finally {
323
+ setLoadingActions((prev) => {
324
+ const next = new Set(prev);
325
+ next.delete(resolved.action);
326
+ return next;
327
+ });
328
+ }
329
+ },
330
+ [state, handlers, get, set, navigate]
331
+ );
332
+ const confirm = useCallback2(() => {
333
+ pendingConfirmation?.resolve();
334
+ }, [pendingConfirmation]);
335
+ const cancel = useCallback2(() => {
336
+ pendingConfirmation?.reject();
337
+ }, [pendingConfirmation]);
338
+ const value = useMemo3(
339
+ () => ({
340
+ handlers,
341
+ loadingActions,
342
+ pendingConfirmation,
343
+ execute,
344
+ confirm,
345
+ cancel,
346
+ registerHandler
347
+ }),
348
+ [
349
+ handlers,
350
+ loadingActions,
351
+ pendingConfirmation,
352
+ execute,
353
+ confirm,
354
+ cancel,
355
+ registerHandler
356
+ ]
357
+ );
358
+ return /* @__PURE__ */ jsx3(ActionContext.Provider, { value, children });
359
+ }
360
+ function useActions() {
361
+ const ctx = useContext3(ActionContext);
362
+ if (!ctx) {
363
+ throw new Error("useActions must be used within an ActionProvider");
364
+ }
365
+ return ctx;
366
+ }
367
+ function useAction(binding) {
368
+ const { execute, loadingActions } = useActions();
369
+ const isLoading = loadingActions.has(binding.action);
370
+ const executeAction2 = useCallback2(() => execute(binding), [execute, binding]);
371
+ return { execute: executeAction2, isLoading };
372
+ }
373
+ function ConfirmDialog({
374
+ confirm,
375
+ onConfirm,
376
+ onCancel
377
+ }) {
378
+ const isDanger = confirm.variant === "danger";
379
+ return /* @__PURE__ */ jsx3(Modal, { transparent: true, animationType: "fade", visible: true, onRequestClose: onCancel, children: /* @__PURE__ */ jsx3(Pressable, { style: styles.overlay, onPress: onCancel, children: /* @__PURE__ */ jsxs(Pressable, { style: styles.dialog, onPress: () => {
380
+ }, children: [
381
+ /* @__PURE__ */ jsx3(Text, { style: styles.title, children: confirm.title }),
382
+ /* @__PURE__ */ jsx3(Text, { style: styles.message, children: confirm.message }),
383
+ /* @__PURE__ */ jsxs(View, { style: styles.buttons, children: [
384
+ /* @__PURE__ */ jsx3(Pressable, { style: styles.cancelButton, onPress: onCancel, children: /* @__PURE__ */ jsx3(Text, { style: styles.cancelButtonText, children: confirm.cancelLabel ?? "Cancel" }) }),
385
+ /* @__PURE__ */ jsx3(
386
+ Pressable,
387
+ {
388
+ style: [
389
+ styles.confirmButton,
390
+ isDanger && styles.confirmButtonDanger
391
+ ],
392
+ onPress: onConfirm,
393
+ children: /* @__PURE__ */ jsx3(Text, { style: styles.confirmButtonText, children: confirm.confirmLabel ?? "Confirm" })
394
+ }
395
+ )
396
+ ] })
397
+ ] }) }) });
398
+ }
399
+ var styles = StyleSheet.create({
400
+ overlay: {
401
+ flex: 1,
402
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
403
+ alignItems: "center",
404
+ justifyContent: "center"
405
+ },
406
+ dialog: {
407
+ backgroundColor: "white",
408
+ borderRadius: 8,
409
+ padding: 24,
410
+ maxWidth: 400,
411
+ width: "90%",
412
+ shadowColor: "#000",
413
+ shadowOffset: { width: 0, height: 20 },
414
+ shadowOpacity: 0.1,
415
+ shadowRadius: 25,
416
+ elevation: 10
417
+ },
418
+ title: {
419
+ fontSize: 18,
420
+ fontWeight: "600",
421
+ marginBottom: 8
422
+ },
423
+ message: {
424
+ fontSize: 14,
425
+ color: "#6b7280",
426
+ marginBottom: 24
427
+ },
428
+ buttons: {
429
+ flexDirection: "row",
430
+ justifyContent: "flex-end",
431
+ gap: 12
432
+ },
433
+ cancelButton: {
434
+ paddingVertical: 8,
435
+ paddingHorizontal: 16,
436
+ borderRadius: 6,
437
+ borderWidth: 1,
438
+ borderColor: "#d1d5db",
439
+ backgroundColor: "white"
440
+ },
441
+ cancelButtonText: {
442
+ fontSize: 14,
443
+ color: "#374151"
444
+ },
445
+ confirmButton: {
446
+ paddingVertical: 8,
447
+ paddingHorizontal: 16,
448
+ borderRadius: 6,
449
+ backgroundColor: "#3b82f6"
450
+ },
451
+ confirmButtonDanger: {
452
+ backgroundColor: "#dc2626"
453
+ },
454
+ confirmButtonText: {
455
+ fontSize: 14,
456
+ color: "white"
457
+ }
458
+ });
459
+
460
+ // src/contexts/validation.tsx
461
+ import React4, {
462
+ createContext as createContext4,
463
+ useContext as useContext4,
464
+ useState as useState3,
465
+ useCallback as useCallback3,
466
+ useMemo as useMemo4
467
+ } from "react";
468
+ import {
469
+ runValidation
470
+ } from "@json-render/core";
471
+ import { jsx as jsx4 } from "react/jsx-runtime";
472
+ var ValidationContext = createContext4(null);
473
+ function ValidationProvider({
474
+ customFunctions = {},
475
+ children
476
+ }) {
477
+ const { state, authState } = useStateStore();
478
+ const [fieldStates, setFieldStates] = useState3({});
479
+ const [fieldConfigs, setFieldConfigs] = useState3({});
480
+ const registerField = useCallback3(
481
+ (path, config) => {
482
+ setFieldConfigs((prev) => ({ ...prev, [path]: config }));
483
+ },
484
+ []
485
+ );
486
+ const validate = useCallback3(
487
+ (path, config) => {
488
+ const value2 = state[path.split("/").filter(Boolean).join(".")];
489
+ const result = runValidation(config, {
490
+ value: value2,
491
+ stateModel: state,
492
+ customFunctions,
493
+ authState
494
+ });
495
+ setFieldStates((prev) => ({
496
+ ...prev,
497
+ [path]: {
498
+ touched: prev[path]?.touched ?? true,
499
+ validated: true,
500
+ result
501
+ }
502
+ }));
503
+ return result;
504
+ },
505
+ [state, customFunctions, authState]
506
+ );
507
+ const touch = useCallback3((path) => {
508
+ setFieldStates((prev) => ({
509
+ ...prev,
510
+ [path]: {
511
+ ...prev[path],
512
+ touched: true,
513
+ validated: prev[path]?.validated ?? false,
514
+ result: prev[path]?.result ?? null
515
+ }
516
+ }));
517
+ }, []);
518
+ const clear = useCallback3((path) => {
519
+ setFieldStates((prev) => {
520
+ const { [path]: _, ...rest } = prev;
521
+ return rest;
522
+ });
523
+ }, []);
524
+ const validateAll = useCallback3(() => {
525
+ let allValid = true;
526
+ for (const [path, config] of Object.entries(fieldConfigs)) {
527
+ const result = validate(path, config);
528
+ if (!result.valid) {
529
+ allValid = false;
530
+ }
531
+ }
532
+ return allValid;
533
+ }, [fieldConfigs, validate]);
534
+ const value = useMemo4(
535
+ () => ({
536
+ customFunctions,
537
+ fieldStates,
538
+ validate,
539
+ touch,
540
+ clear,
541
+ validateAll,
542
+ registerField
543
+ }),
544
+ [
545
+ customFunctions,
546
+ fieldStates,
547
+ validate,
548
+ touch,
549
+ clear,
550
+ validateAll,
551
+ registerField
552
+ ]
553
+ );
554
+ return /* @__PURE__ */ jsx4(ValidationContext.Provider, { value, children });
555
+ }
556
+ function useValidation() {
557
+ const ctx = useContext4(ValidationContext);
558
+ if (!ctx) {
559
+ throw new Error("useValidation must be used within a ValidationProvider");
560
+ }
561
+ return ctx;
562
+ }
563
+ function useFieldValidation(path, config) {
564
+ const {
565
+ fieldStates,
566
+ validate: validateField,
567
+ touch: touchField,
568
+ clear: clearField,
569
+ registerField
570
+ } = useValidation();
571
+ React4.useEffect(() => {
572
+ if (config) {
573
+ registerField(path, config);
574
+ }
575
+ }, [path, config, registerField]);
576
+ const state = fieldStates[path] ?? {
577
+ touched: false,
578
+ validated: false,
579
+ result: null
580
+ };
581
+ const validate = useCallback3(
582
+ () => validateField(path, config ?? { checks: [] }),
583
+ [path, config, validateField]
584
+ );
585
+ const touch = useCallback3(() => touchField(path), [path, touchField]);
586
+ const clear = useCallback3(() => clearField(path), [path, clearField]);
587
+ return {
588
+ state,
589
+ validate,
590
+ touch,
591
+ clear,
592
+ errors: state.result?.errors ?? [],
593
+ isValid: state.result?.valid ?? true
594
+ };
595
+ }
596
+
597
+ // src/contexts/repeat-scope.tsx
598
+ import { createContext as createContext5, useContext as useContext5 } from "react";
599
+ import { jsx as jsx5 } from "react/jsx-runtime";
600
+ var RepeatScopeContext = createContext5(null);
601
+ function RepeatScopeProvider({
602
+ basePath,
603
+ index,
604
+ children
605
+ }) {
606
+ return /* @__PURE__ */ jsx5(RepeatScopeContext.Provider, { value: { basePath, index }, children });
607
+ }
608
+ function useRepeatScope() {
609
+ return useContext5(RepeatScopeContext);
610
+ }
611
+ function rewriteRepeatTokens(value, basePath, index) {
612
+ if (typeof value === "string") {
613
+ if (value === "$index") return index;
614
+ if (value === "$item") return basePath;
615
+ if (value.startsWith("$item/"))
616
+ return basePath + value.slice("$item".length);
617
+ return value;
618
+ }
619
+ if (Array.isArray(value)) {
620
+ let changed = false;
621
+ const result = value.map((item) => {
622
+ const rewritten = rewriteRepeatTokens(item, basePath, index);
623
+ if (rewritten !== item) changed = true;
624
+ return rewritten;
625
+ });
626
+ return changed ? result : value;
627
+ }
628
+ if (typeof value === "object" && value !== null) {
629
+ let changed = false;
630
+ const result = {};
631
+ for (const [key, val] of Object.entries(value)) {
632
+ const rewritten = rewriteRepeatTokens(val, basePath, index);
633
+ if (rewritten !== val) changed = true;
634
+ result[key] = rewritten;
635
+ }
636
+ return changed ? result : value;
637
+ }
638
+ return value;
639
+ }
640
+
641
+ // src/renderer.tsx
642
+ import {
643
+ useCallback as useCallback4,
644
+ useMemo as useMemo5
645
+ } from "react";
646
+ import { resolveElementProps, getByPath as getByPath2 } from "@json-render/core";
647
+
648
+ // src/components/standard.tsx
649
+ import {
650
+ View as View2,
651
+ Text as Text2,
652
+ Image as RNImage,
653
+ ScrollView,
654
+ SafeAreaView,
655
+ Pressable as Pressable2,
656
+ TextInput as RNTextInput,
657
+ Switch as RNSwitch,
658
+ ActivityIndicator,
659
+ Modal as RNModal,
660
+ StyleSheet as StyleSheet2,
661
+ Alert,
662
+ Share,
663
+ Linking
664
+ } from "react-native";
665
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
666
+ function ContainerComponent({ element, children }) {
667
+ const p = element.props;
668
+ return /* @__PURE__ */ jsx6(
669
+ View2,
670
+ {
671
+ style: {
672
+ padding: p.padding ?? void 0,
673
+ paddingHorizontal: p.paddingHorizontal ?? void 0,
674
+ paddingVertical: p.paddingVertical ?? void 0,
675
+ margin: p.margin ?? void 0,
676
+ backgroundColor: p.backgroundColor ?? void 0,
677
+ borderRadius: p.borderRadius ?? void 0,
678
+ flex: p.flex ?? void 0
679
+ },
680
+ children
681
+ }
682
+ );
683
+ }
684
+ function RowComponent({ element, children }) {
685
+ const p = element.props;
686
+ return /* @__PURE__ */ jsx6(
687
+ View2,
688
+ {
689
+ style: {
690
+ flexDirection: "row",
691
+ gap: p.gap ?? void 0,
692
+ alignItems: p.alignItems ?? void 0,
693
+ justifyContent: p.justifyContent ?? void 0,
694
+ flexWrap: p.flexWrap ?? void 0,
695
+ padding: p.padding ?? void 0,
696
+ flex: p.flex ?? void 0
697
+ },
698
+ children
699
+ }
700
+ );
701
+ }
702
+ function ColumnComponent({ element, children }) {
703
+ const p = element.props;
704
+ return /* @__PURE__ */ jsx6(
705
+ View2,
706
+ {
707
+ style: {
708
+ flexDirection: "column",
709
+ gap: p.gap ?? void 0,
710
+ alignItems: p.alignItems ?? void 0,
711
+ justifyContent: p.justifyContent ?? void 0,
712
+ padding: p.padding ?? void 0,
713
+ flex: p.flex ?? void 0
714
+ },
715
+ children
716
+ }
717
+ );
718
+ }
719
+ function ScrollContainerComponent({ element, children }) {
720
+ const p = element.props;
721
+ return /* @__PURE__ */ jsx6(
722
+ ScrollView,
723
+ {
724
+ horizontal: p.horizontal ?? false,
725
+ showsVerticalScrollIndicator: p.showsScrollIndicator ?? true,
726
+ showsHorizontalScrollIndicator: p.showsScrollIndicator ?? true,
727
+ contentContainerStyle: {
728
+ padding: p.padding ?? void 0
729
+ },
730
+ style: {
731
+ flex: p.flex ?? 1,
732
+ backgroundColor: p.backgroundColor ?? void 0
733
+ },
734
+ children
735
+ }
736
+ );
737
+ }
738
+ function SafeAreaComponent({ element, children }) {
739
+ const p = element.props;
740
+ return /* @__PURE__ */ jsx6(
741
+ SafeAreaView,
742
+ {
743
+ style: {
744
+ flex: 1,
745
+ backgroundColor: p.backgroundColor ?? void 0
746
+ },
747
+ children
748
+ }
749
+ );
750
+ }
751
+ function SpacerComponent({ element }) {
752
+ const p = element.props;
753
+ return /* @__PURE__ */ jsx6(
754
+ View2,
755
+ {
756
+ style: {
757
+ width: p.size ?? void 0,
758
+ height: p.size ?? void 0,
759
+ flex: p.flex ?? void 0
760
+ }
761
+ }
762
+ );
763
+ }
764
+ function PressableComponent({ children, emit }) {
765
+ return /* @__PURE__ */ jsx6(
766
+ Pressable2,
767
+ {
768
+ onPress: () => emit?.("press"),
769
+ onLongPress: () => emit?.("longPress"),
770
+ style: ({ pressed }) => ({
771
+ opacity: pressed ? 0.7 : 1
772
+ }),
773
+ children
774
+ }
775
+ );
776
+ }
777
+ function DividerComponent({ element }) {
778
+ const p = element.props;
779
+ const isVertical = p.direction === "vertical";
780
+ const thickness = p.thickness ?? 1;
781
+ const color = p.color ?? "#e5e7eb";
782
+ return /* @__PURE__ */ jsx6(
783
+ View2,
784
+ {
785
+ style: {
786
+ width: isVertical ? thickness : "100%",
787
+ height: isVertical ? "100%" : thickness,
788
+ backgroundColor: color,
789
+ marginVertical: isVertical ? 0 : p.margin ?? 8,
790
+ marginHorizontal: isVertical ? p.margin ?? 8 : 0
791
+ }
792
+ }
793
+ );
794
+ }
795
+ var headingSizes = {
796
+ h1: 32,
797
+ h2: 24,
798
+ h3: 20,
799
+ h4: 16
800
+ };
801
+ function HeadingComponent({ element }) {
802
+ const p = element.props;
803
+ const level = p.level ?? "h2";
804
+ return /* @__PURE__ */ jsx6(
805
+ Text2,
806
+ {
807
+ style: {
808
+ fontSize: headingSizes[level],
809
+ fontWeight: "700",
810
+ color: p.color ?? "#111827",
811
+ textAlign: p.align ?? "left"
812
+ },
813
+ children: p.text
814
+ }
815
+ );
816
+ }
817
+ function ParagraphComponent({ element }) {
818
+ const p = element.props;
819
+ return /* @__PURE__ */ jsx6(
820
+ Text2,
821
+ {
822
+ numberOfLines: p.numberOfLines ?? void 0,
823
+ style: {
824
+ fontSize: p.fontSize ?? 16,
825
+ lineHeight: (p.fontSize ?? 16) * 1.5,
826
+ color: p.color ?? "#374151",
827
+ textAlign: p.align ?? "left"
828
+ },
829
+ children: p.text
830
+ }
831
+ );
832
+ }
833
+ var labelSizes = {
834
+ xs: 10,
835
+ sm: 12,
836
+ md: 14
837
+ };
838
+ function LabelComponent({ element }) {
839
+ const p = element.props;
840
+ return /* @__PURE__ */ jsx6(
841
+ Text2,
842
+ {
843
+ style: {
844
+ fontSize: labelSizes[p.size ?? "sm"],
845
+ fontWeight: p.bold ? "600" : "400",
846
+ color: p.color ?? "#6b7280"
847
+ },
848
+ children: p.text
849
+ }
850
+ );
851
+ }
852
+ function ImageComponent({ element }) {
853
+ const p = element.props;
854
+ return /* @__PURE__ */ jsx6(
855
+ RNImage,
856
+ {
857
+ source: { uri: p.src },
858
+ accessibilityLabel: p.alt ?? void 0,
859
+ resizeMode: p.resizeMode ?? "cover",
860
+ style: {
861
+ width: p.width ?? "100%",
862
+ height: p.height ?? 200,
863
+ borderRadius: p.borderRadius ?? 0
864
+ }
865
+ }
866
+ );
867
+ }
868
+ var avatarSizeMap = {
869
+ sm: 32,
870
+ md: 48,
871
+ lg: 64,
872
+ xl: 96
873
+ };
874
+ function AvatarComponent({ element }) {
875
+ const p = element.props;
876
+ const size = avatarSizeMap[p.size ?? "md"];
877
+ const bgColor = p.backgroundColor ?? "#6366f1";
878
+ if (p.src) {
879
+ return /* @__PURE__ */ jsx6(
880
+ RNImage,
881
+ {
882
+ source: { uri: p.src },
883
+ style: {
884
+ width: size,
885
+ height: size,
886
+ borderRadius: size / 2,
887
+ backgroundColor: "#e5e7eb"
888
+ }
889
+ }
890
+ );
891
+ }
892
+ return /* @__PURE__ */ jsx6(
893
+ View2,
894
+ {
895
+ style: {
896
+ width: size,
897
+ height: size,
898
+ borderRadius: size / 2,
899
+ backgroundColor: bgColor,
900
+ alignItems: "center",
901
+ justifyContent: "center"
902
+ },
903
+ children: /* @__PURE__ */ jsx6(
904
+ Text2,
905
+ {
906
+ style: {
907
+ color: "#ffffff",
908
+ fontSize: size * 0.4,
909
+ fontWeight: "600"
910
+ },
911
+ children: p.initials ?? "?"
912
+ }
913
+ )
914
+ }
915
+ );
916
+ }
917
+ var badgeVariantColors = {
918
+ default: { bg: "#e5e7eb", text: "#374151" },
919
+ info: { bg: "#dbeafe", text: "#1d4ed8" },
920
+ success: { bg: "#dcfce7", text: "#15803d" },
921
+ warning: { bg: "#fef3c7", text: "#92400e" },
922
+ error: { bg: "#fee2e2", text: "#dc2626" }
923
+ };
924
+ function BadgeComponent({ element }) {
925
+ const p = element.props;
926
+ const colors = badgeVariantColors[p.variant ?? "default"] ?? {
927
+ bg: "#e5e7eb",
928
+ text: "#374151"
929
+ };
930
+ return /* @__PURE__ */ jsx6(
931
+ View2,
932
+ {
933
+ style: {
934
+ backgroundColor: colors.bg,
935
+ paddingHorizontal: 8,
936
+ paddingVertical: 2,
937
+ borderRadius: 9999,
938
+ alignSelf: "flex-start"
939
+ },
940
+ children: /* @__PURE__ */ jsx6(
941
+ Text2,
942
+ {
943
+ style: {
944
+ fontSize: 12,
945
+ fontWeight: "600",
946
+ color: colors.text
947
+ },
948
+ children: p.label
949
+ }
950
+ )
951
+ }
952
+ );
953
+ }
954
+ function ChipComponent({ element, emit }) {
955
+ const p = element.props;
956
+ const bgColor = p.selected ? "#6366f1" : p.backgroundColor ?? "#f3f4f6";
957
+ const textColor = p.selected ? "#ffffff" : "#374151";
958
+ const hasRemove = !!element.on?.remove;
959
+ return /* @__PURE__ */ jsxs2(
960
+ Pressable2,
961
+ {
962
+ onPress: () => emit?.("press"),
963
+ style: {
964
+ flexDirection: "row",
965
+ alignItems: "center",
966
+ backgroundColor: bgColor,
967
+ paddingHorizontal: 12,
968
+ paddingVertical: 6,
969
+ borderRadius: 9999,
970
+ alignSelf: "flex-start"
971
+ },
972
+ children: [
973
+ /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: textColor }, children: p.label }),
974
+ hasRemove && /* @__PURE__ */ jsx6(Pressable2, { onPress: () => emit?.("remove"), style: { marginLeft: 4 }, children: /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: textColor, fontWeight: "700" }, children: "x" }) })
975
+ ]
976
+ }
977
+ );
978
+ }
979
+ var buttonVariantStyles = {
980
+ primary: { bg: "#3b82f6", text: "#ffffff" },
981
+ secondary: { bg: "#6b7280", text: "#ffffff" },
982
+ danger: { bg: "#dc2626", text: "#ffffff" },
983
+ outline: { bg: "transparent", text: "#3b82f6", border: "#3b82f6" },
984
+ ghost: { bg: "transparent", text: "#374151" }
985
+ };
986
+ var buttonSizeStyles = {
987
+ sm: { paddingH: 12, paddingV: 6, fontSize: 13 },
988
+ md: { paddingH: 16, paddingV: 10, fontSize: 15 },
989
+ lg: { paddingH: 24, paddingV: 14, fontSize: 17 }
990
+ };
991
+ function ButtonComponent({ element, emit }) {
992
+ const p = element.props;
993
+ const variant = buttonVariantStyles[p.variant ?? "primary"] ?? {
994
+ bg: "#3b82f6",
995
+ text: "#ffffff"
996
+ };
997
+ const size = buttonSizeStyles[p.size ?? "md"] ?? {
998
+ paddingH: 16,
999
+ paddingV: 10,
1000
+ fontSize: 15
1001
+ };
1002
+ const disabled = p.disabled || p.loading;
1003
+ return /* @__PURE__ */ jsxs2(
1004
+ Pressable2,
1005
+ {
1006
+ disabled,
1007
+ onPress: () => emit?.("press"),
1008
+ style: ({ pressed }) => ({
1009
+ backgroundColor: variant.bg,
1010
+ paddingHorizontal: size.paddingH,
1011
+ paddingVertical: size.paddingV,
1012
+ borderRadius: 8,
1013
+ alignItems: "center",
1014
+ justifyContent: "center",
1015
+ flexDirection: "row",
1016
+ opacity: disabled ? 0.5 : pressed ? 0.8 : 1,
1017
+ borderWidth: variant.border ? 1 : 0,
1018
+ borderColor: variant.border,
1019
+ alignSelf: "flex-start"
1020
+ }),
1021
+ children: [
1022
+ p.loading && /* @__PURE__ */ jsx6(
1023
+ ActivityIndicator,
1024
+ {
1025
+ size: "small",
1026
+ color: variant.text,
1027
+ style: { marginRight: 8 }
1028
+ }
1029
+ ),
1030
+ /* @__PURE__ */ jsx6(
1031
+ Text2,
1032
+ {
1033
+ style: {
1034
+ color: variant.text,
1035
+ fontSize: size.fontSize,
1036
+ fontWeight: "600"
1037
+ },
1038
+ children: p.label
1039
+ }
1040
+ )
1041
+ ]
1042
+ }
1043
+ );
1044
+ }
1045
+ function TextInputComponent({ element }) {
1046
+ const p = element.props;
1047
+ const [boundValue, setBoundValue] = useStateBinding(
1048
+ p.statePath ?? ""
1049
+ );
1050
+ const displayValue = p.statePath ? boundValue ?? "" : p.value ?? "";
1051
+ return /* @__PURE__ */ jsxs2(View2, { style: p.flex != null ? { flex: p.flex } : void 0, children: [
1052
+ p.label && /* @__PURE__ */ jsx6(Text2, { style: inputStyles.label, children: p.label }),
1053
+ /* @__PURE__ */ jsx6(
1054
+ RNTextInput,
1055
+ {
1056
+ placeholder: p.placeholder ?? void 0,
1057
+ value: displayValue,
1058
+ onChangeText: p.statePath ? setBoundValue : void 0,
1059
+ secureTextEntry: p.secureTextEntry ?? false,
1060
+ keyboardType: p.keyboardType ?? "default",
1061
+ multiline: p.multiline ?? false,
1062
+ numberOfLines: p.numberOfLines ?? void 0,
1063
+ style: [
1064
+ inputStyles.input,
1065
+ p.multiline && {
1066
+ minHeight: (p.numberOfLines ?? 3) * 20,
1067
+ textAlignVertical: "top"
1068
+ }
1069
+ ]
1070
+ }
1071
+ )
1072
+ ] });
1073
+ }
1074
+ function SwitchComponent({ element }) {
1075
+ const p = element.props;
1076
+ const [boundValue, setBoundValue] = useStateBinding(
1077
+ p.statePath ?? ""
1078
+ );
1079
+ const displayValue = p.statePath ? boundValue ?? false : p.value ?? false;
1080
+ return /* @__PURE__ */ jsxs2(View2, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
1081
+ /* @__PURE__ */ jsx6(
1082
+ RNSwitch,
1083
+ {
1084
+ value: displayValue,
1085
+ onValueChange: p.statePath ? setBoundValue : void 0,
1086
+ disabled: p.disabled ?? false
1087
+ }
1088
+ ),
1089
+ p.label && /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 16, color: "#374151" }, children: p.label })
1090
+ ] });
1091
+ }
1092
+ function CheckboxComponent({ element }) {
1093
+ const p = element.props;
1094
+ const [boundValue, setBoundValue] = useStateBinding(
1095
+ p.statePath ?? ""
1096
+ );
1097
+ const checked = p.statePath ? boundValue ?? false : p.checked ?? false;
1098
+ return /* @__PURE__ */ jsxs2(
1099
+ Pressable2,
1100
+ {
1101
+ disabled: p.disabled ?? false,
1102
+ onPress: () => {
1103
+ if (p.statePath) {
1104
+ setBoundValue(!checked);
1105
+ }
1106
+ },
1107
+ style: {
1108
+ flexDirection: "row",
1109
+ alignItems: "center",
1110
+ gap: 8,
1111
+ opacity: p.disabled ? 0.5 : 1
1112
+ },
1113
+ children: [
1114
+ /* @__PURE__ */ jsx6(
1115
+ View2,
1116
+ {
1117
+ style: {
1118
+ width: 20,
1119
+ height: 20,
1120
+ borderRadius: 4,
1121
+ borderWidth: 2,
1122
+ borderColor: checked ? "#3b82f6" : "#d1d5db",
1123
+ backgroundColor: checked ? "#3b82f6" : "transparent",
1124
+ alignItems: "center",
1125
+ justifyContent: "center"
1126
+ },
1127
+ children: checked && /* @__PURE__ */ jsx6(Text2, { style: { color: "#ffffff", fontSize: 12, fontWeight: "700" }, children: "\u2713" })
1128
+ }
1129
+ ),
1130
+ p.label && /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 16, color: "#374151" }, children: p.label })
1131
+ ]
1132
+ }
1133
+ );
1134
+ }
1135
+ function SliderComponent({ element }) {
1136
+ const p = element.props;
1137
+ const min = p.min ?? 0;
1138
+ const max = p.max ?? 100;
1139
+ const value = p.value ?? min;
1140
+ const progress = max > min ? (value - min) / (max - min) : 0;
1141
+ const trackColor = p.color ?? "#3b82f6";
1142
+ return /* @__PURE__ */ jsxs2(View2, { children: [
1143
+ p.label && /* @__PURE__ */ jsxs2(
1144
+ View2,
1145
+ {
1146
+ style: {
1147
+ flexDirection: "row",
1148
+ justifyContent: "space-between",
1149
+ marginBottom: 4
1150
+ },
1151
+ children: [
1152
+ /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: "#374151" }, children: p.label }),
1153
+ /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: "#6b7280" }, children: value })
1154
+ ]
1155
+ }
1156
+ ),
1157
+ /* @__PURE__ */ jsx6(
1158
+ View2,
1159
+ {
1160
+ style: {
1161
+ height: 6,
1162
+ backgroundColor: "#e5e7eb",
1163
+ borderRadius: 3,
1164
+ overflow: "hidden"
1165
+ },
1166
+ children: /* @__PURE__ */ jsx6(
1167
+ View2,
1168
+ {
1169
+ style: {
1170
+ width: `${Math.min(Math.max(progress * 100, 0), 100)}%`,
1171
+ height: "100%",
1172
+ backgroundColor: trackColor,
1173
+ borderRadius: 3
1174
+ }
1175
+ }
1176
+ )
1177
+ }
1178
+ )
1179
+ ] });
1180
+ }
1181
+ function SearchBarComponent({ element, emit }) {
1182
+ const p = element.props;
1183
+ return /* @__PURE__ */ jsxs2(
1184
+ View2,
1185
+ {
1186
+ style: {
1187
+ flexDirection: "row",
1188
+ alignItems: "center",
1189
+ backgroundColor: "#f3f4f6",
1190
+ borderRadius: 8,
1191
+ paddingHorizontal: 12
1192
+ },
1193
+ children: [
1194
+ /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 16, color: "#9ca3af", marginRight: 8 }, children: "Search" }),
1195
+ /* @__PURE__ */ jsx6(
1196
+ RNTextInput,
1197
+ {
1198
+ placeholder: p.placeholder ?? "Search...",
1199
+ value: p.value ?? void 0,
1200
+ returnKeyType: "search",
1201
+ onSubmitEditing: () => emit?.("submit"),
1202
+ style: {
1203
+ flex: 1,
1204
+ paddingVertical: 10,
1205
+ fontSize: 16,
1206
+ color: "#111827"
1207
+ }
1208
+ }
1209
+ )
1210
+ ]
1211
+ }
1212
+ );
1213
+ }
1214
+ function SpinnerComponent({ element }) {
1215
+ const p = element.props;
1216
+ return /* @__PURE__ */ jsx6(ActivityIndicator, { size: p.size ?? "small", color: p.color ?? "#3b82f6" });
1217
+ }
1218
+ function ProgressBarComponent({ element }) {
1219
+ const p = element.props;
1220
+ const height = p.height ?? 6;
1221
+ const progress = Math.min(Math.max(p.progress, 0), 1);
1222
+ return /* @__PURE__ */ jsx6(
1223
+ View2,
1224
+ {
1225
+ style: {
1226
+ height,
1227
+ backgroundColor: p.trackColor ?? "#e5e7eb",
1228
+ borderRadius: height / 2,
1229
+ overflow: "hidden"
1230
+ },
1231
+ children: /* @__PURE__ */ jsx6(
1232
+ View2,
1233
+ {
1234
+ style: {
1235
+ width: `${progress * 100}%`,
1236
+ height: "100%",
1237
+ backgroundColor: p.color ?? "#3b82f6",
1238
+ borderRadius: height / 2
1239
+ }
1240
+ }
1241
+ )
1242
+ }
1243
+ );
1244
+ }
1245
+ function CardComponent({ element, children }) {
1246
+ const p = element.props;
1247
+ const elevated = p.elevated !== false;
1248
+ const padding = p.padding ?? 16;
1249
+ return /* @__PURE__ */ jsxs2(
1250
+ View2,
1251
+ {
1252
+ style: [
1253
+ {
1254
+ backgroundColor: p.backgroundColor ?? "#ffffff",
1255
+ borderRadius: p.borderRadius ?? 12,
1256
+ padding
1257
+ },
1258
+ elevated && {
1259
+ shadowColor: "#000",
1260
+ shadowOffset: { width: 0, height: 2 },
1261
+ shadowOpacity: 0.1,
1262
+ shadowRadius: 8,
1263
+ elevation: 3
1264
+ }
1265
+ ],
1266
+ children: [
1267
+ p.title && /* @__PURE__ */ jsx6(
1268
+ Text2,
1269
+ {
1270
+ style: {
1271
+ fontSize: 18,
1272
+ fontWeight: "600",
1273
+ color: "#111827",
1274
+ marginBottom: p.subtitle ? 2 : 12
1275
+ },
1276
+ children: p.title
1277
+ }
1278
+ ),
1279
+ p.subtitle && /* @__PURE__ */ jsx6(
1280
+ Text2,
1281
+ {
1282
+ style: {
1283
+ fontSize: 14,
1284
+ color: "#6b7280",
1285
+ marginBottom: 12
1286
+ },
1287
+ children: p.subtitle
1288
+ }
1289
+ ),
1290
+ children
1291
+ ]
1292
+ }
1293
+ );
1294
+ }
1295
+ function ListItemComponent({ element, emit }) {
1296
+ const p = element.props;
1297
+ const hasPress = !!element.on?.press;
1298
+ const content = /* @__PURE__ */ jsxs2(
1299
+ View2,
1300
+ {
1301
+ style: {
1302
+ flexDirection: "row",
1303
+ alignItems: "center",
1304
+ paddingVertical: 12,
1305
+ paddingHorizontal: 16
1306
+ },
1307
+ children: [
1308
+ p.leading && /* @__PURE__ */ jsx6(
1309
+ Text2,
1310
+ {
1311
+ style: {
1312
+ fontSize: 16,
1313
+ color: "#6b7280",
1314
+ marginRight: 12,
1315
+ width: 24,
1316
+ textAlign: "center"
1317
+ },
1318
+ children: p.leading
1319
+ }
1320
+ ),
1321
+ /* @__PURE__ */ jsxs2(View2, { style: { flex: 1 }, children: [
1322
+ /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 16, color: "#111827" }, children: p.title }),
1323
+ p.subtitle && /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: "#6b7280", marginTop: 2 }, children: p.subtitle })
1324
+ ] }),
1325
+ p.trailing && /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 14, color: "#6b7280", marginLeft: 8 }, children: p.trailing }),
1326
+ p.showChevron && /* @__PURE__ */ jsx6(Text2, { style: { fontSize: 16, color: "#9ca3af", marginLeft: 8 }, children: ">" })
1327
+ ]
1328
+ }
1329
+ );
1330
+ if (hasPress) {
1331
+ return /* @__PURE__ */ jsx6(
1332
+ Pressable2,
1333
+ {
1334
+ onPress: () => emit?.("press"),
1335
+ style: ({ pressed }) => ({
1336
+ opacity: pressed ? 0.7 : 1,
1337
+ backgroundColor: pressed ? "#f9fafb" : "transparent"
1338
+ }),
1339
+ children: content
1340
+ }
1341
+ );
1342
+ }
1343
+ return content;
1344
+ }
1345
+ function ModalComponent({ element, children }) {
1346
+ const p = element.props;
1347
+ return /* @__PURE__ */ jsx6(
1348
+ RNModal,
1349
+ {
1350
+ visible: p.visible,
1351
+ animationType: p.animationType ?? "slide",
1352
+ transparent: true,
1353
+ children: /* @__PURE__ */ jsx6(View2, { style: modalStyles.overlay, children: /* @__PURE__ */ jsxs2(View2, { style: modalStyles.content, children: [
1354
+ p.title && /* @__PURE__ */ jsx6(Text2, { style: modalStyles.title, children: p.title }),
1355
+ children
1356
+ ] }) })
1357
+ }
1358
+ );
1359
+ }
1360
+ function createStandardActionHandlers(options) {
1361
+ return {
1362
+ navigate: async (params) => {
1363
+ const screen = params.screen;
1364
+ const navParams = params.params;
1365
+ options?.navigate?.(screen, navParams ?? void 0);
1366
+ },
1367
+ goBack: async () => {
1368
+ options?.goBack?.();
1369
+ },
1370
+ showAlert: async (params) => {
1371
+ const title = params.title;
1372
+ const message = params.message ?? void 0;
1373
+ const buttons = params.buttons;
1374
+ const alertButtons = buttons?.map((btn) => ({
1375
+ text: btn.text,
1376
+ style: btn.style
1377
+ }));
1378
+ Alert.alert(title, message, alertButtons);
1379
+ },
1380
+ share: async (params) => {
1381
+ const message = params.message;
1382
+ const url = params.url ?? void 0;
1383
+ const title = params.title ?? void 0;
1384
+ await Share.share({ message, url, title });
1385
+ },
1386
+ openURL: async (params) => {
1387
+ const url = params.url;
1388
+ const canOpen = await Linking.canOpenURL(url);
1389
+ if (canOpen) {
1390
+ await Linking.openURL(url);
1391
+ }
1392
+ },
1393
+ setData: async (params) => {
1394
+ const path = params.path;
1395
+ const value = params.value;
1396
+ options?.onSetData?.(path, value);
1397
+ },
1398
+ refresh: async (params) => {
1399
+ const target = params.target ?? void 0;
1400
+ options?.onRefresh?.(target);
1401
+ }
1402
+ };
1403
+ }
1404
+ var standardComponents = {
1405
+ // Layout
1406
+ Container: ContainerComponent,
1407
+ Row: RowComponent,
1408
+ Column: ColumnComponent,
1409
+ ScrollContainer: ScrollContainerComponent,
1410
+ SafeArea: SafeAreaComponent,
1411
+ Pressable: PressableComponent,
1412
+ Spacer: SpacerComponent,
1413
+ Divider: DividerComponent,
1414
+ // Content
1415
+ Heading: HeadingComponent,
1416
+ Paragraph: ParagraphComponent,
1417
+ Label: LabelComponent,
1418
+ Image: ImageComponent,
1419
+ Avatar: AvatarComponent,
1420
+ Badge: BadgeComponent,
1421
+ Chip: ChipComponent,
1422
+ // Input
1423
+ Button: ButtonComponent,
1424
+ TextInput: TextInputComponent,
1425
+ Switch: SwitchComponent,
1426
+ Checkbox: CheckboxComponent,
1427
+ Slider: SliderComponent,
1428
+ SearchBar: SearchBarComponent,
1429
+ // Feedback
1430
+ Spinner: SpinnerComponent,
1431
+ ProgressBar: ProgressBarComponent,
1432
+ // Composite
1433
+ Card: CardComponent,
1434
+ ListItem: ListItemComponent,
1435
+ Modal: ModalComponent
1436
+ };
1437
+ var inputStyles = StyleSheet2.create({
1438
+ label: {
1439
+ fontSize: 14,
1440
+ fontWeight: "500",
1441
+ color: "#374151",
1442
+ marginBottom: 4
1443
+ },
1444
+ input: {
1445
+ borderWidth: 1,
1446
+ borderColor: "#d1d5db",
1447
+ borderRadius: 8,
1448
+ paddingHorizontal: 12,
1449
+ paddingVertical: 10,
1450
+ fontSize: 16,
1451
+ color: "#111827",
1452
+ backgroundColor: "#ffffff"
1453
+ }
1454
+ });
1455
+ var modalStyles = StyleSheet2.create({
1456
+ overlay: {
1457
+ flex: 1,
1458
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
1459
+ justifyContent: "flex-end"
1460
+ },
1461
+ content: {
1462
+ backgroundColor: "#ffffff",
1463
+ borderTopLeftRadius: 16,
1464
+ borderTopRightRadius: 16,
1465
+ padding: 24,
1466
+ maxHeight: "80%"
1467
+ },
1468
+ title: {
1469
+ fontSize: 20,
1470
+ fontWeight: "600",
1471
+ color: "#111827",
1472
+ marginBottom: 16
1473
+ }
1474
+ });
1475
+
1476
+ // src/renderer.tsx
1477
+ import { Fragment, jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
1478
+ function ElementRenderer({
1479
+ element,
1480
+ spec,
1481
+ registry,
1482
+ loading,
1483
+ fallback
1484
+ }) {
1485
+ const repeatScope = useRepeatScope();
1486
+ const { ctx } = useVisibility();
1487
+ const { execute } = useActions();
1488
+ let effectiveElement = element;
1489
+ if (repeatScope) {
1490
+ const rewrittenProps = rewriteRepeatTokens(
1491
+ element.props,
1492
+ repeatScope.basePath,
1493
+ repeatScope.index
1494
+ );
1495
+ const rewrittenVisible = element.visible !== void 0 ? rewriteRepeatTokens(
1496
+ element.visible,
1497
+ repeatScope.basePath,
1498
+ repeatScope.index
1499
+ ) : element.visible;
1500
+ const rewrittenOn = element.on !== void 0 ? rewriteRepeatTokens(
1501
+ element.on,
1502
+ repeatScope.basePath,
1503
+ repeatScope.index
1504
+ ) : element.on;
1505
+ if (rewrittenProps !== element.props || rewrittenVisible !== element.visible || rewrittenOn !== element.on) {
1506
+ effectiveElement = {
1507
+ ...element,
1508
+ props: rewrittenProps,
1509
+ visible: rewrittenVisible,
1510
+ on: rewrittenOn
1511
+ };
1512
+ }
1513
+ }
1514
+ const isVisible = useIsVisible(effectiveElement.visible);
1515
+ const onBindings = effectiveElement.on;
1516
+ const emit = useCallback4(
1517
+ (eventName) => {
1518
+ const binding = onBindings?.[eventName];
1519
+ if (!binding) return;
1520
+ const bindings = Array.isArray(binding) ? binding : [binding];
1521
+ for (const b of bindings) {
1522
+ execute(b);
1523
+ }
1524
+ },
1525
+ [onBindings, execute]
1526
+ );
1527
+ if (!isVisible) {
1528
+ return null;
1529
+ }
1530
+ const resolvedProps = resolveElementProps(
1531
+ effectiveElement.props,
1532
+ ctx
1533
+ );
1534
+ const resolvedElement = resolvedProps !== effectiveElement.props ? { ...effectiveElement, props: resolvedProps } : effectiveElement;
1535
+ const Component = registry[resolvedElement.type] ?? fallback;
1536
+ if (!Component) {
1537
+ console.warn(`No renderer for component type: ${resolvedElement.type}`);
1538
+ return null;
1539
+ }
1540
+ const children = resolvedElement.repeat ? /* @__PURE__ */ jsx7(
1541
+ RepeatChildren,
1542
+ {
1543
+ element: resolvedElement,
1544
+ spec,
1545
+ registry,
1546
+ loading,
1547
+ fallback
1548
+ }
1549
+ ) : resolvedElement.children?.map((childKey) => {
1550
+ const childElement = spec.elements[childKey];
1551
+ if (!childElement) {
1552
+ if (!loading) {
1553
+ console.warn(
1554
+ `[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
1555
+ );
1556
+ }
1557
+ return null;
1558
+ }
1559
+ return /* @__PURE__ */ jsx7(
1560
+ ElementRenderer,
1561
+ {
1562
+ element: childElement,
1563
+ spec,
1564
+ registry,
1565
+ loading,
1566
+ fallback
1567
+ },
1568
+ childKey
1569
+ );
1570
+ });
1571
+ return /* @__PURE__ */ jsx7(Component, { element: resolvedElement, emit, loading, children });
1572
+ }
1573
+ function RepeatChildren({
1574
+ element,
1575
+ spec,
1576
+ registry,
1577
+ loading,
1578
+ fallback
1579
+ }) {
1580
+ const { state } = useStateStore();
1581
+ const repeat = element.repeat;
1582
+ const statePath = repeat.path;
1583
+ const items = getByPath2(state, statePath) ?? [];
1584
+ return /* @__PURE__ */ jsx7(Fragment, { children: items.map((item, index) => {
1585
+ const key = repeat.key && typeof item === "object" && item !== null ? String(item[repeat.key] ?? index) : String(index);
1586
+ return /* @__PURE__ */ jsx7(
1587
+ RepeatScopeProvider,
1588
+ {
1589
+ basePath: `${statePath}/${index}`,
1590
+ index,
1591
+ children: element.children?.map((childKey) => {
1592
+ const childElement = spec.elements[childKey];
1593
+ if (!childElement) {
1594
+ if (!loading) {
1595
+ console.warn(
1596
+ `[json-render] Missing element "${childKey}" referenced as child of "${element.type}" (repeat). This element will not render.`
1597
+ );
1598
+ }
1599
+ return null;
1600
+ }
1601
+ return /* @__PURE__ */ jsx7(
1602
+ ElementRenderer,
1603
+ {
1604
+ element: childElement,
1605
+ spec,
1606
+ registry,
1607
+ loading,
1608
+ fallback
1609
+ },
1610
+ childKey
1611
+ );
1612
+ })
1613
+ },
1614
+ key
1615
+ );
1616
+ }) });
1617
+ }
1618
+ function Renderer({
1619
+ spec,
1620
+ registry: customRegistry,
1621
+ includeStandard = true,
1622
+ loading,
1623
+ fallback
1624
+ }) {
1625
+ const registry = useMemo5(
1626
+ () => ({
1627
+ ...includeStandard ? standardComponents : {},
1628
+ ...customRegistry
1629
+ }),
1630
+ [customRegistry, includeStandard]
1631
+ );
1632
+ if (!spec || !spec.root) {
1633
+ return null;
1634
+ }
1635
+ const rootElement = spec.elements[spec.root];
1636
+ if (!rootElement) {
1637
+ return null;
1638
+ }
1639
+ return /* @__PURE__ */ jsx7(
1640
+ ElementRenderer,
1641
+ {
1642
+ element: rootElement,
1643
+ spec,
1644
+ registry,
1645
+ loading,
1646
+ fallback
1647
+ }
1648
+ );
1649
+ }
1650
+ function JSONUIProvider({
1651
+ registry,
1652
+ initialState,
1653
+ authState,
1654
+ actionHandlers,
1655
+ navigate,
1656
+ validationFunctions,
1657
+ onStateChange,
1658
+ children
1659
+ }) {
1660
+ return /* @__PURE__ */ jsx7(
1661
+ StateProvider,
1662
+ {
1663
+ initialState,
1664
+ authState,
1665
+ onStateChange,
1666
+ children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers: actionHandlers, navigate, children: /* @__PURE__ */ jsxs3(ValidationProvider, { customFunctions: validationFunctions, children: [
1667
+ children,
1668
+ /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
1669
+ ] }) }) })
1670
+ }
1671
+ );
1672
+ }
1673
+ function ConfirmationDialogManager() {
1674
+ const { pendingConfirmation, confirm, cancel } = useActions();
1675
+ if (!pendingConfirmation?.action.confirm) {
1676
+ return null;
1677
+ }
1678
+ return /* @__PURE__ */ jsx7(
1679
+ ConfirmDialog,
1680
+ {
1681
+ confirm: pendingConfirmation.action.confirm,
1682
+ onConfirm: confirm,
1683
+ onCancel: cancel
1684
+ }
1685
+ );
1686
+ }
1687
+ function createRendererFromCatalog(_catalog, registry) {
1688
+ return function CatalogRenderer(props) {
1689
+ return /* @__PURE__ */ jsx7(Renderer, { ...props, registry });
1690
+ };
1691
+ }
1692
+ function defineRegistry(_catalog, options) {
1693
+ const registry = {};
1694
+ if (options.components) {
1695
+ for (const [name, componentFn] of Object.entries(options.components)) {
1696
+ registry[name] = ({
1697
+ element,
1698
+ children,
1699
+ emit,
1700
+ loading
1701
+ }) => {
1702
+ return componentFn({
1703
+ props: element.props,
1704
+ children,
1705
+ emit,
1706
+ loading
1707
+ });
1708
+ };
1709
+ }
1710
+ }
1711
+ const actionMap = options.actions ? Object.entries(options.actions) : [];
1712
+ const handlers = (getSetState, getState) => {
1713
+ const result = {};
1714
+ for (const [name, actionFn] of actionMap) {
1715
+ result[name] = async (params) => {
1716
+ const setState = getSetState();
1717
+ const state = getState();
1718
+ if (setState) {
1719
+ await actionFn(params, setState, state);
1720
+ }
1721
+ };
1722
+ }
1723
+ return result;
1724
+ };
1725
+ const executeAction2 = async (actionName, params, setState, state = {}) => {
1726
+ const entry = actionMap.find(([name]) => name === actionName);
1727
+ if (entry) {
1728
+ await entry[1](params, setState, state);
1729
+ } else {
1730
+ console.warn(`Unknown action: ${actionName}`);
1731
+ }
1732
+ };
1733
+ return { registry, handlers, executeAction: executeAction2 };
1734
+ }
1735
+ function createRenderer(catalog, components) {
1736
+ const registry = components;
1737
+ return function CatalogRenderer({
1738
+ spec,
1739
+ state,
1740
+ onAction,
1741
+ onStateChange,
1742
+ loading,
1743
+ authState,
1744
+ fallback
1745
+ }) {
1746
+ const actionHandlers = onAction ? {
1747
+ __default__: (params) => {
1748
+ const actionName = params.__actionName__;
1749
+ const actionParams = params.__actionParams__;
1750
+ return onAction(actionName, actionParams);
1751
+ }
1752
+ } : void 0;
1753
+ return /* @__PURE__ */ jsx7(
1754
+ StateProvider,
1755
+ {
1756
+ initialState: state,
1757
+ authState,
1758
+ onStateChange,
1759
+ children: /* @__PURE__ */ jsx7(VisibilityProvider, { children: /* @__PURE__ */ jsx7(ActionProvider, { handlers: actionHandlers, children: /* @__PURE__ */ jsxs3(ValidationProvider, { children: [
1760
+ /* @__PURE__ */ jsx7(
1761
+ Renderer,
1762
+ {
1763
+ spec,
1764
+ registry,
1765
+ loading,
1766
+ fallback
1767
+ }
1768
+ ),
1769
+ /* @__PURE__ */ jsx7(ConfirmationDialogManager, {})
1770
+ ] }) }) })
1771
+ }
1772
+ );
1773
+ };
1774
+ }
1775
+
1776
+ // src/hooks.ts
1777
+ import { useState as useState4, useCallback as useCallback5, useRef as useRef2, useEffect as useEffect2 } from "react";
1778
+ import {
1779
+ setByPath as setByPath2,
1780
+ getByPath as getByPath3,
1781
+ removeByPath,
1782
+ validateSpec,
1783
+ autoFixSpec,
1784
+ formatSpecIssues
1785
+ } from "@json-render/core";
1786
+ function looksLikeJson(line) {
1787
+ return line.startsWith("{") || line.startsWith("[");
1788
+ }
1789
+ function parsePatchLine(line) {
1790
+ const trimmed = line.trim();
1791
+ if (!trimmed || trimmed.startsWith("//")) {
1792
+ return { patch: null, malformed: false };
1793
+ }
1794
+ if (!looksLikeJson(trimmed)) {
1795
+ return { patch: null, malformed: false };
1796
+ }
1797
+ try {
1798
+ return { patch: JSON.parse(trimmed), malformed: false };
1799
+ } catch {
1800
+ }
1801
+ let attempt = trimmed;
1802
+ for (let i = 0; i < 3; i++) {
1803
+ const last = attempt[attempt.length - 1];
1804
+ if (last === "}" || last === "]") {
1805
+ attempt = attempt.slice(0, -1);
1806
+ try {
1807
+ const result = JSON.parse(attempt);
1808
+ console.warn(
1809
+ `[json-render] Recovered malformed JSONL line by removing ${i + 1} trailing '${last}'`
1810
+ );
1811
+ return { patch: result, malformed: false };
1812
+ } catch {
1813
+ }
1814
+ } else {
1815
+ break;
1816
+ }
1817
+ }
1818
+ return { patch: null, malformed: true };
1819
+ }
1820
+ function setSpecValue(newSpec, path, value) {
1821
+ if (path === "/root") {
1822
+ newSpec.root = value;
1823
+ return;
1824
+ }
1825
+ if (path === "/state") {
1826
+ newSpec.state = value;
1827
+ return;
1828
+ }
1829
+ if (path.startsWith("/state/")) {
1830
+ if (!newSpec.state) newSpec.state = {};
1831
+ const statePath = path.slice("/state".length);
1832
+ setByPath2(newSpec.state, statePath, value);
1833
+ return;
1834
+ }
1835
+ if (path.startsWith("/elements/")) {
1836
+ const pathParts = path.slice("/elements/".length).split("/");
1837
+ const elementKey = pathParts[0];
1838
+ if (!elementKey) return;
1839
+ if (pathParts.length === 1) {
1840
+ newSpec.elements[elementKey] = value;
1841
+ } else {
1842
+ const element = newSpec.elements[elementKey];
1843
+ if (element) {
1844
+ const propPath = "/" + pathParts.slice(1).join("/");
1845
+ const newElement = { ...element };
1846
+ setByPath2(
1847
+ newElement,
1848
+ propPath,
1849
+ value
1850
+ );
1851
+ newSpec.elements[elementKey] = newElement;
1852
+ }
1853
+ }
1854
+ }
1855
+ }
1856
+ function removeSpecValue(newSpec, path) {
1857
+ if (path === "/state") {
1858
+ newSpec.state = void 0;
1859
+ return;
1860
+ }
1861
+ if (path.startsWith("/state/") && newSpec.state) {
1862
+ const statePath = path.slice("/state".length);
1863
+ removeByPath(newSpec.state, statePath);
1864
+ return;
1865
+ }
1866
+ if (path.startsWith("/elements/")) {
1867
+ const pathParts = path.slice("/elements/".length).split("/");
1868
+ const elementKey = pathParts[0];
1869
+ if (!elementKey) return;
1870
+ if (pathParts.length === 1) {
1871
+ const { [elementKey]: _, ...rest } = newSpec.elements;
1872
+ newSpec.elements = rest;
1873
+ } else {
1874
+ const element = newSpec.elements[elementKey];
1875
+ if (element) {
1876
+ const propPath = "/" + pathParts.slice(1).join("/");
1877
+ const newElement = { ...element };
1878
+ removeByPath(
1879
+ newElement,
1880
+ propPath
1881
+ );
1882
+ newSpec.elements[elementKey] = newElement;
1883
+ }
1884
+ }
1885
+ }
1886
+ }
1887
+ function getSpecValue(spec, path) {
1888
+ if (path === "/root") return spec.root;
1889
+ return getByPath3(spec, path);
1890
+ }
1891
+ function applyPatch(spec, patch) {
1892
+ const newSpec = { ...spec, elements: { ...spec.elements } };
1893
+ switch (patch.op) {
1894
+ case "add":
1895
+ case "replace": {
1896
+ setSpecValue(newSpec, patch.path, patch.value);
1897
+ break;
1898
+ }
1899
+ case "remove": {
1900
+ removeSpecValue(newSpec, patch.path);
1901
+ break;
1902
+ }
1903
+ case "move": {
1904
+ if (!patch.from) break;
1905
+ const moveValue = getSpecValue(newSpec, patch.from);
1906
+ removeSpecValue(newSpec, patch.from);
1907
+ setSpecValue(newSpec, patch.path, moveValue);
1908
+ break;
1909
+ }
1910
+ case "copy": {
1911
+ if (!patch.from) break;
1912
+ const copyValue = getSpecValue(newSpec, patch.from);
1913
+ setSpecValue(newSpec, patch.path, copyValue);
1914
+ break;
1915
+ }
1916
+ case "test": {
1917
+ break;
1918
+ }
1919
+ }
1920
+ return newSpec;
1921
+ }
1922
+ function useUIStream({
1923
+ api,
1924
+ onComplete,
1925
+ onError,
1926
+ fetch: fetchFn = globalThis.fetch,
1927
+ validate: enableValidation = false,
1928
+ maxRetries = 5
1929
+ }) {
1930
+ const [spec, setSpec] = useState4(null);
1931
+ const [isStreaming, setIsStreaming] = useState4(false);
1932
+ const [error, setError] = useState4(null);
1933
+ const [rawLines, setRawLines] = useState4([]);
1934
+ const abortControllerRef = useRef2(null);
1935
+ const stop = useCallback5(() => {
1936
+ abortControllerRef.current?.abort();
1937
+ abortControllerRef.current = null;
1938
+ setIsStreaming(false);
1939
+ }, []);
1940
+ const clear = useCallback5(() => {
1941
+ setSpec(null);
1942
+ setError(null);
1943
+ setRawLines([]);
1944
+ }, []);
1945
+ const streamRequest = useCallback5(
1946
+ async (prompt, context, initialSpec, abortOnMalformed) => {
1947
+ let currentSpec = initialSpec;
1948
+ setSpec(currentSpec);
1949
+ const malformedLines = [];
1950
+ const response = await fetchFn(api, {
1951
+ method: "POST",
1952
+ headers: { "Content-Type": "application/json" },
1953
+ body: JSON.stringify({
1954
+ prompt,
1955
+ context,
1956
+ currentSpec
1957
+ }),
1958
+ signal: abortControllerRef.current.signal
1959
+ });
1960
+ console.log("[useUIStream] response status:", response.status);
1961
+ if (!response.ok) {
1962
+ let errorMessage = `HTTP error: ${response.status}`;
1963
+ try {
1964
+ const errorData = await response.json();
1965
+ if (errorData.message) {
1966
+ errorMessage = errorData.message;
1967
+ } else if (errorData.error) {
1968
+ errorMessage = errorData.error;
1969
+ }
1970
+ } catch {
1971
+ }
1972
+ throw new Error(errorMessage);
1973
+ }
1974
+ const reader = response.body?.getReader();
1975
+ if (!reader) {
1976
+ throw new Error("No response body");
1977
+ }
1978
+ const decoder = new TextDecoder();
1979
+ let buffer = "";
1980
+ let chunkCount = 0;
1981
+ let totalBytes = 0;
1982
+ let aborted = false;
1983
+ while (true) {
1984
+ const { done, value } = await reader.read();
1985
+ if (done) {
1986
+ console.log(
1987
+ "[useUIStream] stream done. Total chunks:",
1988
+ chunkCount,
1989
+ "bytes:",
1990
+ totalBytes
1991
+ );
1992
+ break;
1993
+ }
1994
+ chunkCount++;
1995
+ totalBytes += value?.byteLength ?? 0;
1996
+ const decoded = decoder.decode(value, { stream: true });
1997
+ buffer += decoded;
1998
+ if (chunkCount <= 5) {
1999
+ console.log(
2000
+ `[useUIStream] chunk #${chunkCount} (${value?.byteLength ?? 0}b):`,
2001
+ decoded.slice(0, 200)
2002
+ );
2003
+ }
2004
+ const lines = buffer.split("\n");
2005
+ buffer = lines.pop() ?? "";
2006
+ const newLines = [];
2007
+ for (const line of lines) {
2008
+ if (line.trim()) {
2009
+ newLines.push(line);
2010
+ }
2011
+ const { patch, malformed } = parsePatchLine(line);
2012
+ if (patch) {
2013
+ console.log("[useUIStream] applied patch:", patch.op, patch.path);
2014
+ currentSpec = applyPatch(currentSpec, patch);
2015
+ setSpec({ ...currentSpec });
2016
+ } else if (malformed) {
2017
+ malformedLines.push(line.trim());
2018
+ console.warn(
2019
+ "[useUIStream] malformed JSON line:",
2020
+ line.trim().slice(0, 200)
2021
+ );
2022
+ if (abortOnMalformed) {
2023
+ console.warn(
2024
+ "[useUIStream] aborting stream due to malformed JSON, will retry"
2025
+ );
2026
+ await reader.cancel();
2027
+ aborted = true;
2028
+ break;
2029
+ }
2030
+ }
2031
+ }
2032
+ if (aborted) break;
2033
+ if (newLines.length > 0) {
2034
+ setRawLines((prev) => [...prev, ...newLines]);
2035
+ }
2036
+ }
2037
+ if (!aborted && buffer.trim()) {
2038
+ console.log("[useUIStream] remaining buffer:", buffer.slice(0, 200));
2039
+ setRawLines((prev) => [...prev, buffer]);
2040
+ const { patch, malformed } = parsePatchLine(buffer);
2041
+ if (patch) {
2042
+ currentSpec = applyPatch(currentSpec, patch);
2043
+ setSpec({ ...currentSpec });
2044
+ } else if (malformed) {
2045
+ malformedLines.push(buffer.trim());
2046
+ }
2047
+ }
2048
+ console.log(
2049
+ "[useUIStream] spec state - root:",
2050
+ currentSpec.root,
2051
+ "elements:",
2052
+ Object.keys(currentSpec.elements).length,
2053
+ "completed:",
2054
+ !aborted,
2055
+ "malformed:",
2056
+ malformedLines.length
2057
+ );
2058
+ return { spec: currentSpec, completed: !aborted, malformedLines };
2059
+ },
2060
+ [api, fetchFn]
2061
+ );
2062
+ const send = useCallback5(
2063
+ async (prompt, context) => {
2064
+ abortControllerRef.current?.abort();
2065
+ abortControllerRef.current = new AbortController();
2066
+ setIsStreaming(true);
2067
+ setError(null);
2068
+ setRawLines([]);
2069
+ const previousSpec = context?.previousSpec;
2070
+ let currentSpec = previousSpec && previousSpec.root ? { ...previousSpec, elements: { ...previousSpec.elements } } : { root: "", elements: {} };
2071
+ let retriesUsed = 0;
2072
+ let currentPrompt = prompt;
2073
+ let currentContext = context;
2074
+ try {
2075
+ while (retriesUsed <= maxRetries) {
2076
+ const result = await streamRequest(
2077
+ currentPrompt,
2078
+ currentContext,
2079
+ currentSpec,
2080
+ enableValidation
2081
+ // only abort on malformed when validation is on
2082
+ );
2083
+ currentSpec = result.spec;
2084
+ if (!result.completed && result.malformedLines.length > 0) {
2085
+ if (retriesUsed >= maxRetries) {
2086
+ console.warn(
2087
+ "[useUIStream] malformed lines but max retries exhausted"
2088
+ );
2089
+ break;
2090
+ }
2091
+ retriesUsed++;
2092
+ console.warn(
2093
+ `[useUIStream] mid-stream retry ${retriesUsed}/${maxRetries} due to malformed JSON`
2094
+ );
2095
+ currentContext = { ...context, previousSpec: currentSpec };
2096
+ currentPrompt = `The previous generation contained malformed JSON that could not be parsed. The line was:
2097
+ ${result.malformedLines[result.malformedLines.length - 1]?.slice(0, 500)}
2098
+
2099
+ The current spec state is provided. Continue generating from where you left off. Output ONLY the remaining patches needed to complete the UI.`;
2100
+ continue;
2101
+ }
2102
+ if (!enableValidation || !currentSpec.root) {
2103
+ break;
2104
+ }
2105
+ const { spec: fixedSpec, fixes } = autoFixSpec(currentSpec);
2106
+ if (fixes.length > 0) {
2107
+ console.log("[useUIStream] auto-fixed spec issues:", fixes);
2108
+ currentSpec = fixedSpec;
2109
+ setSpec({ ...currentSpec });
2110
+ }
2111
+ const validation = validateSpec(currentSpec);
2112
+ if (validation.valid) {
2113
+ console.log("[useUIStream] spec validation passed");
2114
+ break;
2115
+ }
2116
+ const errors = validation.issues.filter(
2117
+ (i) => i.severity === "error"
2118
+ );
2119
+ if (retriesUsed >= maxRetries) {
2120
+ console.warn(
2121
+ "[useUIStream] spec validation failed, max retries exhausted:",
2122
+ errors.map((e) => e.message)
2123
+ );
2124
+ break;
2125
+ }
2126
+ retriesUsed++;
2127
+ const issueText = formatSpecIssues(validation.issues);
2128
+ console.warn(
2129
+ `[useUIStream] post-stream retry ${retriesUsed}/${maxRetries}:`,
2130
+ errors.map((e) => e.message)
2131
+ );
2132
+ currentContext = { ...context, previousSpec: currentSpec };
2133
+ currentPrompt = `FIX THE FOLLOWING ERRORS in the current UI spec. Output ONLY the patches needed to fix these issues, do not recreate the entire UI.
2134
+
2135
+ ` + issueText;
2136
+ }
2137
+ onComplete?.(currentSpec);
2138
+ } catch (err) {
2139
+ if (err.name === "AbortError") {
2140
+ return;
2141
+ }
2142
+ const error2 = err instanceof Error ? err : new Error(String(err));
2143
+ setError(error2);
2144
+ onError?.(error2);
2145
+ } finally {
2146
+ setIsStreaming(false);
2147
+ }
2148
+ },
2149
+ [
2150
+ api,
2151
+ fetchFn,
2152
+ onComplete,
2153
+ onError,
2154
+ enableValidation,
2155
+ maxRetries,
2156
+ streamRequest
2157
+ ]
2158
+ );
2159
+ useEffect2(() => {
2160
+ return () => {
2161
+ abortControllerRef.current?.abort();
2162
+ };
2163
+ }, []);
2164
+ return {
2165
+ spec,
2166
+ isStreaming,
2167
+ error,
2168
+ rawLines,
2169
+ send,
2170
+ stop,
2171
+ clear
2172
+ };
2173
+ }
2174
+ function flatToTree(elements) {
2175
+ const elementMap = {};
2176
+ let root = "";
2177
+ for (const element of elements) {
2178
+ elementMap[element.key] = {
2179
+ type: element.type,
2180
+ props: element.props,
2181
+ children: [],
2182
+ visible: element.visible
2183
+ };
2184
+ }
2185
+ for (const element of elements) {
2186
+ if (element.parentKey) {
2187
+ const parent = elementMap[element.parentKey];
2188
+ if (parent) {
2189
+ if (!parent.children) {
2190
+ parent.children = [];
2191
+ }
2192
+ parent.children.push(element.key);
2193
+ }
2194
+ } else {
2195
+ root = element.key;
2196
+ }
2197
+ }
2198
+ return { root, elements: elementMap };
2199
+ }
2200
+ export {
2201
+ ActionProvider,
2202
+ ConfirmDialog,
2203
+ JSONUIProvider,
2204
+ Renderer,
2205
+ RepeatScopeProvider,
2206
+ StateProvider,
2207
+ ValidationProvider,
2208
+ VisibilityProvider,
2209
+ createRenderer,
2210
+ createRendererFromCatalog,
2211
+ createStandardActionHandlers,
2212
+ defineRegistry,
2213
+ elementTreeSchema,
2214
+ flatToTree,
2215
+ rewriteRepeatTokens,
2216
+ schema,
2217
+ standardActionDefinitions,
2218
+ standardComponentDefinitions,
2219
+ standardComponents,
2220
+ useAction,
2221
+ useActions,
2222
+ useFieldValidation,
2223
+ useIsVisible,
2224
+ useRepeatScope,
2225
+ useStateBinding,
2226
+ useStateStore,
2227
+ useStateValue,
2228
+ useUIStream,
2229
+ useValidation,
2230
+ useVisibility
2231
+ };
2232
+ //# sourceMappingURL=index.mjs.map