@json-render/react-native 0.5.2 → 0.6.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/README.md +12 -8
- package/dist/catalog.d.mts +5 -12
- package/dist/catalog.d.ts +5 -12
- package/dist/catalog.js +14 -21
- package/dist/catalog.js.map +1 -1
- package/dist/catalog.mjs +1 -1
- package/dist/{chunk-7PT5BNV7.mjs → chunk-QYY3Q42V.mjs} +15 -22
- package/dist/chunk-QYY3Q42V.mjs.map +1 -0
- package/dist/{chunk-MJPWQ6K7.mjs → chunk-T27PQA6P.mjs} +10 -10
- package/dist/chunk-T27PQA6P.mjs.map +1 -0
- package/dist/index.d.mts +50 -51
- package/dist/index.d.ts +50 -51
- package/dist/index.js +1267 -1305
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1464 -1491
- package/dist/index.mjs.map +1 -1
- package/dist/schema.js +9 -9
- package/dist/schema.js.map +1 -1
- package/dist/schema.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-7PT5BNV7.mjs.map +0 -1
- package/dist/chunk-MJPWQ6K7.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -39,18 +39,17 @@ __export(index_exports, {
|
|
|
39
39
|
ValidationProvider: () => ValidationProvider,
|
|
40
40
|
VisibilityProvider: () => VisibilityProvider,
|
|
41
41
|
createRenderer: () => createRenderer,
|
|
42
|
-
createRendererFromCatalog: () => createRendererFromCatalog,
|
|
43
42
|
createStandardActionHandlers: () => createStandardActionHandlers,
|
|
44
43
|
defineRegistry: () => defineRegistry,
|
|
45
44
|
elementTreeSchema: () => elementTreeSchema,
|
|
46
45
|
flatToTree: () => flatToTree,
|
|
47
|
-
rewriteRepeatTokens: () => rewriteRepeatTokens,
|
|
48
46
|
schema: () => schema,
|
|
49
47
|
standardActionDefinitions: () => standardActionDefinitions,
|
|
50
48
|
standardComponentDefinitions: () => standardComponentDefinitions,
|
|
51
49
|
standardComponents: () => standardComponents,
|
|
52
50
|
useAction: () => useAction,
|
|
53
51
|
useActions: () => useActions,
|
|
52
|
+
useBoundProp: () => useBoundProp,
|
|
54
53
|
useFieldValidation: () => useFieldValidation,
|
|
55
54
|
useIsVisible: () => useIsVisible,
|
|
56
55
|
useRepeatScope: () => useRepeatScope,
|
|
@@ -70,11 +69,12 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
70
69
|
var StateContext = (0, import_react.createContext)(null);
|
|
71
70
|
function StateProvider({
|
|
72
71
|
initialState = {},
|
|
73
|
-
authState,
|
|
74
72
|
onStateChange,
|
|
75
73
|
children
|
|
76
74
|
}) {
|
|
77
75
|
const [state, setStateInternal] = (0, import_react.useState)(initialState);
|
|
76
|
+
const stateRef = (0, import_react.useRef)(state);
|
|
77
|
+
stateRef.current = state;
|
|
78
78
|
const initialStateJsonRef = (0, import_react.useRef)(JSON.stringify(initialState));
|
|
79
79
|
(0, import_react.useEffect)(() => {
|
|
80
80
|
const newJson = JSON.stringify(initialState);
|
|
@@ -85,7 +85,10 @@ function StateProvider({
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}, [initialState]);
|
|
88
|
-
const get = (0, import_react.useCallback)(
|
|
88
|
+
const get = (0, import_react.useCallback)(
|
|
89
|
+
(path) => (0, import_core.getByPath)(stateRef.current, path),
|
|
90
|
+
[]
|
|
91
|
+
);
|
|
89
92
|
const set = (0, import_react.useCallback)(
|
|
90
93
|
(path, value2) => {
|
|
91
94
|
setStateInternal((prev) => {
|
|
@@ -99,26 +102,28 @@ function StateProvider({
|
|
|
99
102
|
);
|
|
100
103
|
const update = (0, import_react.useCallback)(
|
|
101
104
|
(updates) => {
|
|
105
|
+
const entries = Object.entries(updates);
|
|
102
106
|
setStateInternal((prev) => {
|
|
103
107
|
const next = { ...prev };
|
|
104
|
-
for (const [path, value2] of
|
|
108
|
+
for (const [path, value2] of entries) {
|
|
105
109
|
(0, import_core.setByPath)(next, path, value2);
|
|
106
|
-
onStateChange?.(path, value2);
|
|
107
110
|
}
|
|
108
111
|
return next;
|
|
109
112
|
});
|
|
113
|
+
for (const [path, value2] of entries) {
|
|
114
|
+
onStateChange?.(path, value2);
|
|
115
|
+
}
|
|
110
116
|
},
|
|
111
117
|
[onStateChange]
|
|
112
118
|
);
|
|
113
119
|
const value = (0, import_react.useMemo)(
|
|
114
120
|
() => ({
|
|
115
121
|
state,
|
|
116
|
-
authState,
|
|
117
122
|
get,
|
|
118
123
|
set,
|
|
119
124
|
update
|
|
120
125
|
}),
|
|
121
|
-
[state,
|
|
126
|
+
[state, get, set, update]
|
|
122
127
|
);
|
|
123
128
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StateContext.Provider, { value, children });
|
|
124
129
|
}
|
|
@@ -149,13 +154,12 @@ var import_core2 = require("@json-render/core");
|
|
|
149
154
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
150
155
|
var VisibilityContext = (0, import_react2.createContext)(null);
|
|
151
156
|
function VisibilityProvider({ children }) {
|
|
152
|
-
const { state
|
|
157
|
+
const { state } = useStateStore();
|
|
153
158
|
const ctx = (0, import_react2.useMemo)(
|
|
154
159
|
() => ({
|
|
155
|
-
stateModel: state
|
|
156
|
-
authState
|
|
160
|
+
stateModel: state
|
|
157
161
|
}),
|
|
158
|
-
[state
|
|
162
|
+
[state]
|
|
159
163
|
);
|
|
160
164
|
const isVisible = (0, import_react2.useMemo)(
|
|
161
165
|
() => (condition) => (0, import_core2.evaluateVisibility)(condition, ctx),
|
|
@@ -197,8 +201,8 @@ function deepResolveValue(value, get) {
|
|
|
197
201
|
if (typeof value === "object" && !Array.isArray(value)) {
|
|
198
202
|
const obj = value;
|
|
199
203
|
const keys = Object.keys(obj);
|
|
200
|
-
if (keys.length === 1 && typeof obj
|
|
201
|
-
return get(obj
|
|
204
|
+
if (keys.length === 1 && typeof obj.$state === "string") {
|
|
205
|
+
return get(obj.$state);
|
|
202
206
|
}
|
|
203
207
|
if (keys.length === 1 && "$id" in obj) {
|
|
204
208
|
return generateUniqueId();
|
|
@@ -236,34 +240,34 @@ function ActionProvider({
|
|
|
236
240
|
async (binding) => {
|
|
237
241
|
const resolved = (0, import_core3.resolveAction)(binding, state);
|
|
238
242
|
if (resolved.action === "setState" && resolved.params) {
|
|
239
|
-
const
|
|
243
|
+
const statePath = resolved.params.statePath;
|
|
240
244
|
const value2 = resolved.params.value;
|
|
241
|
-
if (
|
|
242
|
-
set(
|
|
245
|
+
if (statePath) {
|
|
246
|
+
set(statePath, value2);
|
|
243
247
|
}
|
|
244
248
|
return;
|
|
245
249
|
}
|
|
246
250
|
if (resolved.action === "pushState" && resolved.params) {
|
|
247
|
-
const
|
|
251
|
+
const statePath = resolved.params.statePath;
|
|
248
252
|
const rawValue = resolved.params.value;
|
|
249
|
-
if (
|
|
253
|
+
if (statePath) {
|
|
250
254
|
const resolvedValue = deepResolveValue(rawValue, get);
|
|
251
|
-
const arr = get(
|
|
252
|
-
set(
|
|
253
|
-
const
|
|
254
|
-
if (
|
|
255
|
-
set(
|
|
255
|
+
const arr = get(statePath) ?? [];
|
|
256
|
+
set(statePath, [...arr, resolvedValue]);
|
|
257
|
+
const clearStatePath = resolved.params.clearStatePath;
|
|
258
|
+
if (clearStatePath) {
|
|
259
|
+
set(clearStatePath, "");
|
|
256
260
|
}
|
|
257
261
|
}
|
|
258
262
|
return;
|
|
259
263
|
}
|
|
260
264
|
if (resolved.action === "removeState" && resolved.params) {
|
|
261
|
-
const
|
|
265
|
+
const statePath = resolved.params.statePath;
|
|
262
266
|
const index = resolved.params.index;
|
|
263
|
-
if (
|
|
264
|
-
const arr = get(
|
|
267
|
+
if (statePath !== void 0 && index !== void 0) {
|
|
268
|
+
const arr = get(statePath) ?? [];
|
|
265
269
|
set(
|
|
266
|
-
|
|
270
|
+
statePath,
|
|
267
271
|
arr.filter((_, i) => i !== index)
|
|
268
272
|
);
|
|
269
273
|
}
|
|
@@ -492,27 +496,75 @@ var import_react4 = __toESM(require("react"));
|
|
|
492
496
|
var import_core4 = require("@json-render/core");
|
|
493
497
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
494
498
|
var ValidationContext = (0, import_react4.createContext)(null);
|
|
499
|
+
function dynamicArgsEqual(a, b) {
|
|
500
|
+
if (a === b) return true;
|
|
501
|
+
if (!a || !b) return false;
|
|
502
|
+
const keysA = Object.keys(a);
|
|
503
|
+
const keysB = Object.keys(b);
|
|
504
|
+
if (keysA.length !== keysB.length) return false;
|
|
505
|
+
for (const key of keysA) {
|
|
506
|
+
const va = a[key];
|
|
507
|
+
const vb = b[key];
|
|
508
|
+
if (va === vb) continue;
|
|
509
|
+
if (typeof va === "object" && va !== null && typeof vb === "object" && vb !== null) {
|
|
510
|
+
const sa = va.$state;
|
|
511
|
+
const sb = vb.$state;
|
|
512
|
+
if (typeof sa === "string" && sa === sb) continue;
|
|
513
|
+
}
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
function validationConfigEqual(a, b) {
|
|
519
|
+
if (a === b) return true;
|
|
520
|
+
if (a.validateOn !== b.validateOn) return false;
|
|
521
|
+
const ac = a.checks ?? [];
|
|
522
|
+
const bc = b.checks ?? [];
|
|
523
|
+
if (ac.length !== bc.length) return false;
|
|
524
|
+
for (let i = 0; i < ac.length; i++) {
|
|
525
|
+
const ca = ac[i];
|
|
526
|
+
const cb = bc[i];
|
|
527
|
+
if (ca.type !== cb.type) return false;
|
|
528
|
+
if (ca.message !== cb.message) return false;
|
|
529
|
+
if (!dynamicArgsEqual(ca.args, cb.args)) return false;
|
|
530
|
+
}
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
495
533
|
function ValidationProvider({
|
|
496
534
|
customFunctions = {},
|
|
497
535
|
children
|
|
498
536
|
}) {
|
|
499
|
-
const { state
|
|
537
|
+
const { state } = useStateStore();
|
|
500
538
|
const [fieldStates, setFieldStates] = (0, import_react4.useState)({});
|
|
501
539
|
const [fieldConfigs, setFieldConfigs] = (0, import_react4.useState)({});
|
|
502
540
|
const registerField = (0, import_react4.useCallback)(
|
|
503
541
|
(path, config) => {
|
|
504
|
-
setFieldConfigs((prev) =>
|
|
542
|
+
setFieldConfigs((prev) => {
|
|
543
|
+
const existing = prev[path];
|
|
544
|
+
if (existing && validationConfigEqual(existing, config)) {
|
|
545
|
+
return prev;
|
|
546
|
+
}
|
|
547
|
+
return { ...prev, [path]: config };
|
|
548
|
+
});
|
|
505
549
|
},
|
|
506
550
|
[]
|
|
507
551
|
);
|
|
508
552
|
const validate = (0, import_react4.useCallback)(
|
|
509
553
|
(path, config) => {
|
|
510
|
-
const
|
|
554
|
+
const segments = path.split("/").filter(Boolean);
|
|
555
|
+
let value2 = state;
|
|
556
|
+
for (const seg of segments) {
|
|
557
|
+
if (value2 != null && typeof value2 === "object") {
|
|
558
|
+
value2 = value2[seg];
|
|
559
|
+
} else {
|
|
560
|
+
value2 = void 0;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
511
564
|
const result = (0, import_core4.runValidation)(config, {
|
|
512
565
|
value: value2,
|
|
513
566
|
stateModel: state,
|
|
514
|
-
customFunctions
|
|
515
|
-
authState
|
|
567
|
+
customFunctions
|
|
516
568
|
});
|
|
517
569
|
setFieldStates((prev) => ({
|
|
518
570
|
...prev,
|
|
@@ -524,7 +576,7 @@ function ValidationProvider({
|
|
|
524
576
|
}));
|
|
525
577
|
return result;
|
|
526
578
|
},
|
|
527
|
-
[state, customFunctions
|
|
579
|
+
[state, customFunctions]
|
|
528
580
|
);
|
|
529
581
|
const touch = (0, import_react4.useCallback)((path) => {
|
|
530
582
|
setFieldStates((prev) => ({
|
|
@@ -621,44 +673,16 @@ var import_react5 = require("react");
|
|
|
621
673
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
622
674
|
var RepeatScopeContext = (0, import_react5.createContext)(null);
|
|
623
675
|
function RepeatScopeProvider({
|
|
624
|
-
|
|
676
|
+
item,
|
|
625
677
|
index,
|
|
678
|
+
basePath,
|
|
626
679
|
children
|
|
627
680
|
}) {
|
|
628
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RepeatScopeContext.Provider, { value: {
|
|
681
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RepeatScopeContext.Provider, { value: { item, index, basePath }, children });
|
|
629
682
|
}
|
|
630
683
|
function useRepeatScope() {
|
|
631
684
|
return (0, import_react5.useContext)(RepeatScopeContext);
|
|
632
685
|
}
|
|
633
|
-
function rewriteRepeatTokens(value, basePath, index) {
|
|
634
|
-
if (typeof value === "string") {
|
|
635
|
-
if (value === "$index") return index;
|
|
636
|
-
if (value === "$item") return basePath;
|
|
637
|
-
if (value.startsWith("$item/"))
|
|
638
|
-
return basePath + value.slice("$item".length);
|
|
639
|
-
return value;
|
|
640
|
-
}
|
|
641
|
-
if (Array.isArray(value)) {
|
|
642
|
-
let changed = false;
|
|
643
|
-
const result = value.map((item) => {
|
|
644
|
-
const rewritten = rewriteRepeatTokens(item, basePath, index);
|
|
645
|
-
if (rewritten !== item) changed = true;
|
|
646
|
-
return rewritten;
|
|
647
|
-
});
|
|
648
|
-
return changed ? result : value;
|
|
649
|
-
}
|
|
650
|
-
if (typeof value === "object" && value !== null) {
|
|
651
|
-
let changed = false;
|
|
652
|
-
const result = {};
|
|
653
|
-
for (const [key, val] of Object.entries(value)) {
|
|
654
|
-
const rewritten = rewriteRepeatTokens(val, basePath, index);
|
|
655
|
-
if (rewritten !== val) changed = true;
|
|
656
|
-
result[key] = rewritten;
|
|
657
|
-
}
|
|
658
|
-
return changed ? result : value;
|
|
659
|
-
}
|
|
660
|
-
return value;
|
|
661
|
-
}
|
|
662
686
|
|
|
663
687
|
// src/schema.ts
|
|
664
688
|
var import_core5 = require("@json-render/core");
|
|
@@ -712,177 +736,555 @@ var schema = (0, import_core5.defineSchema)(
|
|
|
712
736
|
// Element integrity
|
|
713
737
|
"CRITICAL INTEGRITY CHECK: Before outputting ANY element that references children, you MUST have already output (or will output) each child as its own element. If an element has children: ['a', 'b'], then elements 'a' and 'b' MUST exist. A missing child element causes that entire branch of the UI to be invisible.",
|
|
714
738
|
"SELF-CHECK: After generating all elements, mentally walk the tree from root. Every key in every children array must resolve to a defined element. If you find a gap, output the missing element immediately.",
|
|
715
|
-
'When building repeating content backed by a state array (e.g. todos, posts, cart items), use the "repeat" field on a container element from the AVAILABLE COMPONENTS list. Example: { "type": "<ContainerComponent>", "props": { "gap": 8 }, "repeat": { "
|
|
739
|
+
'When building repeating content backed by a state array (e.g. todos, posts, cart items), use the "repeat" field on a container element from the AVAILABLE COMPONENTS list. Example: { "type": "<ContainerComponent>", "props": { "gap": 8 }, "repeat": { "statePath": "/todos", "key": "id" }, "children": ["todo-item"] }. Inside repeated children, use { "$item": "field" } to read a field from the current item, and { "$index": true } for the current array index. For two-way binding to an item field use { "$bindItem": "completed" }. Do NOT hardcode individual elements for each array item.',
|
|
716
740
|
// Visible field placement
|
|
717
|
-
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"
|
|
741
|
+
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"<ComponentName>","props":{},"visible":{"$state":"/activeTab","eq":"home"},"children":[...]}. WRONG: {"type":"<ComponentName>","props":{},"visible":{...},"children":[...]} with visible inside props.',
|
|
718
742
|
// Tab navigation pattern
|
|
719
743
|
"TAB NAVIGATION PATTERN: When building a UI with multiple tabs, use a pressable/tappable component + setState action + visible conditions to make tabs functional. ONLY use components from the AVAILABLE COMPONENTS list.",
|
|
720
|
-
'Each tab button should be a pressable component wrapping its icon/label children, with action "setState" and actionParams { "
|
|
721
|
-
`Each tab's content section should have a visible condition: { "
|
|
744
|
+
'Each tab button should be a pressable component wrapping its icon/label children, with action "setState" and actionParams { "statePath": "/activeTab", "value": "tabName" }.',
|
|
745
|
+
`Each tab's content section should have a visible condition: { "$state": "/activeTab", "eq": "tabName" }.`,
|
|
722
746
|
"The first tab's content should NOT have a visible condition (so it shows by default when no tab is selected yet). All other tabs MUST have a visible condition.",
|
|
723
747
|
// Tab active state highlighting (using dynamic props)
|
|
724
748
|
"TAB ACTIVE STYLING: Use $cond dynamic props on icon elements inside each tab button so a single icon changes appearance based on the active tab.",
|
|
725
|
-
' - For the icon name: { "$cond": { "
|
|
726
|
-
' - For the icon color: { "$cond": { "
|
|
749
|
+
' - For the icon name: { "$cond": { "$state": "/activeTab", "eq": "thisTabName" }, "$then": "home", "$else": "home-outline" }',
|
|
750
|
+
' - For the icon color: { "$cond": { "$state": "/activeTab", "eq": "thisTabName" }, "$then": "#007AFF", "$else": "#8E8E93" }',
|
|
727
751
|
" - For labels, use $cond on the color prop similarly.",
|
|
728
|
-
' - For the FIRST/DEFAULT tab, use { "$cond":
|
|
752
|
+
' - For the FIRST/DEFAULT tab, use { "$cond": [{ "$state": "/activeTab", "eq": "thisTabName" }], "$then": "#007AFF", "$else": "#8E8E93" } so it appears active before any tab is tapped. When no tab is selected yet, include a default tab with no visible condition.',
|
|
729
753
|
// Push/pop screen navigation (all screens in one spec)
|
|
730
754
|
'SCREEN NAVIGATION: Use a pressable component with action "push" and actionParams { "screen": "screenName" } to navigate to a new screen. Use action "pop" to go back. All screens must be defined in the SAME spec. ONLY use components from the AVAILABLE COMPONENTS list.',
|
|
731
|
-
'Each screen section uses a visible condition on /currentScreen: { "
|
|
755
|
+
'Each screen section uses a visible condition on /currentScreen: { "$state": "/currentScreen", "eq": "screenName" }. The default/home screen should show by default (no visible condition) and all other screens should have the appropriate visible condition.',
|
|
732
756
|
"push automatically maintains a /navStack in the state model so pop always returns to the previous screen.",
|
|
733
757
|
'Include a back button on pushed screens using action "pop". Example: pressable(action:"pop") > row layout > back icon + back label. ONLY use components from the AVAILABLE COMPONENTS list.',
|
|
734
758
|
"Use push/pop for drill-down flows: tapping a list item to see details, opening a profile, etc. Use setState + visible conditions for tab switching within a screen.",
|
|
735
|
-
'Example: A list screen with items that push to detail: a pressable component with action:"push" and actionParams:{screen:"detail"} wrapping each list item. The detail screen section has visible:{"
|
|
759
|
+
'Example: A list screen with items that push to detail: a pressable component with action:"push" and actionParams:{screen:"detail"} wrapping each list item. The detail screen section has visible:{"$state":"/currentScreen","eq":"detail"} and contains a back button with action:"pop". ONLY use components from the AVAILABLE COMPONENTS list.'
|
|
736
760
|
]
|
|
737
761
|
}
|
|
738
762
|
);
|
|
739
763
|
var elementTreeSchema = schema;
|
|
740
764
|
|
|
741
765
|
// src/renderer.tsx
|
|
742
|
-
var
|
|
743
|
-
var
|
|
766
|
+
var import_react7 = __toESM(require("react"));
|
|
767
|
+
var import_core7 = require("@json-render/core");
|
|
744
768
|
|
|
745
769
|
// src/components/standard.tsx
|
|
746
770
|
var import_react_native2 = require("react-native");
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
backgroundColor: p.backgroundColor ?? void 0,
|
|
759
|
-
borderRadius: p.borderRadius ?? void 0,
|
|
760
|
-
flex: p.flex ?? void 0
|
|
761
|
-
},
|
|
762
|
-
children
|
|
763
|
-
}
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
function RowComponent({ element, children }) {
|
|
767
|
-
const p = element.props;
|
|
768
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
769
|
-
import_react_native2.View,
|
|
770
|
-
{
|
|
771
|
-
style: {
|
|
772
|
-
flexDirection: "row",
|
|
773
|
-
gap: p.gap ?? void 0,
|
|
774
|
-
alignItems: p.alignItems ?? void 0,
|
|
775
|
-
justifyContent: p.justifyContent ?? void 0,
|
|
776
|
-
flexWrap: p.flexWrap ?? void 0,
|
|
777
|
-
padding: p.padding ?? void 0,
|
|
778
|
-
flex: p.flex ?? void 0
|
|
779
|
-
},
|
|
780
|
-
children
|
|
781
|
-
}
|
|
771
|
+
|
|
772
|
+
// src/hooks.ts
|
|
773
|
+
var import_react6 = require("react");
|
|
774
|
+
var import_core6 = require("@json-render/core");
|
|
775
|
+
function useBoundProp(propValue, bindingPath) {
|
|
776
|
+
const { set } = useStateStore();
|
|
777
|
+
const setValue = (0, import_react6.useCallback)(
|
|
778
|
+
(value) => {
|
|
779
|
+
if (bindingPath) set(bindingPath, value);
|
|
780
|
+
},
|
|
781
|
+
[bindingPath, set]
|
|
782
782
|
);
|
|
783
|
+
return [propValue, setValue];
|
|
783
784
|
}
|
|
784
|
-
function
|
|
785
|
-
|
|
786
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
787
|
-
import_react_native2.View,
|
|
788
|
-
{
|
|
789
|
-
style: {
|
|
790
|
-
flexDirection: "column",
|
|
791
|
-
gap: p.gap ?? void 0,
|
|
792
|
-
alignItems: p.alignItems ?? void 0,
|
|
793
|
-
justifyContent: p.justifyContent ?? void 0,
|
|
794
|
-
padding: p.padding ?? void 0,
|
|
795
|
-
flex: p.flex ?? void 0
|
|
796
|
-
},
|
|
797
|
-
children
|
|
798
|
-
}
|
|
799
|
-
);
|
|
785
|
+
function looksLikeJson(line) {
|
|
786
|
+
return line.startsWith("{") || line.startsWith("[");
|
|
800
787
|
}
|
|
801
|
-
function
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
788
|
+
function parsePatchLine(line) {
|
|
789
|
+
const trimmed = line.trim();
|
|
790
|
+
if (!trimmed || trimmed.startsWith("//")) {
|
|
791
|
+
return { patch: null, malformed: false };
|
|
792
|
+
}
|
|
793
|
+
if (!looksLikeJson(trimmed)) {
|
|
794
|
+
return { patch: null, malformed: false };
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
return { patch: JSON.parse(trimmed), malformed: false };
|
|
798
|
+
} catch {
|
|
799
|
+
}
|
|
800
|
+
let attempt = trimmed;
|
|
801
|
+
for (let i = 0; i < 3; i++) {
|
|
802
|
+
const last = attempt[attempt.length - 1];
|
|
803
|
+
if (last === "}" || last === "]") {
|
|
804
|
+
attempt = attempt.slice(0, -1);
|
|
805
|
+
try {
|
|
806
|
+
const result = JSON.parse(attempt);
|
|
807
|
+
console.warn(
|
|
808
|
+
`[json-render] Recovered malformed JSONL line by removing ${i + 1} trailing '${last}'`
|
|
809
|
+
);
|
|
810
|
+
return { patch: result, malformed: false };
|
|
811
|
+
} catch {
|
|
812
|
+
}
|
|
813
|
+
} else {
|
|
814
|
+
break;
|
|
817
815
|
}
|
|
818
|
-
|
|
816
|
+
}
|
|
817
|
+
return { patch: null, malformed: true };
|
|
819
818
|
}
|
|
820
|
-
function
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
819
|
+
function setSpecValue(newSpec, path, value) {
|
|
820
|
+
if (path === "/root") {
|
|
821
|
+
newSpec.root = value;
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (path === "/state") {
|
|
825
|
+
newSpec.state = value;
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (path.startsWith("/state/")) {
|
|
829
|
+
if (!newSpec.state) newSpec.state = {};
|
|
830
|
+
const statePath = path.slice("/state".length);
|
|
831
|
+
(0, import_core6.setByPath)(newSpec.state, statePath, value);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
if (path.startsWith("/elements/")) {
|
|
835
|
+
const pathParts = path.slice("/elements/".length).split("/");
|
|
836
|
+
const elementKey = pathParts[0];
|
|
837
|
+
if (!elementKey) return;
|
|
838
|
+
if (pathParts.length === 1) {
|
|
839
|
+
newSpec.elements[elementKey] = value;
|
|
840
|
+
} else {
|
|
841
|
+
const element = newSpec.elements[elementKey];
|
|
842
|
+
if (element) {
|
|
843
|
+
const propPath = "/" + pathParts.slice(1).join("/");
|
|
844
|
+
const newElement = { ...element };
|
|
845
|
+
(0, import_core6.setByPath)(
|
|
846
|
+
newElement,
|
|
847
|
+
propPath,
|
|
848
|
+
value
|
|
849
|
+
);
|
|
850
|
+
newSpec.elements[elementKey] = newElement;
|
|
851
|
+
}
|
|
830
852
|
}
|
|
831
|
-
|
|
853
|
+
}
|
|
832
854
|
}
|
|
833
|
-
function
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
855
|
+
function removeSpecValue(newSpec, path) {
|
|
856
|
+
if (path === "/state") {
|
|
857
|
+
newSpec.state = void 0;
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (path.startsWith("/state/") && newSpec.state) {
|
|
861
|
+
const statePath = path.slice("/state".length);
|
|
862
|
+
(0, import_core6.removeByPath)(newSpec.state, statePath);
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
if (path.startsWith("/elements/")) {
|
|
866
|
+
const pathParts = path.slice("/elements/".length).split("/");
|
|
867
|
+
const elementKey = pathParts[0];
|
|
868
|
+
if (!elementKey) return;
|
|
869
|
+
if (pathParts.length === 1) {
|
|
870
|
+
const { [elementKey]: _, ...rest } = newSpec.elements;
|
|
871
|
+
newSpec.elements = rest;
|
|
872
|
+
} else {
|
|
873
|
+
const element = newSpec.elements[elementKey];
|
|
874
|
+
if (element) {
|
|
875
|
+
const propPath = "/" + pathParts.slice(1).join("/");
|
|
876
|
+
const newElement = { ...element };
|
|
877
|
+
(0, import_core6.removeByPath)(
|
|
878
|
+
newElement,
|
|
879
|
+
propPath
|
|
880
|
+
);
|
|
881
|
+
newSpec.elements[elementKey] = newElement;
|
|
842
882
|
}
|
|
843
883
|
}
|
|
844
|
-
|
|
884
|
+
}
|
|
845
885
|
}
|
|
846
|
-
function
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
{
|
|
850
|
-
onPress: () => emit?.("press"),
|
|
851
|
-
onLongPress: () => emit?.("longPress"),
|
|
852
|
-
style: ({ pressed }) => ({
|
|
853
|
-
opacity: pressed ? 0.7 : 1
|
|
854
|
-
}),
|
|
855
|
-
children
|
|
856
|
-
}
|
|
857
|
-
);
|
|
886
|
+
function getSpecValue(spec, path) {
|
|
887
|
+
if (path === "/root") return spec.root;
|
|
888
|
+
return (0, import_core6.getByPath)(spec, path);
|
|
858
889
|
}
|
|
859
|
-
function
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
{
|
|
867
|
-
style: {
|
|
868
|
-
width: isVertical ? thickness : "100%",
|
|
869
|
-
height: isVertical ? "100%" : thickness,
|
|
870
|
-
backgroundColor: color,
|
|
871
|
-
marginVertical: isVertical ? 0 : p.margin ?? 8,
|
|
872
|
-
marginHorizontal: isVertical ? p.margin ?? 8 : 0
|
|
873
|
-
}
|
|
890
|
+
function applyPatch(spec, patch) {
|
|
891
|
+
const newSpec = { ...spec, elements: { ...spec.elements } };
|
|
892
|
+
switch (patch.op) {
|
|
893
|
+
case "add":
|
|
894
|
+
case "replace": {
|
|
895
|
+
setSpecValue(newSpec, patch.path, patch.value);
|
|
896
|
+
break;
|
|
874
897
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
898
|
+
case "remove": {
|
|
899
|
+
removeSpecValue(newSpec, patch.path);
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
case "move": {
|
|
903
|
+
if (!patch.from) break;
|
|
904
|
+
const moveValue = getSpecValue(newSpec, patch.from);
|
|
905
|
+
removeSpecValue(newSpec, patch.from);
|
|
906
|
+
setSpecValue(newSpec, patch.path, moveValue);
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
case "copy": {
|
|
910
|
+
if (!patch.from) break;
|
|
911
|
+
const copyValue = getSpecValue(newSpec, patch.from);
|
|
912
|
+
setSpecValue(newSpec, patch.path, copyValue);
|
|
913
|
+
break;
|
|
914
|
+
}
|
|
915
|
+
case "test": {
|
|
916
|
+
break;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return newSpec;
|
|
920
|
+
}
|
|
921
|
+
function useUIStream({
|
|
922
|
+
api,
|
|
923
|
+
onComplete,
|
|
924
|
+
onError,
|
|
925
|
+
fetch: fetchFn = globalThis.fetch,
|
|
926
|
+
validate: enableValidation = false,
|
|
927
|
+
maxRetries = 5
|
|
928
|
+
}) {
|
|
929
|
+
const [spec, setSpec] = (0, import_react6.useState)(null);
|
|
930
|
+
const [isStreaming, setIsStreaming] = (0, import_react6.useState)(false);
|
|
931
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
932
|
+
const [rawLines, setRawLines] = (0, import_react6.useState)([]);
|
|
933
|
+
const abortControllerRef = (0, import_react6.useRef)(null);
|
|
934
|
+
const stop = (0, import_react6.useCallback)(() => {
|
|
935
|
+
abortControllerRef.current?.abort();
|
|
936
|
+
abortControllerRef.current = null;
|
|
937
|
+
setIsStreaming(false);
|
|
938
|
+
}, []);
|
|
939
|
+
const clear = (0, import_react6.useCallback)(() => {
|
|
940
|
+
setSpec(null);
|
|
941
|
+
setError(null);
|
|
942
|
+
setRawLines([]);
|
|
943
|
+
}, []);
|
|
944
|
+
const streamRequest = (0, import_react6.useCallback)(
|
|
945
|
+
async (prompt, context, initialSpec, abortOnMalformed) => {
|
|
946
|
+
let currentSpec = initialSpec;
|
|
947
|
+
setSpec(currentSpec);
|
|
948
|
+
const malformedLines = [];
|
|
949
|
+
const response = await fetchFn(api, {
|
|
950
|
+
method: "POST",
|
|
951
|
+
headers: { "Content-Type": "application/json" },
|
|
952
|
+
body: JSON.stringify({
|
|
953
|
+
prompt,
|
|
954
|
+
context,
|
|
955
|
+
currentSpec
|
|
956
|
+
}),
|
|
957
|
+
signal: abortControllerRef.current.signal
|
|
958
|
+
});
|
|
959
|
+
if (!response.ok) {
|
|
960
|
+
let errorMessage = `HTTP error: ${response.status}`;
|
|
961
|
+
try {
|
|
962
|
+
const errorData = await response.json();
|
|
963
|
+
if (errorData.message) {
|
|
964
|
+
errorMessage = errorData.message;
|
|
965
|
+
} else if (errorData.error) {
|
|
966
|
+
errorMessage = errorData.error;
|
|
967
|
+
}
|
|
968
|
+
} catch {
|
|
969
|
+
}
|
|
970
|
+
throw new Error(errorMessage);
|
|
971
|
+
}
|
|
972
|
+
const reader = response.body?.getReader();
|
|
973
|
+
if (!reader) {
|
|
974
|
+
throw new Error("No response body");
|
|
975
|
+
}
|
|
976
|
+
const decoder = new TextDecoder();
|
|
977
|
+
let buffer = "";
|
|
978
|
+
let aborted = false;
|
|
979
|
+
while (true) {
|
|
980
|
+
const { done, value } = await reader.read();
|
|
981
|
+
if (done) {
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
const decoded = decoder.decode(value, { stream: true });
|
|
985
|
+
buffer += decoded;
|
|
986
|
+
const lines = buffer.split("\n");
|
|
987
|
+
buffer = lines.pop() ?? "";
|
|
988
|
+
const newLines = [];
|
|
989
|
+
for (const line of lines) {
|
|
990
|
+
if (line.trim()) {
|
|
991
|
+
newLines.push(line);
|
|
992
|
+
}
|
|
993
|
+
const { patch, malformed } = parsePatchLine(line);
|
|
994
|
+
if (patch) {
|
|
995
|
+
currentSpec = applyPatch(currentSpec, patch);
|
|
996
|
+
setSpec({ ...currentSpec });
|
|
997
|
+
} else if (malformed) {
|
|
998
|
+
malformedLines.push(line.trim());
|
|
999
|
+
if (abortOnMalformed) {
|
|
1000
|
+
await reader.cancel();
|
|
1001
|
+
aborted = true;
|
|
1002
|
+
break;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
if (aborted) break;
|
|
1007
|
+
if (newLines.length > 0) {
|
|
1008
|
+
setRawLines((prev) => [...prev, ...newLines]);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (!aborted && buffer.trim()) {
|
|
1012
|
+
setRawLines((prev) => [...prev, buffer]);
|
|
1013
|
+
const { patch, malformed } = parsePatchLine(buffer);
|
|
1014
|
+
if (patch) {
|
|
1015
|
+
currentSpec = applyPatch(currentSpec, patch);
|
|
1016
|
+
setSpec({ ...currentSpec });
|
|
1017
|
+
} else if (malformed) {
|
|
1018
|
+
malformedLines.push(buffer.trim());
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return { spec: currentSpec, completed: !aborted, malformedLines };
|
|
1022
|
+
},
|
|
1023
|
+
[api, fetchFn]
|
|
1024
|
+
);
|
|
1025
|
+
const send = (0, import_react6.useCallback)(
|
|
1026
|
+
async (prompt, context) => {
|
|
1027
|
+
abortControllerRef.current?.abort();
|
|
1028
|
+
abortControllerRef.current = new AbortController();
|
|
1029
|
+
setIsStreaming(true);
|
|
1030
|
+
setError(null);
|
|
1031
|
+
setRawLines([]);
|
|
1032
|
+
const previousSpec = context?.previousSpec;
|
|
1033
|
+
let currentSpec = previousSpec && previousSpec.root ? { ...previousSpec, elements: { ...previousSpec.elements } } : { root: "", elements: {} };
|
|
1034
|
+
let retriesUsed = 0;
|
|
1035
|
+
let currentPrompt = prompt;
|
|
1036
|
+
let currentContext = context;
|
|
1037
|
+
try {
|
|
1038
|
+
while (retriesUsed <= maxRetries) {
|
|
1039
|
+
const result = await streamRequest(
|
|
1040
|
+
currentPrompt,
|
|
1041
|
+
currentContext,
|
|
1042
|
+
currentSpec,
|
|
1043
|
+
enableValidation
|
|
1044
|
+
// only abort on malformed when validation is on
|
|
1045
|
+
);
|
|
1046
|
+
currentSpec = result.spec;
|
|
1047
|
+
if (!result.completed && result.malformedLines.length > 0) {
|
|
1048
|
+
if (retriesUsed >= maxRetries) {
|
|
1049
|
+
break;
|
|
1050
|
+
}
|
|
1051
|
+
retriesUsed++;
|
|
1052
|
+
currentContext = { ...context, previousSpec: currentSpec };
|
|
1053
|
+
currentPrompt = `The previous generation contained malformed JSON that could not be parsed. The line was:
|
|
1054
|
+
${result.malformedLines[result.malformedLines.length - 1]?.slice(0, 500)}
|
|
1055
|
+
|
|
1056
|
+
The current spec state is provided. Continue generating from where you left off. Output ONLY the remaining patches needed to complete the UI.`;
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (!enableValidation || !currentSpec.root) {
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
const { spec: fixedSpec, fixes } = (0, import_core6.autoFixSpec)(currentSpec);
|
|
1063
|
+
if (fixes.length > 0) {
|
|
1064
|
+
currentSpec = fixedSpec;
|
|
1065
|
+
setSpec({ ...currentSpec });
|
|
1066
|
+
}
|
|
1067
|
+
const validation = (0, import_core6.validateSpec)(currentSpec);
|
|
1068
|
+
if (validation.valid) {
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
const errors = validation.issues.filter(
|
|
1072
|
+
(i) => i.severity === "error"
|
|
1073
|
+
);
|
|
1074
|
+
if (retriesUsed >= maxRetries) {
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
retriesUsed++;
|
|
1078
|
+
const issueText = (0, import_core6.formatSpecIssues)(validation.issues);
|
|
1079
|
+
currentContext = { ...context, previousSpec: currentSpec };
|
|
1080
|
+
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.
|
|
1081
|
+
|
|
1082
|
+
` + issueText;
|
|
1083
|
+
}
|
|
1084
|
+
onComplete?.(currentSpec);
|
|
1085
|
+
} catch (err) {
|
|
1086
|
+
if (err.name === "AbortError") {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
1090
|
+
setError(error2);
|
|
1091
|
+
onError?.(error2);
|
|
1092
|
+
} finally {
|
|
1093
|
+
setIsStreaming(false);
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
[
|
|
1097
|
+
api,
|
|
1098
|
+
fetchFn,
|
|
1099
|
+
onComplete,
|
|
1100
|
+
onError,
|
|
1101
|
+
enableValidation,
|
|
1102
|
+
maxRetries,
|
|
1103
|
+
streamRequest
|
|
1104
|
+
]
|
|
1105
|
+
);
|
|
1106
|
+
(0, import_react6.useEffect)(() => {
|
|
1107
|
+
return () => {
|
|
1108
|
+
abortControllerRef.current?.abort();
|
|
1109
|
+
};
|
|
1110
|
+
}, []);
|
|
1111
|
+
return {
|
|
1112
|
+
spec,
|
|
1113
|
+
isStreaming,
|
|
1114
|
+
error,
|
|
1115
|
+
rawLines,
|
|
1116
|
+
send,
|
|
1117
|
+
stop,
|
|
1118
|
+
clear
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
function flatToTree(elements) {
|
|
1122
|
+
const elementMap = {};
|
|
1123
|
+
let root = "";
|
|
1124
|
+
for (const element of elements) {
|
|
1125
|
+
elementMap[element.key] = {
|
|
1126
|
+
type: element.type,
|
|
1127
|
+
props: element.props,
|
|
1128
|
+
children: [],
|
|
1129
|
+
visible: element.visible
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
for (const element of elements) {
|
|
1133
|
+
if (element.parentKey) {
|
|
1134
|
+
const parent = elementMap[element.parentKey];
|
|
1135
|
+
if (parent) {
|
|
1136
|
+
if (!parent.children) {
|
|
1137
|
+
parent.children = [];
|
|
1138
|
+
}
|
|
1139
|
+
parent.children.push(element.key);
|
|
1140
|
+
}
|
|
1141
|
+
} else {
|
|
1142
|
+
root = element.key;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return { root, elements: elementMap };
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/components/standard.tsx
|
|
1149
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1150
|
+
function ContainerComponent({ element, children }) {
|
|
1151
|
+
const p = element.props;
|
|
1152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1153
|
+
import_react_native2.View,
|
|
1154
|
+
{
|
|
1155
|
+
style: {
|
|
1156
|
+
padding: p.padding ?? void 0,
|
|
1157
|
+
paddingHorizontal: p.paddingHorizontal ?? void 0,
|
|
1158
|
+
paddingVertical: p.paddingVertical ?? void 0,
|
|
1159
|
+
margin: p.margin ?? void 0,
|
|
1160
|
+
backgroundColor: p.backgroundColor ?? void 0,
|
|
1161
|
+
borderRadius: p.borderRadius ?? void 0,
|
|
1162
|
+
flex: p.flex ?? void 0
|
|
1163
|
+
},
|
|
1164
|
+
children
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
function RowComponent({ element, children }) {
|
|
1169
|
+
const p = element.props;
|
|
1170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1171
|
+
import_react_native2.View,
|
|
1172
|
+
{
|
|
1173
|
+
style: {
|
|
1174
|
+
flexDirection: "row",
|
|
1175
|
+
gap: p.gap ?? void 0,
|
|
1176
|
+
alignItems: p.alignItems ?? void 0,
|
|
1177
|
+
justifyContent: p.justifyContent ?? void 0,
|
|
1178
|
+
flexWrap: p.flexWrap ?? void 0,
|
|
1179
|
+
padding: p.padding ?? void 0,
|
|
1180
|
+
flex: p.flex ?? void 0
|
|
1181
|
+
},
|
|
1182
|
+
children
|
|
1183
|
+
}
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
function ColumnComponent({ element, children }) {
|
|
1187
|
+
const p = element.props;
|
|
1188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1189
|
+
import_react_native2.View,
|
|
1190
|
+
{
|
|
1191
|
+
style: {
|
|
1192
|
+
flexDirection: "column",
|
|
1193
|
+
gap: p.gap ?? void 0,
|
|
1194
|
+
alignItems: p.alignItems ?? void 0,
|
|
1195
|
+
justifyContent: p.justifyContent ?? void 0,
|
|
1196
|
+
padding: p.padding ?? void 0,
|
|
1197
|
+
flex: p.flex ?? void 0
|
|
1198
|
+
},
|
|
1199
|
+
children
|
|
1200
|
+
}
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
function ScrollContainerComponent({ element, children }) {
|
|
1204
|
+
const p = element.props;
|
|
1205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1206
|
+
import_react_native2.ScrollView,
|
|
1207
|
+
{
|
|
1208
|
+
horizontal: p.horizontal ?? false,
|
|
1209
|
+
showsVerticalScrollIndicator: p.showsScrollIndicator ?? true,
|
|
1210
|
+
showsHorizontalScrollIndicator: p.showsScrollIndicator ?? true,
|
|
1211
|
+
contentContainerStyle: {
|
|
1212
|
+
padding: p.padding ?? void 0
|
|
1213
|
+
},
|
|
1214
|
+
style: {
|
|
1215
|
+
flex: p.flex ?? 1,
|
|
1216
|
+
backgroundColor: p.backgroundColor ?? void 0
|
|
1217
|
+
},
|
|
1218
|
+
children
|
|
1219
|
+
}
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
function SafeAreaComponent({ element, children }) {
|
|
1223
|
+
const p = element.props;
|
|
1224
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1225
|
+
import_react_native2.SafeAreaView,
|
|
1226
|
+
{
|
|
1227
|
+
style: {
|
|
1228
|
+
flex: 1,
|
|
1229
|
+
backgroundColor: p.backgroundColor ?? void 0
|
|
1230
|
+
},
|
|
1231
|
+
children
|
|
1232
|
+
}
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
function SpacerComponent({ element }) {
|
|
1236
|
+
const p = element.props;
|
|
1237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1238
|
+
import_react_native2.View,
|
|
1239
|
+
{
|
|
1240
|
+
style: {
|
|
1241
|
+
width: p.size ?? void 0,
|
|
1242
|
+
height: p.size ?? void 0,
|
|
1243
|
+
flex: p.flex ?? void 0
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
function PressableComponent({ children, emit }) {
|
|
1249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1250
|
+
import_react_native2.Pressable,
|
|
1251
|
+
{
|
|
1252
|
+
onPress: () => emit("press"),
|
|
1253
|
+
onLongPress: () => emit("longPress"),
|
|
1254
|
+
style: ({ pressed }) => ({
|
|
1255
|
+
opacity: pressed ? 0.7 : 1
|
|
1256
|
+
}),
|
|
1257
|
+
children
|
|
1258
|
+
}
|
|
1259
|
+
);
|
|
1260
|
+
}
|
|
1261
|
+
function DividerComponent({ element }) {
|
|
1262
|
+
const p = element.props;
|
|
1263
|
+
const isVertical = p.direction === "vertical";
|
|
1264
|
+
const thickness = p.thickness ?? 1;
|
|
1265
|
+
const color = p.color ?? "#e5e7eb";
|
|
1266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1267
|
+
import_react_native2.View,
|
|
1268
|
+
{
|
|
1269
|
+
style: {
|
|
1270
|
+
width: isVertical ? thickness : "100%",
|
|
1271
|
+
height: isVertical ? "100%" : thickness,
|
|
1272
|
+
backgroundColor: color,
|
|
1273
|
+
marginVertical: isVertical ? 0 : p.margin ?? 8,
|
|
1274
|
+
marginHorizontal: isVertical ? p.margin ?? 8 : 0
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
var headingSizes = {
|
|
1280
|
+
h1: 32,
|
|
1281
|
+
h2: 24,
|
|
1282
|
+
h3: 20,
|
|
1283
|
+
h4: 16
|
|
1284
|
+
};
|
|
1285
|
+
function HeadingComponent({ element }) {
|
|
1286
|
+
const p = element.props;
|
|
1287
|
+
const level = p.level ?? "h2";
|
|
886
1288
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
887
1289
|
import_react_native2.Text,
|
|
888
1290
|
{
|
|
@@ -1041,7 +1443,7 @@ function ChipComponent({ element, emit }) {
|
|
|
1041
1443
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1042
1444
|
import_react_native2.Pressable,
|
|
1043
1445
|
{
|
|
1044
|
-
onPress: () => emit
|
|
1446
|
+
onPress: () => emit("press"),
|
|
1045
1447
|
style: {
|
|
1046
1448
|
flexDirection: "row",
|
|
1047
1449
|
alignItems: "center",
|
|
@@ -1053,7 +1455,7 @@ function ChipComponent({ element, emit }) {
|
|
|
1053
1455
|
},
|
|
1054
1456
|
children: [
|
|
1055
1457
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: textColor }, children: p.label }),
|
|
1056
|
-
hasRemove && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Pressable, { onPress: () => emit
|
|
1458
|
+
hasRemove && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Pressable, { onPress: () => emit("remove"), style: { marginLeft: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: textColor, fontWeight: "700" }, children: "x" }) })
|
|
1057
1459
|
]
|
|
1058
1460
|
}
|
|
1059
1461
|
);
|
|
@@ -1086,7 +1488,7 @@ function ButtonComponent({ element, emit }) {
|
|
|
1086
1488
|
import_react_native2.Pressable,
|
|
1087
1489
|
{
|
|
1088
1490
|
disabled,
|
|
1089
|
-
onPress: () => emit
|
|
1491
|
+
onPress: () => emit("press"),
|
|
1090
1492
|
style: ({ pressed }) => ({
|
|
1091
1493
|
backgroundColor: variant.bg,
|
|
1092
1494
|
paddingHorizontal: size.paddingH,
|
|
@@ -1124,12 +1526,13 @@ function ButtonComponent({ element, emit }) {
|
|
|
1124
1526
|
}
|
|
1125
1527
|
);
|
|
1126
1528
|
}
|
|
1127
|
-
function TextInputComponent({ element }) {
|
|
1529
|
+
function TextInputComponent({ element, bindings }) {
|
|
1128
1530
|
const p = element.props;
|
|
1129
|
-
const [boundValue, setBoundValue] =
|
|
1130
|
-
p.
|
|
1531
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
1532
|
+
p.value,
|
|
1533
|
+
bindings?.value
|
|
1131
1534
|
);
|
|
1132
|
-
const displayValue =
|
|
1535
|
+
const displayValue = boundValue ?? "";
|
|
1133
1536
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: p.flex != null ? { flex: p.flex } : void 0, children: [
|
|
1134
1537
|
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: inputStyles.label, children: p.label }),
|
|
1135
1538
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -1137,7 +1540,7 @@ function TextInputComponent({ element }) {
|
|
|
1137
1540
|
{
|
|
1138
1541
|
placeholder: p.placeholder ?? void 0,
|
|
1139
1542
|
value: displayValue,
|
|
1140
|
-
onChangeText:
|
|
1543
|
+
onChangeText: bindings?.value ? setBoundValue : void 0,
|
|
1141
1544
|
secureTextEntry: p.secureTextEntry ?? false,
|
|
1142
1545
|
keyboardType: p.keyboardType ?? "default",
|
|
1143
1546
|
multiline: p.multiline ?? false,
|
|
@@ -1153,1146 +1556,713 @@ function TextInputComponent({ element }) {
|
|
|
1153
1556
|
)
|
|
1154
1557
|
] });
|
|
1155
1558
|
}
|
|
1156
|
-
function SwitchComponent({ element }) {
|
|
1559
|
+
function SwitchComponent({ element, bindings }) {
|
|
1157
1560
|
const p = element.props;
|
|
1158
|
-
const [
|
|
1159
|
-
p.
|
|
1561
|
+
const [checked, setChecked] = useBoundProp(
|
|
1562
|
+
p.checked,
|
|
1563
|
+
bindings?.checked
|
|
1160
1564
|
);
|
|
1161
|
-
const displayValue =
|
|
1565
|
+
const displayValue = checked ?? false;
|
|
1162
1566
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
|
|
1163
1567
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1164
1568
|
import_react_native2.Switch,
|
|
1165
1569
|
{
|
|
1166
1570
|
value: displayValue,
|
|
1167
|
-
onValueChange:
|
|
1571
|
+
onValueChange: bindings?.checked ? setChecked : void 0,
|
|
1168
1572
|
disabled: p.disabled ?? false
|
|
1169
1573
|
}
|
|
1170
1574
|
),
|
|
1171
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1172
|
-
] });
|
|
1173
|
-
}
|
|
1174
|
-
function CheckboxComponent({ element }) {
|
|
1175
|
-
const p = element.props;
|
|
1176
|
-
const [boundValue, setBoundValue] = useStateBinding(
|
|
1177
|
-
p.statePath ?? ""
|
|
1178
|
-
);
|
|
1179
|
-
const checked = p.statePath ? boundValue ?? false : p.checked ?? false;
|
|
1180
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1181
|
-
import_react_native2.Pressable,
|
|
1182
|
-
{
|
|
1183
|
-
disabled: p.disabled ?? false,
|
|
1184
|
-
onPress: () => {
|
|
1185
|
-
if (p.statePath) {
|
|
1186
|
-
setBoundValue(!checked);
|
|
1187
|
-
}
|
|
1188
|
-
},
|
|
1189
|
-
style: {
|
|
1190
|
-
flexDirection: "row",
|
|
1191
|
-
alignItems: "center",
|
|
1192
|
-
gap: 8,
|
|
1193
|
-
opacity: p.disabled ? 0.5 : 1
|
|
1194
|
-
},
|
|
1195
|
-
children: [
|
|
1196
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1197
|
-
import_react_native2.View,
|
|
1198
|
-
{
|
|
1199
|
-
style: {
|
|
1200
|
-
width: 20,
|
|
1201
|
-
height: 20,
|
|
1202
|
-
borderRadius: 4,
|
|
1203
|
-
borderWidth: 2,
|
|
1204
|
-
borderColor: checked ? "#3b82f6" : "#d1d5db",
|
|
1205
|
-
backgroundColor: checked ? "#3b82f6" : "transparent",
|
|
1206
|
-
alignItems: "center",
|
|
1207
|
-
justifyContent: "center"
|
|
1208
|
-
},
|
|
1209
|
-
children: checked && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { color: "#ffffff", fontSize: 12, fontWeight: "700" }, children: "\u2713" })
|
|
1210
|
-
}
|
|
1211
|
-
),
|
|
1212
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1213
|
-
]
|
|
1214
|
-
}
|
|
1215
|
-
);
|
|
1216
|
-
}
|
|
1217
|
-
function SliderComponent({ element }) {
|
|
1218
|
-
const p = element.props;
|
|
1219
|
-
const min = p.min ?? 0;
|
|
1220
|
-
const max = p.max ?? 100;
|
|
1221
|
-
const value = p.value ?? min;
|
|
1222
|
-
const progress = max > min ? (value - min) / (max - min) : 0;
|
|
1223
|
-
const trackColor = p.color ?? "#3b82f6";
|
|
1224
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { children: [
|
|
1225
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1226
|
-
import_react_native2.View,
|
|
1227
|
-
{
|
|
1228
|
-
style: {
|
|
1229
|
-
flexDirection: "row",
|
|
1230
|
-
justifyContent: "space-between",
|
|
1231
|
-
marginBottom: 4
|
|
1232
|
-
},
|
|
1233
|
-
children: [
|
|
1234
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#374151" }, children: p.label }),
|
|
1235
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280" }, children: value })
|
|
1236
|
-
]
|
|
1237
|
-
}
|
|
1238
|
-
),
|
|
1239
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1240
|
-
import_react_native2.View,
|
|
1241
|
-
{
|
|
1242
|
-
style: {
|
|
1243
|
-
height: 6,
|
|
1244
|
-
backgroundColor: "#e5e7eb",
|
|
1245
|
-
borderRadius: 3,
|
|
1246
|
-
overflow: "hidden"
|
|
1247
|
-
},
|
|
1248
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1249
|
-
import_react_native2.View,
|
|
1250
|
-
{
|
|
1251
|
-
style: {
|
|
1252
|
-
width: `${Math.min(Math.max(progress * 100, 0), 100)}%`,
|
|
1253
|
-
height: "100%",
|
|
1254
|
-
backgroundColor: trackColor,
|
|
1255
|
-
borderRadius: 3
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
)
|
|
1259
|
-
}
|
|
1260
|
-
)
|
|
1261
|
-
] });
|
|
1262
|
-
}
|
|
1263
|
-
function SearchBarComponent({ element, emit }) {
|
|
1264
|
-
const p = element.props;
|
|
1265
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1266
|
-
import_react_native2.View,
|
|
1267
|
-
{
|
|
1268
|
-
style: {
|
|
1269
|
-
flexDirection: "row",
|
|
1270
|
-
alignItems: "center",
|
|
1271
|
-
backgroundColor: "#f3f4f6",
|
|
1272
|
-
borderRadius: 8,
|
|
1273
|
-
paddingHorizontal: 12
|
|
1274
|
-
},
|
|
1275
|
-
children: [
|
|
1276
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginRight: 8 }, children: "Search" }),
|
|
1277
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1278
|
-
import_react_native2.TextInput,
|
|
1279
|
-
{
|
|
1280
|
-
placeholder: p.placeholder ?? "Search...",
|
|
1281
|
-
value: p.value ?? void 0,
|
|
1282
|
-
returnKeyType: "search",
|
|
1283
|
-
onSubmitEditing: () => emit?.("submit"),
|
|
1284
|
-
style: {
|
|
1285
|
-
flex: 1,
|
|
1286
|
-
paddingVertical: 10,
|
|
1287
|
-
fontSize: 16,
|
|
1288
|
-
color: "#111827"
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
)
|
|
1292
|
-
]
|
|
1293
|
-
}
|
|
1294
|
-
);
|
|
1295
|
-
}
|
|
1296
|
-
function SpinnerComponent({ element }) {
|
|
1297
|
-
const p = element.props;
|
|
1298
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.ActivityIndicator, { size: p.size ?? "small", color: p.color ?? "#3b82f6" });
|
|
1299
|
-
}
|
|
1300
|
-
function ProgressBarComponent({ element }) {
|
|
1301
|
-
const p = element.props;
|
|
1302
|
-
const height = p.height ?? 6;
|
|
1303
|
-
const progress = Math.min(Math.max(p.progress, 0), 1);
|
|
1304
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1305
|
-
import_react_native2.View,
|
|
1306
|
-
{
|
|
1307
|
-
style: {
|
|
1308
|
-
height,
|
|
1309
|
-
backgroundColor: p.trackColor ?? "#e5e7eb",
|
|
1310
|
-
borderRadius: height / 2,
|
|
1311
|
-
overflow: "hidden"
|
|
1312
|
-
},
|
|
1313
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1314
|
-
import_react_native2.View,
|
|
1315
|
-
{
|
|
1316
|
-
style: {
|
|
1317
|
-
width: `${progress * 100}%`,
|
|
1318
|
-
height: "100%",
|
|
1319
|
-
backgroundColor: p.color ?? "#3b82f6",
|
|
1320
|
-
borderRadius: height / 2
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
)
|
|
1324
|
-
}
|
|
1325
|
-
);
|
|
1326
|
-
}
|
|
1327
|
-
function CardComponent({ element, children }) {
|
|
1328
|
-
const p = element.props;
|
|
1329
|
-
const elevated = p.elevated !== false;
|
|
1330
|
-
const padding = p.padding ?? 16;
|
|
1331
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1332
|
-
import_react_native2.View,
|
|
1333
|
-
{
|
|
1334
|
-
style: [
|
|
1335
|
-
{
|
|
1336
|
-
backgroundColor: p.backgroundColor ?? "#ffffff",
|
|
1337
|
-
borderRadius: p.borderRadius ?? 12,
|
|
1338
|
-
padding
|
|
1339
|
-
},
|
|
1340
|
-
elevated && {
|
|
1341
|
-
shadowColor: "#000",
|
|
1342
|
-
shadowOffset: { width: 0, height: 2 },
|
|
1343
|
-
shadowOpacity: 0.1,
|
|
1344
|
-
shadowRadius: 8,
|
|
1345
|
-
elevation: 3
|
|
1346
|
-
}
|
|
1347
|
-
],
|
|
1348
|
-
children: [
|
|
1349
|
-
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1350
|
-
import_react_native2.Text,
|
|
1351
|
-
{
|
|
1352
|
-
style: {
|
|
1353
|
-
fontSize: 18,
|
|
1354
|
-
fontWeight: "600",
|
|
1355
|
-
color: "#111827",
|
|
1356
|
-
marginBottom: p.subtitle ? 2 : 12
|
|
1357
|
-
},
|
|
1358
|
-
children: p.title
|
|
1359
|
-
}
|
|
1360
|
-
),
|
|
1361
|
-
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1362
|
-
import_react_native2.Text,
|
|
1363
|
-
{
|
|
1364
|
-
style: {
|
|
1365
|
-
fontSize: 14,
|
|
1366
|
-
color: "#6b7280",
|
|
1367
|
-
marginBottom: 12
|
|
1368
|
-
},
|
|
1369
|
-
children: p.subtitle
|
|
1370
|
-
}
|
|
1371
|
-
),
|
|
1372
|
-
children
|
|
1373
|
-
]
|
|
1374
|
-
}
|
|
1375
|
-
);
|
|
1575
|
+
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1576
|
+
] });
|
|
1376
1577
|
}
|
|
1377
|
-
function
|
|
1578
|
+
function CheckboxComponent({ element, bindings }) {
|
|
1378
1579
|
const p = element.props;
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
|
|
1580
|
+
const [checked, setChecked] = useBoundProp(
|
|
1581
|
+
p.checked,
|
|
1582
|
+
bindings?.checked
|
|
1583
|
+
);
|
|
1584
|
+
const isChecked = checked ?? false;
|
|
1585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1586
|
+
import_react_native2.Pressable,
|
|
1382
1587
|
{
|
|
1588
|
+
disabled: p.disabled ?? false,
|
|
1589
|
+
onPress: () => {
|
|
1590
|
+
if (bindings?.checked) {
|
|
1591
|
+
setChecked(!isChecked);
|
|
1592
|
+
}
|
|
1593
|
+
},
|
|
1383
1594
|
style: {
|
|
1384
1595
|
flexDirection: "row",
|
|
1385
1596
|
alignItems: "center",
|
|
1386
|
-
|
|
1387
|
-
|
|
1597
|
+
gap: 8,
|
|
1598
|
+
opacity: p.disabled ? 0.5 : 1
|
|
1388
1599
|
},
|
|
1389
1600
|
children: [
|
|
1390
|
-
|
|
1391
|
-
import_react_native2.
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1602
|
+
import_react_native2.View,
|
|
1392
1603
|
{
|
|
1393
1604
|
style: {
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1605
|
+
width: 20,
|
|
1606
|
+
height: 20,
|
|
1607
|
+
borderRadius: 4,
|
|
1608
|
+
borderWidth: 2,
|
|
1609
|
+
borderColor: isChecked ? "#3b82f6" : "#d1d5db",
|
|
1610
|
+
backgroundColor: isChecked ? "#3b82f6" : "transparent",
|
|
1611
|
+
alignItems: "center",
|
|
1612
|
+
justifyContent: "center"
|
|
1399
1613
|
},
|
|
1400
|
-
children:
|
|
1401
|
-
}
|
|
1402
|
-
),
|
|
1403
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1404
|
-
|
|
1405
|
-
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginTop: 2 }, children: p.subtitle })
|
|
1406
|
-
] }),
|
|
1407
|
-
p.trailing && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginLeft: 8 }, children: p.trailing }),
|
|
1408
|
-
p.showChevron && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginLeft: 8 }, children: ">" })
|
|
1409
|
-
]
|
|
1410
|
-
}
|
|
1411
|
-
);
|
|
1412
|
-
if (hasPress) {
|
|
1413
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1414
|
-
import_react_native2.Pressable,
|
|
1415
|
-
{
|
|
1416
|
-
onPress: () => emit?.("press"),
|
|
1417
|
-
style: ({ pressed }) => ({
|
|
1418
|
-
opacity: pressed ? 0.7 : 1,
|
|
1419
|
-
backgroundColor: pressed ? "#f9fafb" : "transparent"
|
|
1420
|
-
}),
|
|
1421
|
-
children: content
|
|
1422
|
-
}
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
return content;
|
|
1426
|
-
}
|
|
1427
|
-
function ModalComponent({ element, children }) {
|
|
1428
|
-
const p = element.props;
|
|
1429
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1430
|
-
import_react_native2.Modal,
|
|
1431
|
-
{
|
|
1432
|
-
visible: p.visible,
|
|
1433
|
-
animationType: p.animationType ?? "slide",
|
|
1434
|
-
transparent: true,
|
|
1435
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.View, { style: modalStyles.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: modalStyles.content, children: [
|
|
1436
|
-
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: modalStyles.title, children: p.title }),
|
|
1437
|
-
children
|
|
1438
|
-
] }) })
|
|
1439
|
-
}
|
|
1440
|
-
);
|
|
1441
|
-
}
|
|
1442
|
-
function createStandardActionHandlers(options) {
|
|
1443
|
-
return {
|
|
1444
|
-
navigate: async (params) => {
|
|
1445
|
-
const screen = params.screen;
|
|
1446
|
-
const navParams = params.params;
|
|
1447
|
-
options?.navigate?.(screen, navParams ?? void 0);
|
|
1448
|
-
},
|
|
1449
|
-
goBack: async () => {
|
|
1450
|
-
options?.goBack?.();
|
|
1451
|
-
},
|
|
1452
|
-
showAlert: async (params) => {
|
|
1453
|
-
const title = params.title;
|
|
1454
|
-
const message = params.message ?? void 0;
|
|
1455
|
-
const buttons = params.buttons;
|
|
1456
|
-
const alertButtons = buttons?.map((btn) => ({
|
|
1457
|
-
text: btn.text,
|
|
1458
|
-
style: btn.style
|
|
1459
|
-
}));
|
|
1460
|
-
import_react_native2.Alert.alert(title, message, alertButtons);
|
|
1461
|
-
},
|
|
1462
|
-
share: async (params) => {
|
|
1463
|
-
const message = params.message;
|
|
1464
|
-
const url = params.url ?? void 0;
|
|
1465
|
-
const title = params.title ?? void 0;
|
|
1466
|
-
await import_react_native2.Share.share({ message, url, title });
|
|
1467
|
-
},
|
|
1468
|
-
openURL: async (params) => {
|
|
1469
|
-
const url = params.url;
|
|
1470
|
-
const canOpen = await import_react_native2.Linking.canOpenURL(url);
|
|
1471
|
-
if (canOpen) {
|
|
1472
|
-
await import_react_native2.Linking.openURL(url);
|
|
1473
|
-
}
|
|
1474
|
-
},
|
|
1475
|
-
setData: async (params) => {
|
|
1476
|
-
const path = params.path;
|
|
1477
|
-
const value = params.value;
|
|
1478
|
-
options?.onSetData?.(path, value);
|
|
1479
|
-
},
|
|
1480
|
-
refresh: async (params) => {
|
|
1481
|
-
const target = params.target ?? void 0;
|
|
1482
|
-
options?.onRefresh?.(target);
|
|
1483
|
-
}
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
var standardComponents = {
|
|
1487
|
-
// Layout
|
|
1488
|
-
Container: ContainerComponent,
|
|
1489
|
-
Row: RowComponent,
|
|
1490
|
-
Column: ColumnComponent,
|
|
1491
|
-
ScrollContainer: ScrollContainerComponent,
|
|
1492
|
-
SafeArea: SafeAreaComponent,
|
|
1493
|
-
Pressable: PressableComponent,
|
|
1494
|
-
Spacer: SpacerComponent,
|
|
1495
|
-
Divider: DividerComponent,
|
|
1496
|
-
// Content
|
|
1497
|
-
Heading: HeadingComponent,
|
|
1498
|
-
Paragraph: ParagraphComponent,
|
|
1499
|
-
Label: LabelComponent,
|
|
1500
|
-
Image: ImageComponent,
|
|
1501
|
-
Avatar: AvatarComponent,
|
|
1502
|
-
Badge: BadgeComponent,
|
|
1503
|
-
Chip: ChipComponent,
|
|
1504
|
-
// Input
|
|
1505
|
-
Button: ButtonComponent,
|
|
1506
|
-
TextInput: TextInputComponent,
|
|
1507
|
-
Switch: SwitchComponent,
|
|
1508
|
-
Checkbox: CheckboxComponent,
|
|
1509
|
-
Slider: SliderComponent,
|
|
1510
|
-
SearchBar: SearchBarComponent,
|
|
1511
|
-
// Feedback
|
|
1512
|
-
Spinner: SpinnerComponent,
|
|
1513
|
-
ProgressBar: ProgressBarComponent,
|
|
1514
|
-
// Composite
|
|
1515
|
-
Card: CardComponent,
|
|
1516
|
-
ListItem: ListItemComponent,
|
|
1517
|
-
Modal: ModalComponent
|
|
1518
|
-
};
|
|
1519
|
-
var inputStyles = import_react_native2.StyleSheet.create({
|
|
1520
|
-
label: {
|
|
1521
|
-
fontSize: 14,
|
|
1522
|
-
fontWeight: "500",
|
|
1523
|
-
color: "#374151",
|
|
1524
|
-
marginBottom: 4
|
|
1525
|
-
},
|
|
1526
|
-
input: {
|
|
1527
|
-
borderWidth: 1,
|
|
1528
|
-
borderColor: "#d1d5db",
|
|
1529
|
-
borderRadius: 8,
|
|
1530
|
-
paddingHorizontal: 12,
|
|
1531
|
-
paddingVertical: 10,
|
|
1532
|
-
fontSize: 16,
|
|
1533
|
-
color: "#111827",
|
|
1534
|
-
backgroundColor: "#ffffff"
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
var modalStyles = import_react_native2.StyleSheet.create({
|
|
1538
|
-
overlay: {
|
|
1539
|
-
flex: 1,
|
|
1540
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
1541
|
-
justifyContent: "flex-end"
|
|
1542
|
-
},
|
|
1543
|
-
content: {
|
|
1544
|
-
backgroundColor: "#ffffff",
|
|
1545
|
-
borderTopLeftRadius: 16,
|
|
1546
|
-
borderTopRightRadius: 16,
|
|
1547
|
-
padding: 24,
|
|
1548
|
-
maxHeight: "80%"
|
|
1549
|
-
},
|
|
1550
|
-
title: {
|
|
1551
|
-
fontSize: 20,
|
|
1552
|
-
fontWeight: "600",
|
|
1553
|
-
color: "#111827",
|
|
1554
|
-
marginBottom: 16
|
|
1555
|
-
}
|
|
1556
|
-
});
|
|
1557
|
-
|
|
1558
|
-
// src/renderer.tsx
|
|
1559
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1560
|
-
var ElementErrorBoundary = class extends import_react6.default.Component {
|
|
1561
|
-
constructor(props) {
|
|
1562
|
-
super(props);
|
|
1563
|
-
this.state = { hasError: false };
|
|
1564
|
-
}
|
|
1565
|
-
static getDerivedStateFromError() {
|
|
1566
|
-
return { hasError: true };
|
|
1567
|
-
}
|
|
1568
|
-
componentDidCatch(error, info) {
|
|
1569
|
-
console.error(
|
|
1570
|
-
`[json-render] Rendering error in <${this.props.elementType}>:`,
|
|
1571
|
-
error,
|
|
1572
|
-
info.componentStack
|
|
1573
|
-
);
|
|
1574
|
-
}
|
|
1575
|
-
render() {
|
|
1576
|
-
if (this.state.hasError) {
|
|
1577
|
-
return null;
|
|
1578
|
-
}
|
|
1579
|
-
return this.props.children;
|
|
1580
|
-
}
|
|
1581
|
-
};
|
|
1582
|
-
function ElementRenderer({
|
|
1583
|
-
element,
|
|
1584
|
-
spec,
|
|
1585
|
-
registry,
|
|
1586
|
-
loading,
|
|
1587
|
-
fallback
|
|
1588
|
-
}) {
|
|
1589
|
-
const repeatScope = useRepeatScope();
|
|
1590
|
-
const { ctx } = useVisibility();
|
|
1591
|
-
const { execute } = useActions();
|
|
1592
|
-
let effectiveElement = element;
|
|
1593
|
-
if (repeatScope) {
|
|
1594
|
-
const rewrittenProps = rewriteRepeatTokens(
|
|
1595
|
-
element.props,
|
|
1596
|
-
repeatScope.basePath,
|
|
1597
|
-
repeatScope.index
|
|
1598
|
-
);
|
|
1599
|
-
const rewrittenVisible = element.visible !== void 0 ? rewriteRepeatTokens(
|
|
1600
|
-
element.visible,
|
|
1601
|
-
repeatScope.basePath,
|
|
1602
|
-
repeatScope.index
|
|
1603
|
-
) : element.visible;
|
|
1604
|
-
const rewrittenOn = element.on !== void 0 ? rewriteRepeatTokens(
|
|
1605
|
-
element.on,
|
|
1606
|
-
repeatScope.basePath,
|
|
1607
|
-
repeatScope.index
|
|
1608
|
-
) : element.on;
|
|
1609
|
-
if (rewrittenProps !== element.props || rewrittenVisible !== element.visible || rewrittenOn !== element.on) {
|
|
1610
|
-
effectiveElement = {
|
|
1611
|
-
...element,
|
|
1612
|
-
props: rewrittenProps,
|
|
1613
|
-
visible: rewrittenVisible,
|
|
1614
|
-
on: rewrittenOn
|
|
1615
|
-
};
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
const isVisible = useIsVisible(effectiveElement.visible);
|
|
1619
|
-
const onBindings = effectiveElement.on;
|
|
1620
|
-
const emit = (0, import_react6.useCallback)(
|
|
1621
|
-
(eventName) => {
|
|
1622
|
-
const binding = onBindings?.[eventName];
|
|
1623
|
-
if (!binding) return;
|
|
1624
|
-
const bindings = Array.isArray(binding) ? binding : [binding];
|
|
1625
|
-
for (const b of bindings) {
|
|
1626
|
-
execute(b);
|
|
1627
|
-
}
|
|
1628
|
-
},
|
|
1629
|
-
[onBindings, execute]
|
|
1630
|
-
);
|
|
1631
|
-
if (!isVisible) {
|
|
1632
|
-
return null;
|
|
1633
|
-
}
|
|
1634
|
-
const resolvedProps = (0, import_core6.resolveElementProps)(
|
|
1635
|
-
effectiveElement.props,
|
|
1636
|
-
ctx
|
|
1637
|
-
);
|
|
1638
|
-
const resolvedElement = resolvedProps !== effectiveElement.props ? { ...effectiveElement, props: resolvedProps } : effectiveElement;
|
|
1639
|
-
const Component = registry[resolvedElement.type] ?? fallback;
|
|
1640
|
-
if (!Component) {
|
|
1641
|
-
console.warn(`No renderer for component type: ${resolvedElement.type}`);
|
|
1642
|
-
return null;
|
|
1643
|
-
}
|
|
1644
|
-
const children = resolvedElement.repeat ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1645
|
-
RepeatChildren,
|
|
1646
|
-
{
|
|
1647
|
-
element: resolvedElement,
|
|
1648
|
-
spec,
|
|
1649
|
-
registry,
|
|
1650
|
-
loading,
|
|
1651
|
-
fallback
|
|
1652
|
-
}
|
|
1653
|
-
) : resolvedElement.children?.map((childKey) => {
|
|
1654
|
-
const childElement = spec.elements[childKey];
|
|
1655
|
-
if (!childElement) {
|
|
1656
|
-
if (!loading) {
|
|
1657
|
-
console.warn(
|
|
1658
|
-
`[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
|
|
1659
|
-
);
|
|
1660
|
-
}
|
|
1661
|
-
return null;
|
|
1614
|
+
children: isChecked && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { color: "#ffffff", fontSize: 12, fontWeight: "700" }, children: "\u2713" })
|
|
1615
|
+
}
|
|
1616
|
+
),
|
|
1617
|
+
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1618
|
+
]
|
|
1662
1619
|
}
|
|
1663
|
-
|
|
1664
|
-
ElementRenderer,
|
|
1665
|
-
{
|
|
1666
|
-
element: childElement,
|
|
1667
|
-
spec,
|
|
1668
|
-
registry,
|
|
1669
|
-
loading,
|
|
1670
|
-
fallback
|
|
1671
|
-
},
|
|
1672
|
-
childKey
|
|
1673
|
-
);
|
|
1674
|
-
});
|
|
1675
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ElementErrorBoundary, { elementType: resolvedElement.type, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Component, { element: resolvedElement, emit, loading, children }) });
|
|
1620
|
+
);
|
|
1676
1621
|
}
|
|
1677
|
-
function
|
|
1678
|
-
element
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
const items = (0, import_core6.getByPath)(state, statePath) ?? [];
|
|
1688
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: items.map((item, index) => {
|
|
1689
|
-
const key = repeat.key && typeof item === "object" && item !== null ? String(item[repeat.key] ?? index) : String(index);
|
|
1690
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1691
|
-
RepeatScopeProvider,
|
|
1622
|
+
function SliderComponent({ element }) {
|
|
1623
|
+
const p = element.props;
|
|
1624
|
+
const min = p.min ?? 0;
|
|
1625
|
+
const max = p.max ?? 100;
|
|
1626
|
+
const value = p.value ?? min;
|
|
1627
|
+
const progress = max > min ? (value - min) / (max - min) : 0;
|
|
1628
|
+
const trackColor = p.color ?? "#3b82f6";
|
|
1629
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { children: [
|
|
1630
|
+
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1631
|
+
import_react_native2.View,
|
|
1692
1632
|
{
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1633
|
+
style: {
|
|
1634
|
+
flexDirection: "row",
|
|
1635
|
+
justifyContent: "space-between",
|
|
1636
|
+
marginBottom: 4
|
|
1637
|
+
},
|
|
1638
|
+
children: [
|
|
1639
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#374151" }, children: p.label }),
|
|
1640
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280" }, children: value })
|
|
1641
|
+
]
|
|
1642
|
+
}
|
|
1643
|
+
),
|
|
1644
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1645
|
+
import_react_native2.View,
|
|
1646
|
+
{
|
|
1647
|
+
style: {
|
|
1648
|
+
height: 6,
|
|
1649
|
+
backgroundColor: "#e5e7eb",
|
|
1650
|
+
borderRadius: 3,
|
|
1651
|
+
overflow: "hidden"
|
|
1652
|
+
},
|
|
1653
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1654
|
+
import_react_native2.View,
|
|
1655
|
+
{
|
|
1656
|
+
style: {
|
|
1657
|
+
width: `${Math.min(Math.max(progress * 100, 0), 100)}%`,
|
|
1658
|
+
height: "100%",
|
|
1659
|
+
backgroundColor: trackColor,
|
|
1660
|
+
borderRadius: 3
|
|
1702
1661
|
}
|
|
1703
|
-
return null;
|
|
1704
1662
|
}
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
spec,
|
|
1710
|
-
registry,
|
|
1711
|
-
loading,
|
|
1712
|
-
fallback
|
|
1713
|
-
},
|
|
1714
|
-
childKey
|
|
1715
|
-
);
|
|
1716
|
-
})
|
|
1717
|
-
},
|
|
1718
|
-
key
|
|
1719
|
-
);
|
|
1720
|
-
}) });
|
|
1663
|
+
)
|
|
1664
|
+
}
|
|
1665
|
+
)
|
|
1666
|
+
] });
|
|
1721
1667
|
}
|
|
1722
|
-
function
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
loading,
|
|
1727
|
-
fallback
|
|
1728
|
-
}) {
|
|
1729
|
-
const registry = (0, import_react6.useMemo)(
|
|
1730
|
-
() => ({
|
|
1731
|
-
...includeStandard ? standardComponents : {},
|
|
1732
|
-
...customRegistry
|
|
1733
|
-
}),
|
|
1734
|
-
[customRegistry, includeStandard]
|
|
1735
|
-
);
|
|
1736
|
-
if (!spec || !spec.root) {
|
|
1737
|
-
return null;
|
|
1738
|
-
}
|
|
1739
|
-
const rootElement = spec.elements[spec.root];
|
|
1740
|
-
if (!rootElement) {
|
|
1741
|
-
return null;
|
|
1742
|
-
}
|
|
1743
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1744
|
-
ElementRenderer,
|
|
1668
|
+
function SearchBarComponent({ element, emit }) {
|
|
1669
|
+
const p = element.props;
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1671
|
+
import_react_native2.View,
|
|
1745
1672
|
{
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1673
|
+
style: {
|
|
1674
|
+
flexDirection: "row",
|
|
1675
|
+
alignItems: "center",
|
|
1676
|
+
backgroundColor: "#f3f4f6",
|
|
1677
|
+
borderRadius: 8,
|
|
1678
|
+
paddingHorizontal: 12
|
|
1679
|
+
},
|
|
1680
|
+
children: [
|
|
1681
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginRight: 8 }, children: "Search" }),
|
|
1682
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1683
|
+
import_react_native2.TextInput,
|
|
1684
|
+
{
|
|
1685
|
+
placeholder: p.placeholder ?? "Search...",
|
|
1686
|
+
value: p.value ?? void 0,
|
|
1687
|
+
returnKeyType: "search",
|
|
1688
|
+
onSubmitEditing: () => emit("submit"),
|
|
1689
|
+
style: {
|
|
1690
|
+
flex: 1,
|
|
1691
|
+
paddingVertical: 10,
|
|
1692
|
+
fontSize: 16,
|
|
1693
|
+
color: "#111827"
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
)
|
|
1697
|
+
]
|
|
1751
1698
|
}
|
|
1752
1699
|
);
|
|
1753
1700
|
}
|
|
1754
|
-
function
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
authState,
|
|
1758
|
-
actionHandlers,
|
|
1759
|
-
navigate,
|
|
1760
|
-
validationFunctions,
|
|
1761
|
-
onStateChange,
|
|
1762
|
-
children
|
|
1763
|
-
}) {
|
|
1764
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1765
|
-
StateProvider,
|
|
1766
|
-
{
|
|
1767
|
-
initialState,
|
|
1768
|
-
authState,
|
|
1769
|
-
onStateChange,
|
|
1770
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VisibilityProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionProvider, { handlers: actionHandlers, navigate, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(ValidationProvider, { customFunctions: validationFunctions, children: [
|
|
1771
|
-
children,
|
|
1772
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
1773
|
-
] }) }) })
|
|
1774
|
-
}
|
|
1775
|
-
);
|
|
1701
|
+
function SpinnerComponent({ element }) {
|
|
1702
|
+
const p = element.props;
|
|
1703
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.ActivityIndicator, { size: p.size ?? "small", color: p.color ?? "#3b82f6" });
|
|
1776
1704
|
}
|
|
1777
|
-
function
|
|
1778
|
-
const
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
ConfirmDialog,
|
|
1705
|
+
function ProgressBarComponent({ element }) {
|
|
1706
|
+
const p = element.props;
|
|
1707
|
+
const height = p.height ?? 6;
|
|
1708
|
+
const progress = Math.min(Math.max(p.progress, 0), 1);
|
|
1709
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1710
|
+
import_react_native2.View,
|
|
1784
1711
|
{
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1712
|
+
style: {
|
|
1713
|
+
height,
|
|
1714
|
+
backgroundColor: p.trackColor ?? "#e5e7eb",
|
|
1715
|
+
borderRadius: height / 2,
|
|
1716
|
+
overflow: "hidden"
|
|
1717
|
+
},
|
|
1718
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1719
|
+
import_react_native2.View,
|
|
1720
|
+
{
|
|
1721
|
+
style: {
|
|
1722
|
+
width: `${progress * 100}%`,
|
|
1723
|
+
height: "100%",
|
|
1724
|
+
backgroundColor: p.color ?? "#3b82f6",
|
|
1725
|
+
borderRadius: height / 2
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
)
|
|
1788
1729
|
}
|
|
1789
1730
|
);
|
|
1790
1731
|
}
|
|
1791
|
-
function
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1732
|
+
function CardComponent({ element, children }) {
|
|
1733
|
+
const p = element.props;
|
|
1734
|
+
const elevated = p.elevated !== false;
|
|
1735
|
+
const padding = p.padding ?? 16;
|
|
1736
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1737
|
+
import_react_native2.View,
|
|
1738
|
+
{
|
|
1739
|
+
style: [
|
|
1740
|
+
{
|
|
1741
|
+
backgroundColor: p.backgroundColor ?? "#ffffff",
|
|
1742
|
+
borderRadius: p.borderRadius ?? 12,
|
|
1743
|
+
padding
|
|
1744
|
+
},
|
|
1745
|
+
elevated && {
|
|
1746
|
+
shadowColor: "#000",
|
|
1747
|
+
shadowOffset: { width: 0, height: 2 },
|
|
1748
|
+
shadowOpacity: 0.1,
|
|
1749
|
+
shadowRadius: 8,
|
|
1750
|
+
elevation: 3
|
|
1751
|
+
}
|
|
1752
|
+
],
|
|
1753
|
+
children: [
|
|
1754
|
+
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1755
|
+
import_react_native2.Text,
|
|
1756
|
+
{
|
|
1757
|
+
style: {
|
|
1758
|
+
fontSize: 18,
|
|
1759
|
+
fontWeight: "600",
|
|
1760
|
+
color: "#111827",
|
|
1761
|
+
marginBottom: p.subtitle ? 2 : 12
|
|
1762
|
+
},
|
|
1763
|
+
children: p.title
|
|
1764
|
+
}
|
|
1765
|
+
),
|
|
1766
|
+
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1767
|
+
import_react_native2.Text,
|
|
1768
|
+
{
|
|
1769
|
+
style: {
|
|
1770
|
+
fontSize: 14,
|
|
1771
|
+
color: "#6b7280",
|
|
1772
|
+
marginBottom: 12
|
|
1773
|
+
},
|
|
1774
|
+
children: p.subtitle
|
|
1775
|
+
}
|
|
1776
|
+
),
|
|
1777
|
+
children
|
|
1778
|
+
]
|
|
1835
1779
|
}
|
|
1836
|
-
|
|
1837
|
-
return { registry, handlers, executeAction: executeAction2 };
|
|
1780
|
+
);
|
|
1838
1781
|
}
|
|
1839
|
-
function
|
|
1840
|
-
const
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1782
|
+
function ListItemComponent({ element, emit }) {
|
|
1783
|
+
const p = element.props;
|
|
1784
|
+
const hasPress = !!element.on?.press;
|
|
1785
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1786
|
+
import_react_native2.View,
|
|
1787
|
+
{
|
|
1788
|
+
style: {
|
|
1789
|
+
flexDirection: "row",
|
|
1790
|
+
alignItems: "center",
|
|
1791
|
+
paddingVertical: 12,
|
|
1792
|
+
paddingHorizontal: 16
|
|
1793
|
+
},
|
|
1794
|
+
children: [
|
|
1795
|
+
p.leading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1796
|
+
import_react_native2.Text,
|
|
1797
|
+
{
|
|
1798
|
+
style: {
|
|
1799
|
+
fontSize: 16,
|
|
1800
|
+
color: "#6b7280",
|
|
1801
|
+
marginRight: 12,
|
|
1802
|
+
width: 24,
|
|
1803
|
+
textAlign: "center"
|
|
1804
|
+
},
|
|
1805
|
+
children: p.leading
|
|
1806
|
+
}
|
|
1807
|
+
),
|
|
1808
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: { flex: 1 }, children: [
|
|
1809
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#111827" }, children: p.title }),
|
|
1810
|
+
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginTop: 2 }, children: p.subtitle })
|
|
1811
|
+
] }),
|
|
1812
|
+
p.trailing && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginLeft: 8 }, children: p.trailing }),
|
|
1813
|
+
p.showChevron && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginLeft: 8 }, children: ">" })
|
|
1814
|
+
]
|
|
1815
|
+
}
|
|
1816
|
+
);
|
|
1817
|
+
if (hasPress) {
|
|
1818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1819
|
+
import_react_native2.Pressable,
|
|
1859
1820
|
{
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
{
|
|
1867
|
-
spec,
|
|
1868
|
-
registry,
|
|
1869
|
-
loading,
|
|
1870
|
-
fallback
|
|
1871
|
-
}
|
|
1872
|
-
),
|
|
1873
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
1874
|
-
] }) }) })
|
|
1821
|
+
onPress: () => emit("press"),
|
|
1822
|
+
style: ({ pressed }) => ({
|
|
1823
|
+
opacity: pressed ? 0.7 : 1,
|
|
1824
|
+
backgroundColor: pressed ? "#f9fafb" : "transparent"
|
|
1825
|
+
}),
|
|
1826
|
+
children: content
|
|
1875
1827
|
}
|
|
1876
1828
|
);
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
// src/hooks.ts
|
|
1881
|
-
var import_react7 = require("react");
|
|
1882
|
-
var import_core7 = require("@json-render/core");
|
|
1883
|
-
function looksLikeJson(line) {
|
|
1884
|
-
return line.startsWith("{") || line.startsWith("[");
|
|
1885
|
-
}
|
|
1886
|
-
function parsePatchLine(line) {
|
|
1887
|
-
const trimmed = line.trim();
|
|
1888
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
1889
|
-
return { patch: null, malformed: false };
|
|
1890
|
-
}
|
|
1891
|
-
if (!looksLikeJson(trimmed)) {
|
|
1892
|
-
return { patch: null, malformed: false };
|
|
1893
|
-
}
|
|
1894
|
-
try {
|
|
1895
|
-
return { patch: JSON.parse(trimmed), malformed: false };
|
|
1896
|
-
} catch {
|
|
1897
1829
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
break;
|
|
1830
|
+
return content;
|
|
1831
|
+
}
|
|
1832
|
+
function ModalComponent({ element, children }) {
|
|
1833
|
+
const p = element.props;
|
|
1834
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1835
|
+
import_react_native2.Modal,
|
|
1836
|
+
{
|
|
1837
|
+
visible: p.visible,
|
|
1838
|
+
animationType: p.animationType ?? "slide",
|
|
1839
|
+
transparent: true,
|
|
1840
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.View, { style: modalStyles.overlay, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: modalStyles.content, children: [
|
|
1841
|
+
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: modalStyles.title, children: p.title }),
|
|
1842
|
+
children
|
|
1843
|
+
] }) })
|
|
1913
1844
|
}
|
|
1914
|
-
|
|
1915
|
-
return { patch: null, malformed: true };
|
|
1845
|
+
);
|
|
1916
1846
|
}
|
|
1917
|
-
function
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
const
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
);
|
|
1948
|
-
newSpec.elements[elementKey] = newElement;
|
|
1847
|
+
function createStandardActionHandlers(options) {
|
|
1848
|
+
return {
|
|
1849
|
+
navigate: async (params) => {
|
|
1850
|
+
const screen = params.screen;
|
|
1851
|
+
const navParams = params.params;
|
|
1852
|
+
options?.navigate?.(screen, navParams ?? void 0);
|
|
1853
|
+
},
|
|
1854
|
+
goBack: async () => {
|
|
1855
|
+
options?.goBack?.();
|
|
1856
|
+
},
|
|
1857
|
+
showAlert: async (params) => {
|
|
1858
|
+
const title = params.title;
|
|
1859
|
+
const message = params.message ?? void 0;
|
|
1860
|
+
const buttons = params.buttons;
|
|
1861
|
+
const alertButtons = buttons?.map((btn) => ({
|
|
1862
|
+
text: btn.text,
|
|
1863
|
+
style: btn.style
|
|
1864
|
+
}));
|
|
1865
|
+
import_react_native2.Alert.alert(title, message, alertButtons);
|
|
1866
|
+
},
|
|
1867
|
+
share: async (params) => {
|
|
1868
|
+
const message = params.message;
|
|
1869
|
+
const url = params.url ?? void 0;
|
|
1870
|
+
const title = params.title ?? void 0;
|
|
1871
|
+
await import_react_native2.Share.share({ message, url, title });
|
|
1872
|
+
},
|
|
1873
|
+
openURL: async (params) => {
|
|
1874
|
+
const url = params.url;
|
|
1875
|
+
const canOpen = await import_react_native2.Linking.canOpenURL(url);
|
|
1876
|
+
if (canOpen) {
|
|
1877
|
+
await import_react_native2.Linking.openURL(url);
|
|
1949
1878
|
}
|
|
1879
|
+
},
|
|
1880
|
+
setData: async (params) => {
|
|
1881
|
+
const statePath = params.statePath;
|
|
1882
|
+
const value = params.value;
|
|
1883
|
+
options?.onSetData?.(statePath, value);
|
|
1884
|
+
},
|
|
1885
|
+
refresh: async (params) => {
|
|
1886
|
+
const target = params.target ?? void 0;
|
|
1887
|
+
options?.onRefresh?.(target);
|
|
1950
1888
|
}
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
var standardComponents = {
|
|
1892
|
+
// Layout
|
|
1893
|
+
Container: ContainerComponent,
|
|
1894
|
+
Row: RowComponent,
|
|
1895
|
+
Column: ColumnComponent,
|
|
1896
|
+
ScrollContainer: ScrollContainerComponent,
|
|
1897
|
+
SafeArea: SafeAreaComponent,
|
|
1898
|
+
Pressable: PressableComponent,
|
|
1899
|
+
Spacer: SpacerComponent,
|
|
1900
|
+
Divider: DividerComponent,
|
|
1901
|
+
// Content
|
|
1902
|
+
Heading: HeadingComponent,
|
|
1903
|
+
Paragraph: ParagraphComponent,
|
|
1904
|
+
Label: LabelComponent,
|
|
1905
|
+
Image: ImageComponent,
|
|
1906
|
+
Avatar: AvatarComponent,
|
|
1907
|
+
Badge: BadgeComponent,
|
|
1908
|
+
Chip: ChipComponent,
|
|
1909
|
+
// Input
|
|
1910
|
+
Button: ButtonComponent,
|
|
1911
|
+
TextInput: TextInputComponent,
|
|
1912
|
+
Switch: SwitchComponent,
|
|
1913
|
+
Checkbox: CheckboxComponent,
|
|
1914
|
+
Slider: SliderComponent,
|
|
1915
|
+
SearchBar: SearchBarComponent,
|
|
1916
|
+
// Feedback
|
|
1917
|
+
Spinner: SpinnerComponent,
|
|
1918
|
+
ProgressBar: ProgressBarComponent,
|
|
1919
|
+
// Composite
|
|
1920
|
+
Card: CardComponent,
|
|
1921
|
+
ListItem: ListItemComponent,
|
|
1922
|
+
Modal: ModalComponent
|
|
1923
|
+
};
|
|
1924
|
+
var inputStyles = import_react_native2.StyleSheet.create({
|
|
1925
|
+
label: {
|
|
1926
|
+
fontSize: 14,
|
|
1927
|
+
fontWeight: "500",
|
|
1928
|
+
color: "#374151",
|
|
1929
|
+
marginBottom: 4
|
|
1930
|
+
},
|
|
1931
|
+
input: {
|
|
1932
|
+
borderWidth: 1,
|
|
1933
|
+
borderColor: "#d1d5db",
|
|
1934
|
+
borderRadius: 8,
|
|
1935
|
+
paddingHorizontal: 12,
|
|
1936
|
+
paddingVertical: 10,
|
|
1937
|
+
fontSize: 16,
|
|
1938
|
+
color: "#111827",
|
|
1939
|
+
backgroundColor: "#ffffff"
|
|
1951
1940
|
}
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1941
|
+
});
|
|
1942
|
+
var modalStyles = import_react_native2.StyleSheet.create({
|
|
1943
|
+
overlay: {
|
|
1944
|
+
flex: 1,
|
|
1945
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
1946
|
+
justifyContent: "flex-end"
|
|
1947
|
+
},
|
|
1948
|
+
content: {
|
|
1949
|
+
backgroundColor: "#ffffff",
|
|
1950
|
+
borderTopLeftRadius: 16,
|
|
1951
|
+
borderTopRightRadius: 16,
|
|
1952
|
+
padding: 24,
|
|
1953
|
+
maxHeight: "80%"
|
|
1954
|
+
},
|
|
1955
|
+
title: {
|
|
1956
|
+
fontSize: 20,
|
|
1957
|
+
fontWeight: "600",
|
|
1958
|
+
color: "#111827",
|
|
1959
|
+
marginBottom: 16
|
|
1957
1960
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
// src/renderer.tsx
|
|
1964
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1965
|
+
var ElementErrorBoundary = class extends import_react7.default.Component {
|
|
1966
|
+
constructor(props) {
|
|
1967
|
+
super(props);
|
|
1968
|
+
this.state = { hasError: false };
|
|
1962
1969
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
const elementKey = pathParts[0];
|
|
1966
|
-
if (!elementKey) return;
|
|
1967
|
-
if (pathParts.length === 1) {
|
|
1968
|
-
const { [elementKey]: _, ...rest } = newSpec.elements;
|
|
1969
|
-
newSpec.elements = rest;
|
|
1970
|
-
} else {
|
|
1971
|
-
const element = newSpec.elements[elementKey];
|
|
1972
|
-
if (element) {
|
|
1973
|
-
const propPath = "/" + pathParts.slice(1).join("/");
|
|
1974
|
-
const newElement = { ...element };
|
|
1975
|
-
(0, import_core7.removeByPath)(
|
|
1976
|
-
newElement,
|
|
1977
|
-
propPath
|
|
1978
|
-
);
|
|
1979
|
-
newSpec.elements[elementKey] = newElement;
|
|
1980
|
-
}
|
|
1981
|
-
}
|
|
1970
|
+
static getDerivedStateFromError() {
|
|
1971
|
+
return { hasError: true };
|
|
1982
1972
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
setSpecValue(newSpec, patch.path, patch.value);
|
|
1994
|
-
break;
|
|
1995
|
-
}
|
|
1996
|
-
case "remove": {
|
|
1997
|
-
removeSpecValue(newSpec, patch.path);
|
|
1998
|
-
break;
|
|
1999
|
-
}
|
|
2000
|
-
case "move": {
|
|
2001
|
-
if (!patch.from) break;
|
|
2002
|
-
const moveValue = getSpecValue(newSpec, patch.from);
|
|
2003
|
-
removeSpecValue(newSpec, patch.from);
|
|
2004
|
-
setSpecValue(newSpec, patch.path, moveValue);
|
|
2005
|
-
break;
|
|
2006
|
-
}
|
|
2007
|
-
case "copy": {
|
|
2008
|
-
if (!patch.from) break;
|
|
2009
|
-
const copyValue = getSpecValue(newSpec, patch.from);
|
|
2010
|
-
setSpecValue(newSpec, patch.path, copyValue);
|
|
2011
|
-
break;
|
|
2012
|
-
}
|
|
2013
|
-
case "test": {
|
|
2014
|
-
break;
|
|
1973
|
+
componentDidCatch(error, info) {
|
|
1974
|
+
console.error(
|
|
1975
|
+
`[json-render] Rendering error in <${this.props.elementType}>:`,
|
|
1976
|
+
error,
|
|
1977
|
+
info.componentStack
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
render() {
|
|
1981
|
+
if (this.state.hasError) {
|
|
1982
|
+
return null;
|
|
2015
1983
|
}
|
|
1984
|
+
return this.props.children;
|
|
2016
1985
|
}
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
validate: enableValidation = false,
|
|
2025
|
-
maxRetries = 5
|
|
1986
|
+
};
|
|
1987
|
+
var ElementRenderer = import_react7.default.memo(function ElementRenderer2({
|
|
1988
|
+
element,
|
|
1989
|
+
spec,
|
|
1990
|
+
registry,
|
|
1991
|
+
loading,
|
|
1992
|
+
fallback
|
|
2026
1993
|
}) {
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2029
|
-
const
|
|
2030
|
-
const
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
body: JSON.stringify({
|
|
2051
|
-
prompt,
|
|
2052
|
-
context,
|
|
2053
|
-
currentSpec
|
|
2054
|
-
}),
|
|
2055
|
-
signal: abortControllerRef.current.signal
|
|
2056
|
-
});
|
|
2057
|
-
console.log("[useUIStream] response status:", response.status);
|
|
2058
|
-
if (!response.ok) {
|
|
2059
|
-
let errorMessage = `HTTP error: ${response.status}`;
|
|
2060
|
-
try {
|
|
2061
|
-
const errorData = await response.json();
|
|
2062
|
-
if (errorData.message) {
|
|
2063
|
-
errorMessage = errorData.message;
|
|
2064
|
-
} else if (errorData.error) {
|
|
2065
|
-
errorMessage = errorData.error;
|
|
2066
|
-
}
|
|
2067
|
-
} catch {
|
|
2068
|
-
}
|
|
2069
|
-
throw new Error(errorMessage);
|
|
2070
|
-
}
|
|
2071
|
-
const reader = response.body?.getReader();
|
|
2072
|
-
if (!reader) {
|
|
2073
|
-
throw new Error("No response body");
|
|
2074
|
-
}
|
|
2075
|
-
const decoder = new TextDecoder();
|
|
2076
|
-
let buffer = "";
|
|
2077
|
-
let chunkCount = 0;
|
|
2078
|
-
let totalBytes = 0;
|
|
2079
|
-
let aborted = false;
|
|
2080
|
-
while (true) {
|
|
2081
|
-
const { done, value } = await reader.read();
|
|
2082
|
-
if (done) {
|
|
2083
|
-
console.log(
|
|
2084
|
-
"[useUIStream] stream done. Total chunks:",
|
|
2085
|
-
chunkCount,
|
|
2086
|
-
"bytes:",
|
|
2087
|
-
totalBytes
|
|
2088
|
-
);
|
|
2089
|
-
break;
|
|
2090
|
-
}
|
|
2091
|
-
chunkCount++;
|
|
2092
|
-
totalBytes += value?.byteLength ?? 0;
|
|
2093
|
-
const decoded = decoder.decode(value, { stream: true });
|
|
2094
|
-
buffer += decoded;
|
|
2095
|
-
if (chunkCount <= 5) {
|
|
2096
|
-
console.log(
|
|
2097
|
-
`[useUIStream] chunk #${chunkCount} (${value?.byteLength ?? 0}b):`,
|
|
2098
|
-
decoded.slice(0, 200)
|
|
2099
|
-
);
|
|
2100
|
-
}
|
|
2101
|
-
const lines = buffer.split("\n");
|
|
2102
|
-
buffer = lines.pop() ?? "";
|
|
2103
|
-
const newLines = [];
|
|
2104
|
-
for (const line of lines) {
|
|
2105
|
-
if (line.trim()) {
|
|
2106
|
-
newLines.push(line);
|
|
2107
|
-
}
|
|
2108
|
-
const { patch, malformed } = parsePatchLine(line);
|
|
2109
|
-
if (patch) {
|
|
2110
|
-
console.log("[useUIStream] applied patch:", patch.op, patch.path);
|
|
2111
|
-
currentSpec = applyPatch(currentSpec, patch);
|
|
2112
|
-
setSpec({ ...currentSpec });
|
|
2113
|
-
} else if (malformed) {
|
|
2114
|
-
malformedLines.push(line.trim());
|
|
2115
|
-
console.warn(
|
|
2116
|
-
"[useUIStream] malformed JSON line:",
|
|
2117
|
-
line.trim().slice(0, 200)
|
|
2118
|
-
);
|
|
2119
|
-
if (abortOnMalformed) {
|
|
2120
|
-
console.warn(
|
|
2121
|
-
"[useUIStream] aborting stream due to malformed JSON, will retry"
|
|
2122
|
-
);
|
|
2123
|
-
await reader.cancel();
|
|
2124
|
-
aborted = true;
|
|
2125
|
-
break;
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
if (aborted) break;
|
|
2130
|
-
if (newLines.length > 0) {
|
|
2131
|
-
setRawLines((prev) => [...prev, ...newLines]);
|
|
1994
|
+
const repeatScope = useRepeatScope();
|
|
1995
|
+
const { ctx } = useVisibility();
|
|
1996
|
+
const { execute } = useActions();
|
|
1997
|
+
const fullCtx = (0, import_react7.useMemo)(
|
|
1998
|
+
() => repeatScope ? {
|
|
1999
|
+
...ctx,
|
|
2000
|
+
repeatItem: repeatScope.item,
|
|
2001
|
+
repeatIndex: repeatScope.index,
|
|
2002
|
+
repeatBasePath: repeatScope.basePath
|
|
2003
|
+
} : ctx,
|
|
2004
|
+
[ctx, repeatScope]
|
|
2005
|
+
);
|
|
2006
|
+
const isVisible = element.visible === void 0 ? true : (0, import_core7.evaluateVisibility)(element.visible, fullCtx);
|
|
2007
|
+
const onBindings = element.on;
|
|
2008
|
+
const emit = (0, import_react7.useCallback)(
|
|
2009
|
+
(eventName) => {
|
|
2010
|
+
const binding = onBindings?.[eventName];
|
|
2011
|
+
if (!binding) return;
|
|
2012
|
+
const actionBindings = Array.isArray(binding) ? binding : [binding];
|
|
2013
|
+
for (const b of actionBindings) {
|
|
2014
|
+
if (!b.params) {
|
|
2015
|
+
execute(b);
|
|
2016
|
+
continue;
|
|
2132
2017
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
setRawLines((prev) => [...prev, buffer]);
|
|
2137
|
-
const { patch, malformed } = parsePatchLine(buffer);
|
|
2138
|
-
if (patch) {
|
|
2139
|
-
currentSpec = applyPatch(currentSpec, patch);
|
|
2140
|
-
setSpec({ ...currentSpec });
|
|
2141
|
-
} else if (malformed) {
|
|
2142
|
-
malformedLines.push(buffer.trim());
|
|
2018
|
+
const resolved = {};
|
|
2019
|
+
for (const [key, val] of Object.entries(b.params)) {
|
|
2020
|
+
resolved[key] = (0, import_core7.resolveActionParam)(val, fullCtx);
|
|
2143
2021
|
}
|
|
2022
|
+
execute({ ...b, params: resolved });
|
|
2144
2023
|
}
|
|
2145
|
-
console.log(
|
|
2146
|
-
"[useUIStream] spec state - root:",
|
|
2147
|
-
currentSpec.root,
|
|
2148
|
-
"elements:",
|
|
2149
|
-
Object.keys(currentSpec.elements).length,
|
|
2150
|
-
"completed:",
|
|
2151
|
-
!aborted,
|
|
2152
|
-
"malformed:",
|
|
2153
|
-
malformedLines.length
|
|
2154
|
-
);
|
|
2155
|
-
return { spec: currentSpec, completed: !aborted, malformedLines };
|
|
2156
2024
|
},
|
|
2157
|
-
[
|
|
2025
|
+
[onBindings, execute, fullCtx]
|
|
2158
2026
|
);
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2027
|
+
if (!isVisible) {
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
2030
|
+
const rawProps = element.props;
|
|
2031
|
+
const elementBindings = (0, import_core7.resolveBindings)(rawProps, fullCtx);
|
|
2032
|
+
const resolvedProps = (0, import_core7.resolveElementProps)(rawProps, fullCtx);
|
|
2033
|
+
const resolvedElement = resolvedProps !== element.props ? { ...element, props: resolvedProps } : element;
|
|
2034
|
+
const Component = registry[resolvedElement.type] ?? fallback;
|
|
2035
|
+
if (!Component) {
|
|
2036
|
+
console.warn(`No renderer for component type: ${resolvedElement.type}`);
|
|
2037
|
+
return null;
|
|
2038
|
+
}
|
|
2039
|
+
const children = resolvedElement.repeat ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2040
|
+
RepeatChildren,
|
|
2041
|
+
{
|
|
2042
|
+
element: resolvedElement,
|
|
2043
|
+
spec,
|
|
2044
|
+
registry,
|
|
2045
|
+
loading,
|
|
2046
|
+
fallback
|
|
2047
|
+
}
|
|
2048
|
+
) : resolvedElement.children?.map((childKey) => {
|
|
2049
|
+
const childElement = spec.elements[childKey];
|
|
2050
|
+
if (!childElement) {
|
|
2051
|
+
if (!loading) {
|
|
2052
|
+
console.warn(
|
|
2053
|
+
`[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
return null;
|
|
2057
|
+
}
|
|
2058
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2059
|
+
ElementRenderer2,
|
|
2060
|
+
{
|
|
2061
|
+
element: childElement,
|
|
2062
|
+
spec,
|
|
2063
|
+
registry,
|
|
2064
|
+
loading,
|
|
2065
|
+
fallback
|
|
2066
|
+
},
|
|
2067
|
+
childKey
|
|
2068
|
+
);
|
|
2069
|
+
});
|
|
2070
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ElementErrorBoundary, { elementType: resolvedElement.type, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2071
|
+
Component,
|
|
2072
|
+
{
|
|
2073
|
+
element: resolvedElement,
|
|
2074
|
+
emit,
|
|
2075
|
+
bindings: elementBindings,
|
|
2076
|
+
loading,
|
|
2077
|
+
children
|
|
2078
|
+
}
|
|
2079
|
+
) });
|
|
2080
|
+
});
|
|
2081
|
+
function RepeatChildren({
|
|
2082
|
+
element,
|
|
2083
|
+
spec,
|
|
2084
|
+
registry,
|
|
2085
|
+
loading,
|
|
2086
|
+
fallback
|
|
2087
|
+
}) {
|
|
2088
|
+
const { state } = useStateStore();
|
|
2089
|
+
const repeat = element.repeat;
|
|
2090
|
+
const statePath = repeat.statePath;
|
|
2091
|
+
const items = (0, import_core7.getByPath)(state, statePath) ?? [];
|
|
2092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: items.map((itemValue, index) => {
|
|
2093
|
+
const key = repeat.key && typeof itemValue === "object" && itemValue !== null ? String(
|
|
2094
|
+
itemValue[repeat.key] ?? index
|
|
2095
|
+
) : String(index);
|
|
2096
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2097
|
+
RepeatScopeProvider,
|
|
2098
|
+
{
|
|
2099
|
+
item: itemValue,
|
|
2100
|
+
index,
|
|
2101
|
+
basePath: `${statePath}/${index}`,
|
|
2102
|
+
children: element.children?.map((childKey) => {
|
|
2103
|
+
const childElement = spec.elements[childKey];
|
|
2104
|
+
if (!childElement) {
|
|
2105
|
+
if (!loading) {
|
|
2183
2106
|
console.warn(
|
|
2184
|
-
|
|
2107
|
+
`[json-render] Missing element "${childKey}" referenced as child of "${element.type}" (repeat). This element will not render.`
|
|
2185
2108
|
);
|
|
2186
|
-
break;
|
|
2187
2109
|
}
|
|
2188
|
-
|
|
2189
|
-
console.warn(
|
|
2190
|
-
`[useUIStream] mid-stream retry ${retriesUsed}/${maxRetries} due to malformed JSON`
|
|
2191
|
-
);
|
|
2192
|
-
currentContext = { ...context, previousSpec: currentSpec };
|
|
2193
|
-
currentPrompt = `The previous generation contained malformed JSON that could not be parsed. The line was:
|
|
2194
|
-
${result.malformedLines[result.malformedLines.length - 1]?.slice(0, 500)}
|
|
2195
|
-
|
|
2196
|
-
The current spec state is provided. Continue generating from where you left off. Output ONLY the remaining patches needed to complete the UI.`;
|
|
2197
|
-
continue;
|
|
2198
|
-
}
|
|
2199
|
-
if (!enableValidation || !currentSpec.root) {
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
2202
|
-
const { spec: fixedSpec, fixes } = (0, import_core7.autoFixSpec)(currentSpec);
|
|
2203
|
-
if (fixes.length > 0) {
|
|
2204
|
-
console.log("[useUIStream] auto-fixed spec issues:", fixes);
|
|
2205
|
-
currentSpec = fixedSpec;
|
|
2206
|
-
setSpec({ ...currentSpec });
|
|
2207
|
-
}
|
|
2208
|
-
const validation = (0, import_core7.validateSpec)(currentSpec);
|
|
2209
|
-
if (validation.valid) {
|
|
2210
|
-
console.log("[useUIStream] spec validation passed");
|
|
2211
|
-
break;
|
|
2212
|
-
}
|
|
2213
|
-
const errors = validation.issues.filter(
|
|
2214
|
-
(i) => i.severity === "error"
|
|
2215
|
-
);
|
|
2216
|
-
if (retriesUsed >= maxRetries) {
|
|
2217
|
-
console.warn(
|
|
2218
|
-
"[useUIStream] spec validation failed, max retries exhausted:",
|
|
2219
|
-
errors.map((e) => e.message)
|
|
2220
|
-
);
|
|
2221
|
-
break;
|
|
2110
|
+
return null;
|
|
2222
2111
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2113
|
+
ElementRenderer,
|
|
2114
|
+
{
|
|
2115
|
+
element: childElement,
|
|
2116
|
+
spec,
|
|
2117
|
+
registry,
|
|
2118
|
+
loading,
|
|
2119
|
+
fallback
|
|
2120
|
+
},
|
|
2121
|
+
childKey
|
|
2228
2122
|
);
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
}
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2123
|
+
})
|
|
2124
|
+
},
|
|
2125
|
+
key
|
|
2126
|
+
);
|
|
2127
|
+
}) });
|
|
2128
|
+
}
|
|
2129
|
+
function Renderer({
|
|
2130
|
+
spec,
|
|
2131
|
+
registry: customRegistry,
|
|
2132
|
+
includeStandard = true,
|
|
2133
|
+
loading,
|
|
2134
|
+
fallback
|
|
2135
|
+
}) {
|
|
2136
|
+
const registry = (0, import_react7.useMemo)(
|
|
2137
|
+
() => ({
|
|
2138
|
+
...includeStandard ? standardComponents : {},
|
|
2139
|
+
...customRegistry
|
|
2140
|
+
}),
|
|
2141
|
+
[customRegistry, includeStandard]
|
|
2142
|
+
);
|
|
2143
|
+
if (!spec || !spec.root) {
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
const rootElement = spec.elements[spec.root];
|
|
2147
|
+
if (!rootElement) {
|
|
2148
|
+
return null;
|
|
2149
|
+
}
|
|
2150
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2151
|
+
ElementRenderer,
|
|
2152
|
+
{
|
|
2153
|
+
element: rootElement,
|
|
2154
|
+
spec,
|
|
2155
|
+
registry,
|
|
2156
|
+
loading,
|
|
2157
|
+
fallback
|
|
2158
|
+
}
|
|
2255
2159
|
);
|
|
2256
|
-
(0, import_react7.useEffect)(() => {
|
|
2257
|
-
return () => {
|
|
2258
|
-
abortControllerRef.current?.abort();
|
|
2259
|
-
};
|
|
2260
|
-
}, []);
|
|
2261
|
-
return {
|
|
2262
|
-
spec,
|
|
2263
|
-
isStreaming,
|
|
2264
|
-
error,
|
|
2265
|
-
rawLines,
|
|
2266
|
-
send,
|
|
2267
|
-
stop,
|
|
2268
|
-
clear
|
|
2269
|
-
};
|
|
2270
2160
|
}
|
|
2271
|
-
function
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2161
|
+
function JSONUIProvider({
|
|
2162
|
+
registry,
|
|
2163
|
+
initialState,
|
|
2164
|
+
handlers,
|
|
2165
|
+
navigate,
|
|
2166
|
+
validationFunctions,
|
|
2167
|
+
onStateChange,
|
|
2168
|
+
children
|
|
2169
|
+
}) {
|
|
2170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StateProvider, { initialState, onStateChange, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VisibilityProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionProvider, { handlers, navigate, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(ValidationProvider, { customFunctions: validationFunctions, children: [
|
|
2171
|
+
children,
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
2173
|
+
] }) }) }) });
|
|
2174
|
+
}
|
|
2175
|
+
function ConfirmationDialogManager() {
|
|
2176
|
+
const { pendingConfirmation, confirm, cancel } = useActions();
|
|
2177
|
+
if (!pendingConfirmation?.action.confirm) {
|
|
2178
|
+
return null;
|
|
2281
2179
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2181
|
+
ConfirmDialog,
|
|
2182
|
+
{
|
|
2183
|
+
confirm: pendingConfirmation.action.confirm,
|
|
2184
|
+
onConfirm: confirm,
|
|
2185
|
+
onCancel: cancel
|
|
2186
|
+
}
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
function defineRegistry(_catalog, options) {
|
|
2190
|
+
const registry = {};
|
|
2191
|
+
if (options.components) {
|
|
2192
|
+
for (const [name, componentFn] of Object.entries(options.components)) {
|
|
2193
|
+
registry[name] = ({
|
|
2194
|
+
element,
|
|
2195
|
+
children,
|
|
2196
|
+
emit,
|
|
2197
|
+
bindings,
|
|
2198
|
+
loading
|
|
2199
|
+
}) => {
|
|
2200
|
+
return componentFn({
|
|
2201
|
+
props: element.props,
|
|
2202
|
+
children,
|
|
2203
|
+
emit,
|
|
2204
|
+
bindings,
|
|
2205
|
+
loading
|
|
2206
|
+
});
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
const actionMap = options.actions ? Object.entries(options.actions) : [];
|
|
2211
|
+
const handlers = (getSetState, getState) => {
|
|
2212
|
+
const result = {};
|
|
2213
|
+
for (const [name, actionFn] of actionMap) {
|
|
2214
|
+
result[name] = async (params) => {
|
|
2215
|
+
const setState = getSetState();
|
|
2216
|
+
const state = getState();
|
|
2217
|
+
if (setState) {
|
|
2218
|
+
await actionFn(params, setState, state);
|
|
2288
2219
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
return result;
|
|
2223
|
+
};
|
|
2224
|
+
const executeAction2 = async (actionName, params, setState, state = {}) => {
|
|
2225
|
+
const entry = actionMap.find(([name]) => name === actionName);
|
|
2226
|
+
if (entry) {
|
|
2227
|
+
await entry[1](params, setState, state);
|
|
2291
2228
|
} else {
|
|
2292
|
-
|
|
2229
|
+
console.warn(`Unknown action: ${actionName}`);
|
|
2293
2230
|
}
|
|
2294
|
-
}
|
|
2295
|
-
return {
|
|
2231
|
+
};
|
|
2232
|
+
return { registry, handlers, executeAction: executeAction2 };
|
|
2233
|
+
}
|
|
2234
|
+
function createRenderer(catalog, components) {
|
|
2235
|
+
const registry = components;
|
|
2236
|
+
return function CatalogRenderer({
|
|
2237
|
+
spec,
|
|
2238
|
+
state,
|
|
2239
|
+
onAction,
|
|
2240
|
+
onStateChange,
|
|
2241
|
+
loading,
|
|
2242
|
+
fallback
|
|
2243
|
+
}) {
|
|
2244
|
+
const actionHandlers = onAction ? new Proxy(
|
|
2245
|
+
{},
|
|
2246
|
+
{
|
|
2247
|
+
get: (_target, prop) => {
|
|
2248
|
+
return (params) => onAction(prop, params);
|
|
2249
|
+
},
|
|
2250
|
+
has: () => true
|
|
2251
|
+
}
|
|
2252
|
+
) : void 0;
|
|
2253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StateProvider, { initialState: state, onStateChange, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(VisibilityProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ActionProvider, { handlers: actionHandlers, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(ValidationProvider, { children: [
|
|
2254
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2255
|
+
Renderer,
|
|
2256
|
+
{
|
|
2257
|
+
spec,
|
|
2258
|
+
registry,
|
|
2259
|
+
loading,
|
|
2260
|
+
fallback
|
|
2261
|
+
}
|
|
2262
|
+
),
|
|
2263
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
2264
|
+
] }) }) }) });
|
|
2265
|
+
};
|
|
2296
2266
|
}
|
|
2297
2267
|
|
|
2298
2268
|
// src/catalog.ts
|
|
@@ -2500,7 +2470,6 @@ var standardComponentDefinitions = {
|
|
|
2500
2470
|
props: import_zod.z.object({
|
|
2501
2471
|
placeholder: import_zod.z.string().nullable(),
|
|
2502
2472
|
value: import_zod.z.string().nullable(),
|
|
2503
|
-
statePath: import_zod.z.string().nullable(),
|
|
2504
2473
|
secureTextEntry: import_zod.z.boolean().nullable(),
|
|
2505
2474
|
keyboardType: import_zod.z.enum(["default", "email-address", "numeric", "phone-pad", "url"]).nullable(),
|
|
2506
2475
|
multiline: import_zod.z.boolean().nullable(),
|
|
@@ -2510,34 +2479,31 @@ var standardComponentDefinitions = {
|
|
|
2510
2479
|
}),
|
|
2511
2480
|
events: ["submit", "focus", "blur"],
|
|
2512
2481
|
slots: [],
|
|
2513
|
-
description: "Text input field. Use
|
|
2482
|
+
description: "Text input field. Use $bindState to bind to the state model for two-way binding. The value typed by the user is stored at the bound path.",
|
|
2514
2483
|
example: {
|
|
2515
2484
|
placeholder: "Enter text...",
|
|
2516
|
-
statePath: "/inputValue",
|
|
2517
2485
|
label: "Name"
|
|
2518
2486
|
}
|
|
2519
2487
|
},
|
|
2520
2488
|
Switch: {
|
|
2521
2489
|
props: import_zod.z.object({
|
|
2522
|
-
|
|
2523
|
-
statePath: import_zod.z.string().nullable(),
|
|
2490
|
+
checked: import_zod.z.boolean().nullable(),
|
|
2524
2491
|
label: import_zod.z.string().nullable(),
|
|
2525
2492
|
disabled: import_zod.z.boolean().nullable()
|
|
2526
2493
|
}),
|
|
2527
2494
|
events: ["change"],
|
|
2528
2495
|
slots: [],
|
|
2529
|
-
description: "Toggle switch. Use
|
|
2496
|
+
description: "Toggle switch. Use $bindState to bind to the state model."
|
|
2530
2497
|
},
|
|
2531
2498
|
Checkbox: {
|
|
2532
2499
|
props: import_zod.z.object({
|
|
2533
2500
|
checked: import_zod.z.boolean().nullable(),
|
|
2534
|
-
statePath: import_zod.z.string().nullable(),
|
|
2535
2501
|
label: import_zod.z.string().nullable(),
|
|
2536
2502
|
disabled: import_zod.z.boolean().nullable()
|
|
2537
2503
|
}),
|
|
2538
2504
|
events: ["change"],
|
|
2539
2505
|
slots: [],
|
|
2540
|
-
description: "Checkbox for boolean selections. Use
|
|
2506
|
+
description: "Checkbox for boolean selections. Use $bindState to bind to the state model."
|
|
2541
2507
|
},
|
|
2542
2508
|
Slider: {
|
|
2543
2509
|
props: import_zod.z.object({
|
|
@@ -2545,18 +2511,16 @@ var standardComponentDefinitions = {
|
|
|
2545
2511
|
max: import_zod.z.number().nullable(),
|
|
2546
2512
|
step: import_zod.z.number().nullable(),
|
|
2547
2513
|
value: import_zod.z.number().nullable(),
|
|
2548
|
-
statePath: import_zod.z.string().nullable(),
|
|
2549
2514
|
label: import_zod.z.string().nullable(),
|
|
2550
2515
|
color: import_zod.z.string().nullable()
|
|
2551
2516
|
}),
|
|
2552
2517
|
slots: [],
|
|
2553
|
-
description: "Range slider for numeric values. Use
|
|
2518
|
+
description: "Range slider for numeric values. Use $bindState to bind to the state model."
|
|
2554
2519
|
},
|
|
2555
2520
|
SearchBar: {
|
|
2556
2521
|
props: import_zod.z.object({
|
|
2557
2522
|
placeholder: import_zod.z.string().nullable(),
|
|
2558
|
-
value: import_zod.z.string().nullable()
|
|
2559
|
-
statePath: import_zod.z.string().nullable()
|
|
2523
|
+
value: import_zod.z.string().nullable()
|
|
2560
2524
|
}),
|
|
2561
2525
|
events: ["submit"],
|
|
2562
2526
|
slots: [],
|
|
@@ -2620,11 +2584,10 @@ var standardComponentDefinitions = {
|
|
|
2620
2584
|
props: import_zod.z.object({
|
|
2621
2585
|
visible: import_zod.z.boolean(),
|
|
2622
2586
|
title: import_zod.z.string().nullable(),
|
|
2623
|
-
animationType: import_zod.z.enum(["slide", "fade", "none"]).nullable()
|
|
2624
|
-
statePath: import_zod.z.string().nullable()
|
|
2587
|
+
animationType: import_zod.z.enum(["slide", "fade", "none"]).nullable()
|
|
2625
2588
|
}),
|
|
2626
2589
|
slots: ["default"],
|
|
2627
|
-
description: "Modal overlay dialog. Use
|
|
2590
|
+
description: "Modal overlay dialog. Use $bindState on visible to bind visibility to the state model."
|
|
2628
2591
|
}
|
|
2629
2592
|
};
|
|
2630
2593
|
var standardActionDefinitions = {
|
|
@@ -2669,22 +2632,22 @@ var standardActionDefinitions = {
|
|
|
2669
2632
|
},
|
|
2670
2633
|
setState: {
|
|
2671
2634
|
params: import_zod.z.object({
|
|
2672
|
-
|
|
2635
|
+
statePath: import_zod.z.string(),
|
|
2673
2636
|
value: import_zod.z.unknown()
|
|
2674
2637
|
}),
|
|
2675
|
-
description: "Update a value in the state model at the given
|
|
2638
|
+
description: "Update a value in the state model at the given statePath."
|
|
2676
2639
|
},
|
|
2677
2640
|
pushState: {
|
|
2678
2641
|
params: import_zod.z.object({
|
|
2679
|
-
|
|
2642
|
+
statePath: import_zod.z.string(),
|
|
2680
2643
|
value: import_zod.z.unknown(),
|
|
2681
|
-
|
|
2644
|
+
clearStatePath: import_zod.z.string().optional()
|
|
2682
2645
|
}),
|
|
2683
|
-
description: 'Append an item to an array in the state model. The value can contain {
|
|
2646
|
+
description: 'Append an item to an array in the state model. The value can contain { $state: "/statePath" } references to read from current state, and "$id" to auto-generate a unique ID. Use clearStatePath to reset another path after pushing (e.g. clear an input field). Example: { statePath: "/todos", value: { id: "$id", title: { $state: "/newTodoText" }, completed: false }, clearStatePath: "/newTodoText" }.'
|
|
2684
2647
|
},
|
|
2685
2648
|
removeState: {
|
|
2686
2649
|
params: import_zod.z.object({
|
|
2687
|
-
|
|
2650
|
+
statePath: import_zod.z.string(),
|
|
2688
2651
|
index: import_zod.z.number()
|
|
2689
2652
|
}),
|
|
2690
2653
|
description: "Remove an item from an array in the state model at the given index."
|
|
@@ -2707,18 +2670,17 @@ var standardActionDefinitions = {
|
|
|
2707
2670
|
ValidationProvider,
|
|
2708
2671
|
VisibilityProvider,
|
|
2709
2672
|
createRenderer,
|
|
2710
|
-
createRendererFromCatalog,
|
|
2711
2673
|
createStandardActionHandlers,
|
|
2712
2674
|
defineRegistry,
|
|
2713
2675
|
elementTreeSchema,
|
|
2714
2676
|
flatToTree,
|
|
2715
|
-
rewriteRepeatTokens,
|
|
2716
2677
|
schema,
|
|
2717
2678
|
standardActionDefinitions,
|
|
2718
2679
|
standardComponentDefinitions,
|
|
2719
2680
|
standardComponents,
|
|
2720
2681
|
useAction,
|
|
2721
2682
|
useActions,
|
|
2683
|
+
useBoundProp,
|
|
2722
2684
|
useFieldValidation,
|
|
2723
2685
|
useIsVisible,
|
|
2724
2686
|
useRepeatScope,
|