@json-render/react-native 0.5.1 → 0.6.0
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 +60 -11
- package/dist/catalog.d.ts +60 -11
- package/dist/catalog.js +51 -32
- package/dist/catalog.js.map +1 -1
- package/dist/catalog.mjs +1 -1
- package/dist/{chunk-MP3Y6GEF.mjs → chunk-QYY3Q42V.mjs} +52 -33
- package/dist/chunk-QYY3Q42V.mjs.map +1 -0
- package/dist/chunk-T27PQA6P.mjs +83 -0
- 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 +1311 -1321
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1464 -1491
- package/dist/index.mjs.map +1 -1
- package/dist/schema.d.mts +4 -0
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +18 -16
- package/dist/schema.js.map +1 -1
- package/dist/schema.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-MP3Y6GEF.mjs.map +0 -1
- package/dist/chunk-VBNJFRLI.mjs +0 -81
- package/dist/chunk-VBNJFRLI.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");
|
|
@@ -691,7 +715,9 @@ var schema = (0, import_core5.defineSchema)(
|
|
|
691
715
|
/** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */
|
|
692
716
|
slots: s.array(s.string()),
|
|
693
717
|
/** Description for AI generation hints */
|
|
694
|
-
description: s.string()
|
|
718
|
+
description: s.string(),
|
|
719
|
+
/** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
|
|
720
|
+
example: s.any()
|
|
695
721
|
}),
|
|
696
722
|
/** Action definitions (optional) */
|
|
697
723
|
actions: s.map({
|
|
@@ -705,181 +731,559 @@ var schema = (0, import_core5.defineSchema)(
|
|
|
705
731
|
{
|
|
706
732
|
defaultRules: [
|
|
707
733
|
// Layout patterns
|
|
708
|
-
"FIXED BOTTOM BAR PATTERN: When building a screen with a fixed header and/or fixed bottom tab bar, the outermost
|
|
709
|
-
"NEVER place a bottom tab bar or fixed footer inside a
|
|
734
|
+
"FIXED BOTTOM BAR PATTERN: When building a screen with a fixed header and/or fixed bottom tab bar, the outermost vertical layout component must have flex:1 so it fills the screen. The scrollable content area must also have flex:1. Structure: screen wrapper > vertical layout(flex:1, gap:0) > [header, content wrapper(flex:1) > [scroll container(...)], bottom-tabs]. Both the outer layout AND the content wrapper need flex:1. ONLY use components from the AVAILABLE COMPONENTS list.",
|
|
735
|
+
"NEVER place a bottom tab bar or fixed footer inside a scroll container. It must be a sibling AFTER the flex:1 container that holds the scroll content.",
|
|
710
736
|
// Element integrity
|
|
711
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.",
|
|
712
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.",
|
|
713
|
-
'When building repeating content backed by a state array (e.g. todos, posts, cart items), use the "repeat" field on
|
|
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.',
|
|
714
740
|
// Visible field placement
|
|
715
|
-
'CRITICAL: The "visible" field goes on the ELEMENT object, NOT inside "props". Correct: {"type":"
|
|
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.',
|
|
716
742
|
// Tab navigation pattern
|
|
717
|
-
"TAB NAVIGATION PATTERN: When building a UI with multiple tabs, use
|
|
718
|
-
'Each tab button should be a
|
|
719
|
-
`Each tab's content section should have a visible condition: { "
|
|
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.",
|
|
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" }.`,
|
|
720
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.",
|
|
721
747
|
// Tab active state highlighting (using dynamic props)
|
|
722
|
-
"TAB ACTIVE STYLING: Use $cond dynamic props on
|
|
723
|
-
' - For the icon name: { "$cond": { "
|
|
724
|
-
' - For the icon color: { "$cond": { "
|
|
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.",
|
|
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" }',
|
|
725
751
|
" - For labels, use $cond on the color prop similarly.",
|
|
726
|
-
' - 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.',
|
|
727
753
|
// Push/pop screen navigation (all screens in one spec)
|
|
728
|
-
'SCREEN NAVIGATION: Use
|
|
729
|
-
'Each screen section uses a visible condition on /currentScreen: { "
|
|
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.',
|
|
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.',
|
|
730
756
|
"push automatically maintains a /navStack in the state model so pop always returns to the previous screen.",
|
|
731
|
-
'Include a back button on pushed screens using action "pop". Example:
|
|
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.',
|
|
732
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.",
|
|
733
|
-
'Example: A list screen with items that push to detail:
|
|
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.'
|
|
734
760
|
]
|
|
735
761
|
}
|
|
736
762
|
);
|
|
737
763
|
var elementTreeSchema = schema;
|
|
738
764
|
|
|
739
765
|
// src/renderer.tsx
|
|
740
|
-
var
|
|
741
|
-
var
|
|
766
|
+
var import_react7 = __toESM(require("react"));
|
|
767
|
+
var import_core7 = require("@json-render/core");
|
|
742
768
|
|
|
743
769
|
// src/components/standard.tsx
|
|
744
770
|
var import_react_native2 = require("react-native");
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
backgroundColor: p.backgroundColor ?? void 0,
|
|
757
|
-
borderRadius: p.borderRadius ?? void 0,
|
|
758
|
-
flex: p.flex ?? void 0
|
|
759
|
-
},
|
|
760
|
-
children
|
|
761
|
-
}
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
function RowComponent({ element, children }) {
|
|
765
|
-
const p = element.props;
|
|
766
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
767
|
-
import_react_native2.View,
|
|
768
|
-
{
|
|
769
|
-
style: {
|
|
770
|
-
flexDirection: "row",
|
|
771
|
-
gap: p.gap ?? void 0,
|
|
772
|
-
alignItems: p.alignItems ?? void 0,
|
|
773
|
-
justifyContent: p.justifyContent ?? void 0,
|
|
774
|
-
flexWrap: p.flexWrap ?? void 0,
|
|
775
|
-
padding: p.padding ?? void 0,
|
|
776
|
-
flex: p.flex ?? void 0
|
|
777
|
-
},
|
|
778
|
-
children
|
|
779
|
-
}
|
|
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]
|
|
780
782
|
);
|
|
783
|
+
return [propValue, setValue];
|
|
781
784
|
}
|
|
782
|
-
function
|
|
783
|
-
|
|
784
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
785
|
-
import_react_native2.View,
|
|
786
|
-
{
|
|
787
|
-
style: {
|
|
788
|
-
flexDirection: "column",
|
|
789
|
-
gap: p.gap ?? void 0,
|
|
790
|
-
alignItems: p.alignItems ?? void 0,
|
|
791
|
-
justifyContent: p.justifyContent ?? void 0,
|
|
792
|
-
padding: p.padding ?? void 0,
|
|
793
|
-
flex: p.flex ?? void 0
|
|
794
|
-
},
|
|
795
|
-
children
|
|
796
|
-
}
|
|
797
|
-
);
|
|
785
|
+
function looksLikeJson(line) {
|
|
786
|
+
return line.startsWith("{") || line.startsWith("[");
|
|
798
787
|
}
|
|
799
|
-
function
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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;
|
|
815
815
|
}
|
|
816
|
-
|
|
816
|
+
}
|
|
817
|
+
return { patch: null, malformed: true };
|
|
817
818
|
}
|
|
818
|
-
function
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
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
|
+
}
|
|
828
852
|
}
|
|
829
|
-
|
|
853
|
+
}
|
|
830
854
|
}
|
|
831
|
-
function
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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;
|
|
840
882
|
}
|
|
841
883
|
}
|
|
842
|
-
|
|
884
|
+
}
|
|
843
885
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
{
|
|
848
|
-
onPress: () => emit?.("press"),
|
|
849
|
-
onLongPress: () => emit?.("longPress"),
|
|
850
|
-
style: ({ pressed }) => ({
|
|
851
|
-
opacity: pressed ? 0.7 : 1
|
|
852
|
-
}),
|
|
853
|
-
children
|
|
854
|
-
}
|
|
855
|
-
);
|
|
886
|
+
function getSpecValue(spec, path) {
|
|
887
|
+
if (path === "/root") return spec.root;
|
|
888
|
+
return (0, import_core6.getByPath)(spec, path);
|
|
856
889
|
}
|
|
857
|
-
function
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
{
|
|
865
|
-
style: {
|
|
866
|
-
width: isVertical ? thickness : "100%",
|
|
867
|
-
height: isVertical ? "100%" : thickness,
|
|
868
|
-
backgroundColor: color,
|
|
869
|
-
marginVertical: isVertical ? 0 : p.margin ?? 8,
|
|
870
|
-
marginHorizontal: isVertical ? p.margin ?? 8 : 0
|
|
871
|
-
}
|
|
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;
|
|
872
897
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
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;
|
|
883
1287
|
const level = p.level ?? "h2";
|
|
884
1288
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
885
1289
|
import_react_native2.Text,
|
|
@@ -1039,7 +1443,7 @@ function ChipComponent({ element, emit }) {
|
|
|
1039
1443
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1040
1444
|
import_react_native2.Pressable,
|
|
1041
1445
|
{
|
|
1042
|
-
onPress: () => emit
|
|
1446
|
+
onPress: () => emit("press"),
|
|
1043
1447
|
style: {
|
|
1044
1448
|
flexDirection: "row",
|
|
1045
1449
|
alignItems: "center",
|
|
@@ -1051,7 +1455,7 @@ function ChipComponent({ element, emit }) {
|
|
|
1051
1455
|
},
|
|
1052
1456
|
children: [
|
|
1053
1457
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: textColor }, children: p.label }),
|
|
1054
|
-
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" }) })
|
|
1055
1459
|
]
|
|
1056
1460
|
}
|
|
1057
1461
|
);
|
|
@@ -1084,7 +1488,7 @@ function ButtonComponent({ element, emit }) {
|
|
|
1084
1488
|
import_react_native2.Pressable,
|
|
1085
1489
|
{
|
|
1086
1490
|
disabled,
|
|
1087
|
-
onPress: () => emit
|
|
1491
|
+
onPress: () => emit("press"),
|
|
1088
1492
|
style: ({ pressed }) => ({
|
|
1089
1493
|
backgroundColor: variant.bg,
|
|
1090
1494
|
paddingHorizontal: size.paddingH,
|
|
@@ -1122,12 +1526,13 @@ function ButtonComponent({ element, emit }) {
|
|
|
1122
1526
|
}
|
|
1123
1527
|
);
|
|
1124
1528
|
}
|
|
1125
|
-
function TextInputComponent({ element }) {
|
|
1529
|
+
function TextInputComponent({ element, bindings }) {
|
|
1126
1530
|
const p = element.props;
|
|
1127
|
-
const [boundValue, setBoundValue] =
|
|
1128
|
-
p.
|
|
1531
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
1532
|
+
p.value,
|
|
1533
|
+
bindings?.value
|
|
1129
1534
|
);
|
|
1130
|
-
const displayValue =
|
|
1535
|
+
const displayValue = boundValue ?? "";
|
|
1131
1536
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: p.flex != null ? { flex: p.flex } : void 0, children: [
|
|
1132
1537
|
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: inputStyles.label, children: p.label }),
|
|
1133
1538
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
@@ -1135,7 +1540,7 @@ function TextInputComponent({ element }) {
|
|
|
1135
1540
|
{
|
|
1136
1541
|
placeholder: p.placeholder ?? void 0,
|
|
1137
1542
|
value: displayValue,
|
|
1138
|
-
onChangeText:
|
|
1543
|
+
onChangeText: bindings?.value ? setBoundValue : void 0,
|
|
1139
1544
|
secureTextEntry: p.secureTextEntry ?? false,
|
|
1140
1545
|
keyboardType: p.keyboardType ?? "default",
|
|
1141
1546
|
multiline: p.multiline ?? false,
|
|
@@ -1151,1146 +1556,713 @@ function TextInputComponent({ element }) {
|
|
|
1151
1556
|
)
|
|
1152
1557
|
] });
|
|
1153
1558
|
}
|
|
1154
|
-
function SwitchComponent({ element }) {
|
|
1559
|
+
function SwitchComponent({ element, bindings }) {
|
|
1155
1560
|
const p = element.props;
|
|
1156
|
-
const [
|
|
1157
|
-
p.
|
|
1561
|
+
const [checked, setChecked] = useBoundProp(
|
|
1562
|
+
p.checked,
|
|
1563
|
+
bindings?.checked
|
|
1158
1564
|
);
|
|
1159
|
-
const displayValue =
|
|
1565
|
+
const displayValue = checked ?? false;
|
|
1160
1566
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
|
|
1161
1567
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1162
1568
|
import_react_native2.Switch,
|
|
1163
1569
|
{
|
|
1164
1570
|
value: displayValue,
|
|
1165
|
-
onValueChange:
|
|
1571
|
+
onValueChange: bindings?.checked ? setChecked : void 0,
|
|
1166
1572
|
disabled: p.disabled ?? false
|
|
1167
1573
|
}
|
|
1168
|
-
),
|
|
1169
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1170
|
-
] });
|
|
1171
|
-
}
|
|
1172
|
-
function CheckboxComponent({ element }) {
|
|
1173
|
-
const p = element.props;
|
|
1174
|
-
const [boundValue, setBoundValue] = useStateBinding(
|
|
1175
|
-
p.statePath ?? ""
|
|
1176
|
-
);
|
|
1177
|
-
const checked = p.statePath ? boundValue ?? false : p.checked ?? false;
|
|
1178
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1179
|
-
import_react_native2.Pressable,
|
|
1180
|
-
{
|
|
1181
|
-
disabled: p.disabled ?? false,
|
|
1182
|
-
onPress: () => {
|
|
1183
|
-
if (p.statePath) {
|
|
1184
|
-
setBoundValue(!checked);
|
|
1185
|
-
}
|
|
1186
|
-
},
|
|
1187
|
-
style: {
|
|
1188
|
-
flexDirection: "row",
|
|
1189
|
-
alignItems: "center",
|
|
1190
|
-
gap: 8,
|
|
1191
|
-
opacity: p.disabled ? 0.5 : 1
|
|
1192
|
-
},
|
|
1193
|
-
children: [
|
|
1194
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1195
|
-
import_react_native2.View,
|
|
1196
|
-
{
|
|
1197
|
-
style: {
|
|
1198
|
-
width: 20,
|
|
1199
|
-
height: 20,
|
|
1200
|
-
borderRadius: 4,
|
|
1201
|
-
borderWidth: 2,
|
|
1202
|
-
borderColor: checked ? "#3b82f6" : "#d1d5db",
|
|
1203
|
-
backgroundColor: checked ? "#3b82f6" : "transparent",
|
|
1204
|
-
alignItems: "center",
|
|
1205
|
-
justifyContent: "center"
|
|
1206
|
-
},
|
|
1207
|
-
children: checked && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { color: "#ffffff", fontSize: 12, fontWeight: "700" }, children: "\u2713" })
|
|
1208
|
-
}
|
|
1209
|
-
),
|
|
1210
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1211
|
-
]
|
|
1212
|
-
}
|
|
1213
|
-
);
|
|
1214
|
-
}
|
|
1215
|
-
function SliderComponent({ element }) {
|
|
1216
|
-
const p = element.props;
|
|
1217
|
-
const min = p.min ?? 0;
|
|
1218
|
-
const max = p.max ?? 100;
|
|
1219
|
-
const value = p.value ?? min;
|
|
1220
|
-
const progress = max > min ? (value - min) / (max - min) : 0;
|
|
1221
|
-
const trackColor = p.color ?? "#3b82f6";
|
|
1222
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native2.View, { children: [
|
|
1223
|
-
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1224
|
-
import_react_native2.View,
|
|
1225
|
-
{
|
|
1226
|
-
style: {
|
|
1227
|
-
flexDirection: "row",
|
|
1228
|
-
justifyContent: "space-between",
|
|
1229
|
-
marginBottom: 4
|
|
1230
|
-
},
|
|
1231
|
-
children: [
|
|
1232
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#374151" }, children: p.label }),
|
|
1233
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280" }, children: value })
|
|
1234
|
-
]
|
|
1235
|
-
}
|
|
1236
|
-
),
|
|
1237
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1238
|
-
import_react_native2.View,
|
|
1239
|
-
{
|
|
1240
|
-
style: {
|
|
1241
|
-
height: 6,
|
|
1242
|
-
backgroundColor: "#e5e7eb",
|
|
1243
|
-
borderRadius: 3,
|
|
1244
|
-
overflow: "hidden"
|
|
1245
|
-
},
|
|
1246
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1247
|
-
import_react_native2.View,
|
|
1248
|
-
{
|
|
1249
|
-
style: {
|
|
1250
|
-
width: `${Math.min(Math.max(progress * 100, 0), 100)}%`,
|
|
1251
|
-
height: "100%",
|
|
1252
|
-
backgroundColor: trackColor,
|
|
1253
|
-
borderRadius: 3
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
)
|
|
1257
|
-
}
|
|
1258
|
-
)
|
|
1259
|
-
] });
|
|
1260
|
-
}
|
|
1261
|
-
function SearchBarComponent({ element, emit }) {
|
|
1262
|
-
const p = element.props;
|
|
1263
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1264
|
-
import_react_native2.View,
|
|
1265
|
-
{
|
|
1266
|
-
style: {
|
|
1267
|
-
flexDirection: "row",
|
|
1268
|
-
alignItems: "center",
|
|
1269
|
-
backgroundColor: "#f3f4f6",
|
|
1270
|
-
borderRadius: 8,
|
|
1271
|
-
paddingHorizontal: 12
|
|
1272
|
-
},
|
|
1273
|
-
children: [
|
|
1274
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginRight: 8 }, children: "Search" }),
|
|
1275
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1276
|
-
import_react_native2.TextInput,
|
|
1277
|
-
{
|
|
1278
|
-
placeholder: p.placeholder ?? "Search...",
|
|
1279
|
-
value: p.value ?? void 0,
|
|
1280
|
-
returnKeyType: "search",
|
|
1281
|
-
onSubmitEditing: () => emit?.("submit"),
|
|
1282
|
-
style: {
|
|
1283
|
-
flex: 1,
|
|
1284
|
-
paddingVertical: 10,
|
|
1285
|
-
fontSize: 16,
|
|
1286
|
-
color: "#111827"
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
)
|
|
1290
|
-
]
|
|
1291
|
-
}
|
|
1292
|
-
);
|
|
1293
|
-
}
|
|
1294
|
-
function SpinnerComponent({ element }) {
|
|
1295
|
-
const p = element.props;
|
|
1296
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.ActivityIndicator, { size: p.size ?? "small", color: p.color ?? "#3b82f6" });
|
|
1297
|
-
}
|
|
1298
|
-
function ProgressBarComponent({ element }) {
|
|
1299
|
-
const p = element.props;
|
|
1300
|
-
const height = p.height ?? 6;
|
|
1301
|
-
const progress = Math.min(Math.max(p.progress, 0), 1);
|
|
1302
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1303
|
-
import_react_native2.View,
|
|
1304
|
-
{
|
|
1305
|
-
style: {
|
|
1306
|
-
height,
|
|
1307
|
-
backgroundColor: p.trackColor ?? "#e5e7eb",
|
|
1308
|
-
borderRadius: height / 2,
|
|
1309
|
-
overflow: "hidden"
|
|
1310
|
-
},
|
|
1311
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1312
|
-
import_react_native2.View,
|
|
1313
|
-
{
|
|
1314
|
-
style: {
|
|
1315
|
-
width: `${progress * 100}%`,
|
|
1316
|
-
height: "100%",
|
|
1317
|
-
backgroundColor: p.color ?? "#3b82f6",
|
|
1318
|
-
borderRadius: height / 2
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
)
|
|
1322
|
-
}
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
function CardComponent({ element, children }) {
|
|
1326
|
-
const p = element.props;
|
|
1327
|
-
const elevated = p.elevated !== false;
|
|
1328
|
-
const padding = p.padding ?? 16;
|
|
1329
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1330
|
-
import_react_native2.View,
|
|
1331
|
-
{
|
|
1332
|
-
style: [
|
|
1333
|
-
{
|
|
1334
|
-
backgroundColor: p.backgroundColor ?? "#ffffff",
|
|
1335
|
-
borderRadius: p.borderRadius ?? 12,
|
|
1336
|
-
padding
|
|
1337
|
-
},
|
|
1338
|
-
elevated && {
|
|
1339
|
-
shadowColor: "#000",
|
|
1340
|
-
shadowOffset: { width: 0, height: 2 },
|
|
1341
|
-
shadowOpacity: 0.1,
|
|
1342
|
-
shadowRadius: 8,
|
|
1343
|
-
elevation: 3
|
|
1344
|
-
}
|
|
1345
|
-
],
|
|
1346
|
-
children: [
|
|
1347
|
-
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1348
|
-
import_react_native2.Text,
|
|
1349
|
-
{
|
|
1350
|
-
style: {
|
|
1351
|
-
fontSize: 18,
|
|
1352
|
-
fontWeight: "600",
|
|
1353
|
-
color: "#111827",
|
|
1354
|
-
marginBottom: p.subtitle ? 2 : 12
|
|
1355
|
-
},
|
|
1356
|
-
children: p.title
|
|
1357
|
-
}
|
|
1358
|
-
),
|
|
1359
|
-
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1360
|
-
import_react_native2.Text,
|
|
1361
|
-
{
|
|
1362
|
-
style: {
|
|
1363
|
-
fontSize: 14,
|
|
1364
|
-
color: "#6b7280",
|
|
1365
|
-
marginBottom: 12
|
|
1366
|
-
},
|
|
1367
|
-
children: p.subtitle
|
|
1368
|
-
}
|
|
1369
|
-
),
|
|
1370
|
-
children
|
|
1371
|
-
]
|
|
1372
|
-
}
|
|
1373
|
-
);
|
|
1574
|
+
),
|
|
1575
|
+
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1576
|
+
] });
|
|
1374
1577
|
}
|
|
1375
|
-
function
|
|
1578
|
+
function CheckboxComponent({ element, bindings }) {
|
|
1376
1579
|
const p = element.props;
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
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,
|
|
1380
1587
|
{
|
|
1588
|
+
disabled: p.disabled ?? false,
|
|
1589
|
+
onPress: () => {
|
|
1590
|
+
if (bindings?.checked) {
|
|
1591
|
+
setChecked(!isChecked);
|
|
1592
|
+
}
|
|
1593
|
+
},
|
|
1381
1594
|
style: {
|
|
1382
1595
|
flexDirection: "row",
|
|
1383
1596
|
alignItems: "center",
|
|
1384
|
-
|
|
1385
|
-
|
|
1597
|
+
gap: 8,
|
|
1598
|
+
opacity: p.disabled ? 0.5 : 1
|
|
1386
1599
|
},
|
|
1387
1600
|
children: [
|
|
1388
|
-
|
|
1389
|
-
import_react_native2.
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1602
|
+
import_react_native2.View,
|
|
1390
1603
|
{
|
|
1391
1604
|
style: {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
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"
|
|
1397
1613
|
},
|
|
1398
|
-
children:
|
|
1614
|
+
children: isChecked && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { color: "#ffffff", fontSize: 12, fontWeight: "700" }, children: "\u2713" })
|
|
1399
1615
|
}
|
|
1400
1616
|
),
|
|
1401
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
1402
|
-
|
|
1403
|
-
p.subtitle && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginTop: 2 }, children: p.subtitle })
|
|
1404
|
-
] }),
|
|
1405
|
-
p.trailing && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 14, color: "#6b7280", marginLeft: 8 }, children: p.trailing }),
|
|
1406
|
-
p.showChevron && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#9ca3af", marginLeft: 8 }, children: ">" })
|
|
1407
|
-
]
|
|
1408
|
-
}
|
|
1409
|
-
);
|
|
1410
|
-
if (hasPress) {
|
|
1411
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1412
|
-
import_react_native2.Pressable,
|
|
1413
|
-
{
|
|
1414
|
-
onPress: () => emit?.("press"),
|
|
1415
|
-
style: ({ pressed }) => ({
|
|
1416
|
-
opacity: pressed ? 0.7 : 1,
|
|
1417
|
-
backgroundColor: pressed ? "#f9fafb" : "transparent"
|
|
1418
|
-
}),
|
|
1419
|
-
children: content
|
|
1420
|
-
}
|
|
1421
|
-
);
|
|
1422
|
-
}
|
|
1423
|
-
return content;
|
|
1424
|
-
}
|
|
1425
|
-
function ModalComponent({ element, children }) {
|
|
1426
|
-
const p = element.props;
|
|
1427
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1428
|
-
import_react_native2.Modal,
|
|
1429
|
-
{
|
|
1430
|
-
visible: p.visible,
|
|
1431
|
-
animationType: p.animationType ?? "slide",
|
|
1432
|
-
transparent: true,
|
|
1433
|
-
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: [
|
|
1434
|
-
p.title && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: modalStyles.title, children: p.title }),
|
|
1435
|
-
children
|
|
1436
|
-
] }) })
|
|
1437
|
-
}
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
|
-
function createStandardActionHandlers(options) {
|
|
1441
|
-
return {
|
|
1442
|
-
navigate: async (params) => {
|
|
1443
|
-
const screen = params.screen;
|
|
1444
|
-
const navParams = params.params;
|
|
1445
|
-
options?.navigate?.(screen, navParams ?? void 0);
|
|
1446
|
-
},
|
|
1447
|
-
goBack: async () => {
|
|
1448
|
-
options?.goBack?.();
|
|
1449
|
-
},
|
|
1450
|
-
showAlert: async (params) => {
|
|
1451
|
-
const title = params.title;
|
|
1452
|
-
const message = params.message ?? void 0;
|
|
1453
|
-
const buttons = params.buttons;
|
|
1454
|
-
const alertButtons = buttons?.map((btn) => ({
|
|
1455
|
-
text: btn.text,
|
|
1456
|
-
style: btn.style
|
|
1457
|
-
}));
|
|
1458
|
-
import_react_native2.Alert.alert(title, message, alertButtons);
|
|
1459
|
-
},
|
|
1460
|
-
share: async (params) => {
|
|
1461
|
-
const message = params.message;
|
|
1462
|
-
const url = params.url ?? void 0;
|
|
1463
|
-
const title = params.title ?? void 0;
|
|
1464
|
-
await import_react_native2.Share.share({ message, url, title });
|
|
1465
|
-
},
|
|
1466
|
-
openURL: async (params) => {
|
|
1467
|
-
const url = params.url;
|
|
1468
|
-
const canOpen = await import_react_native2.Linking.canOpenURL(url);
|
|
1469
|
-
if (canOpen) {
|
|
1470
|
-
await import_react_native2.Linking.openURL(url);
|
|
1471
|
-
}
|
|
1472
|
-
},
|
|
1473
|
-
setData: async (params) => {
|
|
1474
|
-
const path = params.path;
|
|
1475
|
-
const value = params.value;
|
|
1476
|
-
options?.onSetData?.(path, value);
|
|
1477
|
-
},
|
|
1478
|
-
refresh: async (params) => {
|
|
1479
|
-
const target = params.target ?? void 0;
|
|
1480
|
-
options?.onRefresh?.(target);
|
|
1481
|
-
}
|
|
1482
|
-
};
|
|
1483
|
-
}
|
|
1484
|
-
var standardComponents = {
|
|
1485
|
-
// Layout
|
|
1486
|
-
Container: ContainerComponent,
|
|
1487
|
-
Row: RowComponent,
|
|
1488
|
-
Column: ColumnComponent,
|
|
1489
|
-
ScrollContainer: ScrollContainerComponent,
|
|
1490
|
-
SafeArea: SafeAreaComponent,
|
|
1491
|
-
Pressable: PressableComponent,
|
|
1492
|
-
Spacer: SpacerComponent,
|
|
1493
|
-
Divider: DividerComponent,
|
|
1494
|
-
// Content
|
|
1495
|
-
Heading: HeadingComponent,
|
|
1496
|
-
Paragraph: ParagraphComponent,
|
|
1497
|
-
Label: LabelComponent,
|
|
1498
|
-
Image: ImageComponent,
|
|
1499
|
-
Avatar: AvatarComponent,
|
|
1500
|
-
Badge: BadgeComponent,
|
|
1501
|
-
Chip: ChipComponent,
|
|
1502
|
-
// Input
|
|
1503
|
-
Button: ButtonComponent,
|
|
1504
|
-
TextInput: TextInputComponent,
|
|
1505
|
-
Switch: SwitchComponent,
|
|
1506
|
-
Checkbox: CheckboxComponent,
|
|
1507
|
-
Slider: SliderComponent,
|
|
1508
|
-
SearchBar: SearchBarComponent,
|
|
1509
|
-
// Feedback
|
|
1510
|
-
Spinner: SpinnerComponent,
|
|
1511
|
-
ProgressBar: ProgressBarComponent,
|
|
1512
|
-
// Composite
|
|
1513
|
-
Card: CardComponent,
|
|
1514
|
-
ListItem: ListItemComponent,
|
|
1515
|
-
Modal: ModalComponent
|
|
1516
|
-
};
|
|
1517
|
-
var inputStyles = import_react_native2.StyleSheet.create({
|
|
1518
|
-
label: {
|
|
1519
|
-
fontSize: 14,
|
|
1520
|
-
fontWeight: "500",
|
|
1521
|
-
color: "#374151",
|
|
1522
|
-
marginBottom: 4
|
|
1523
|
-
},
|
|
1524
|
-
input: {
|
|
1525
|
-
borderWidth: 1,
|
|
1526
|
-
borderColor: "#d1d5db",
|
|
1527
|
-
borderRadius: 8,
|
|
1528
|
-
paddingHorizontal: 12,
|
|
1529
|
-
paddingVertical: 10,
|
|
1530
|
-
fontSize: 16,
|
|
1531
|
-
color: "#111827",
|
|
1532
|
-
backgroundColor: "#ffffff"
|
|
1533
|
-
}
|
|
1534
|
-
});
|
|
1535
|
-
var modalStyles = import_react_native2.StyleSheet.create({
|
|
1536
|
-
overlay: {
|
|
1537
|
-
flex: 1,
|
|
1538
|
-
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
1539
|
-
justifyContent: "flex-end"
|
|
1540
|
-
},
|
|
1541
|
-
content: {
|
|
1542
|
-
backgroundColor: "#ffffff",
|
|
1543
|
-
borderTopLeftRadius: 16,
|
|
1544
|
-
borderTopRightRadius: 16,
|
|
1545
|
-
padding: 24,
|
|
1546
|
-
maxHeight: "80%"
|
|
1547
|
-
},
|
|
1548
|
-
title: {
|
|
1549
|
-
fontSize: 20,
|
|
1550
|
-
fontWeight: "600",
|
|
1551
|
-
color: "#111827",
|
|
1552
|
-
marginBottom: 16
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
|
|
1556
|
-
// src/renderer.tsx
|
|
1557
|
-
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1558
|
-
var ElementErrorBoundary = class extends import_react6.default.Component {
|
|
1559
|
-
constructor(props) {
|
|
1560
|
-
super(props);
|
|
1561
|
-
this.state = { hasError: false };
|
|
1562
|
-
}
|
|
1563
|
-
static getDerivedStateFromError() {
|
|
1564
|
-
return { hasError: true };
|
|
1565
|
-
}
|
|
1566
|
-
componentDidCatch(error, info) {
|
|
1567
|
-
console.error(
|
|
1568
|
-
`[json-render] Rendering error in <${this.props.elementType}>:`,
|
|
1569
|
-
error,
|
|
1570
|
-
info.componentStack
|
|
1571
|
-
);
|
|
1572
|
-
}
|
|
1573
|
-
render() {
|
|
1574
|
-
if (this.state.hasError) {
|
|
1575
|
-
return null;
|
|
1576
|
-
}
|
|
1577
|
-
return this.props.children;
|
|
1578
|
-
}
|
|
1579
|
-
};
|
|
1580
|
-
function ElementRenderer({
|
|
1581
|
-
element,
|
|
1582
|
-
spec,
|
|
1583
|
-
registry,
|
|
1584
|
-
loading,
|
|
1585
|
-
fallback
|
|
1586
|
-
}) {
|
|
1587
|
-
const repeatScope = useRepeatScope();
|
|
1588
|
-
const { ctx } = useVisibility();
|
|
1589
|
-
const { execute } = useActions();
|
|
1590
|
-
let effectiveElement = element;
|
|
1591
|
-
if (repeatScope) {
|
|
1592
|
-
const rewrittenProps = rewriteRepeatTokens(
|
|
1593
|
-
element.props,
|
|
1594
|
-
repeatScope.basePath,
|
|
1595
|
-
repeatScope.index
|
|
1596
|
-
);
|
|
1597
|
-
const rewrittenVisible = element.visible !== void 0 ? rewriteRepeatTokens(
|
|
1598
|
-
element.visible,
|
|
1599
|
-
repeatScope.basePath,
|
|
1600
|
-
repeatScope.index
|
|
1601
|
-
) : element.visible;
|
|
1602
|
-
const rewrittenOn = element.on !== void 0 ? rewriteRepeatTokens(
|
|
1603
|
-
element.on,
|
|
1604
|
-
repeatScope.basePath,
|
|
1605
|
-
repeatScope.index
|
|
1606
|
-
) : element.on;
|
|
1607
|
-
if (rewrittenProps !== element.props || rewrittenVisible !== element.visible || rewrittenOn !== element.on) {
|
|
1608
|
-
effectiveElement = {
|
|
1609
|
-
...element,
|
|
1610
|
-
props: rewrittenProps,
|
|
1611
|
-
visible: rewrittenVisible,
|
|
1612
|
-
on: rewrittenOn
|
|
1613
|
-
};
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
const isVisible = useIsVisible(effectiveElement.visible);
|
|
1617
|
-
const onBindings = effectiveElement.on;
|
|
1618
|
-
const emit = (0, import_react6.useCallback)(
|
|
1619
|
-
(eventName) => {
|
|
1620
|
-
const binding = onBindings?.[eventName];
|
|
1621
|
-
if (!binding) return;
|
|
1622
|
-
const bindings = Array.isArray(binding) ? binding : [binding];
|
|
1623
|
-
for (const b of bindings) {
|
|
1624
|
-
execute(b);
|
|
1625
|
-
}
|
|
1626
|
-
},
|
|
1627
|
-
[onBindings, execute]
|
|
1628
|
-
);
|
|
1629
|
-
if (!isVisible) {
|
|
1630
|
-
return null;
|
|
1631
|
-
}
|
|
1632
|
-
const resolvedProps = (0, import_core6.resolveElementProps)(
|
|
1633
|
-
effectiveElement.props,
|
|
1634
|
-
ctx
|
|
1635
|
-
);
|
|
1636
|
-
const resolvedElement = resolvedProps !== effectiveElement.props ? { ...effectiveElement, props: resolvedProps } : effectiveElement;
|
|
1637
|
-
const Component = registry[resolvedElement.type] ?? fallback;
|
|
1638
|
-
if (!Component) {
|
|
1639
|
-
console.warn(`No renderer for component type: ${resolvedElement.type}`);
|
|
1640
|
-
return null;
|
|
1641
|
-
}
|
|
1642
|
-
const children = resolvedElement.repeat ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1643
|
-
RepeatChildren,
|
|
1644
|
-
{
|
|
1645
|
-
element: resolvedElement,
|
|
1646
|
-
spec,
|
|
1647
|
-
registry,
|
|
1648
|
-
loading,
|
|
1649
|
-
fallback
|
|
1650
|
-
}
|
|
1651
|
-
) : resolvedElement.children?.map((childKey) => {
|
|
1652
|
-
const childElement = spec.elements[childKey];
|
|
1653
|
-
if (!childElement) {
|
|
1654
|
-
if (!loading) {
|
|
1655
|
-
console.warn(
|
|
1656
|
-
`[json-render] Missing element "${childKey}" referenced as child of "${resolvedElement.type}". This element will not render.`
|
|
1657
|
-
);
|
|
1658
|
-
}
|
|
1659
|
-
return null;
|
|
1617
|
+
p.label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react_native2.Text, { style: { fontSize: 16, color: "#374151" }, children: p.label })
|
|
1618
|
+
]
|
|
1660
1619
|
}
|
|
1661
|
-
|
|
1662
|
-
ElementRenderer,
|
|
1663
|
-
{
|
|
1664
|
-
element: childElement,
|
|
1665
|
-
spec,
|
|
1666
|
-
registry,
|
|
1667
|
-
loading,
|
|
1668
|
-
fallback
|
|
1669
|
-
},
|
|
1670
|
-
childKey
|
|
1671
|
-
);
|
|
1672
|
-
});
|
|
1673
|
-
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
|
+
);
|
|
1674
1621
|
}
|
|
1675
|
-
function
|
|
1676
|
-
element
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
const items = (0, import_core6.getByPath)(state, statePath) ?? [];
|
|
1686
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: items.map((item, index) => {
|
|
1687
|
-
const key = repeat.key && typeof item === "object" && item !== null ? String(item[repeat.key] ?? index) : String(index);
|
|
1688
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1689
|
-
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,
|
|
1690
1632
|
{
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
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
|
|
1700
1661
|
}
|
|
1701
|
-
return null;
|
|
1702
1662
|
}
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
spec,
|
|
1708
|
-
registry,
|
|
1709
|
-
loading,
|
|
1710
|
-
fallback
|
|
1711
|
-
},
|
|
1712
|
-
childKey
|
|
1713
|
-
);
|
|
1714
|
-
})
|
|
1715
|
-
},
|
|
1716
|
-
key
|
|
1717
|
-
);
|
|
1718
|
-
}) });
|
|
1663
|
+
)
|
|
1664
|
+
}
|
|
1665
|
+
)
|
|
1666
|
+
] });
|
|
1719
1667
|
}
|
|
1720
|
-
function
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
loading,
|
|
1725
|
-
fallback
|
|
1726
|
-
}) {
|
|
1727
|
-
const registry = (0, import_react6.useMemo)(
|
|
1728
|
-
() => ({
|
|
1729
|
-
...includeStandard ? standardComponents : {},
|
|
1730
|
-
...customRegistry
|
|
1731
|
-
}),
|
|
1732
|
-
[customRegistry, includeStandard]
|
|
1733
|
-
);
|
|
1734
|
-
if (!spec || !spec.root) {
|
|
1735
|
-
return null;
|
|
1736
|
-
}
|
|
1737
|
-
const rootElement = spec.elements[spec.root];
|
|
1738
|
-
if (!rootElement) {
|
|
1739
|
-
return null;
|
|
1740
|
-
}
|
|
1741
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1742
|
-
ElementRenderer,
|
|
1668
|
+
function SearchBarComponent({ element, emit }) {
|
|
1669
|
+
const p = element.props;
|
|
1670
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
1671
|
+
import_react_native2.View,
|
|
1743
1672
|
{
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
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
|
+
]
|
|
1749
1698
|
}
|
|
1750
1699
|
);
|
|
1751
1700
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
authState,
|
|
1756
|
-
actionHandlers,
|
|
1757
|
-
navigate,
|
|
1758
|
-
validationFunctions,
|
|
1759
|
-
onStateChange,
|
|
1760
|
-
children
|
|
1761
|
-
}) {
|
|
1762
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1763
|
-
StateProvider,
|
|
1764
|
-
{
|
|
1765
|
-
initialState,
|
|
1766
|
-
authState,
|
|
1767
|
-
onStateChange,
|
|
1768
|
-
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: [
|
|
1769
|
-
children,
|
|
1770
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
1771
|
-
] }) }) })
|
|
1772
|
-
}
|
|
1773
|
-
);
|
|
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" });
|
|
1774
1704
|
}
|
|
1775
|
-
function
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
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,
|
|
1782
1711
|
{
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
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
|
+
)
|
|
1786
1729
|
}
|
|
1787
1730
|
);
|
|
1788
1731
|
}
|
|
1789
|
-
function
|
|
1790
|
-
|
|
1791
|
-
|
|
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
|
-
|
|
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
|
+
]
|
|
1833
1779
|
}
|
|
1834
|
-
|
|
1835
|
-
return { registry, handlers, executeAction: executeAction2 };
|
|
1780
|
+
);
|
|
1836
1781
|
}
|
|
1837
|
-
function
|
|
1838
|
-
const
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
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,
|
|
1857
1820
|
{
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
{
|
|
1865
|
-
spec,
|
|
1866
|
-
registry,
|
|
1867
|
-
loading,
|
|
1868
|
-
fallback
|
|
1869
|
-
}
|
|
1870
|
-
),
|
|
1871
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ConfirmationDialogManager, {})
|
|
1872
|
-
] }) }) })
|
|
1821
|
+
onPress: () => emit("press"),
|
|
1822
|
+
style: ({ pressed }) => ({
|
|
1823
|
+
opacity: pressed ? 0.7 : 1,
|
|
1824
|
+
backgroundColor: pressed ? "#f9fafb" : "transparent"
|
|
1825
|
+
}),
|
|
1826
|
+
children: content
|
|
1873
1827
|
}
|
|
1874
1828
|
);
|
|
1875
|
-
};
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
// src/hooks.ts
|
|
1879
|
-
var import_react7 = require("react");
|
|
1880
|
-
var import_core7 = require("@json-render/core");
|
|
1881
|
-
function looksLikeJson(line) {
|
|
1882
|
-
return line.startsWith("{") || line.startsWith("[");
|
|
1883
|
-
}
|
|
1884
|
-
function parsePatchLine(line) {
|
|
1885
|
-
const trimmed = line.trim();
|
|
1886
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
1887
|
-
return { patch: null, malformed: false };
|
|
1888
1829
|
}
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
console.warn(
|
|
1904
|
-
`[json-render] Recovered malformed JSONL line by removing ${i + 1} trailing '${last}'`
|
|
1905
|
-
);
|
|
1906
|
-
return { patch: result, malformed: false };
|
|
1907
|
-
} catch {
|
|
1908
|
-
}
|
|
1909
|
-
} else {
|
|
1910
|
-
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
|
+
] }) })
|
|
1911
1844
|
}
|
|
1912
|
-
|
|
1913
|
-
return { patch: null, malformed: true };
|
|
1845
|
+
);
|
|
1914
1846
|
}
|
|
1915
|
-
function
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
const
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
);
|
|
1946
|
-
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);
|
|
1947
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);
|
|
1948
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"
|
|
1949
1940
|
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
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
|
|
1955
1960
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
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 };
|
|
1960
1969
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
const elementKey = pathParts[0];
|
|
1964
|
-
if (!elementKey) return;
|
|
1965
|
-
if (pathParts.length === 1) {
|
|
1966
|
-
const { [elementKey]: _, ...rest } = newSpec.elements;
|
|
1967
|
-
newSpec.elements = rest;
|
|
1968
|
-
} else {
|
|
1969
|
-
const element = newSpec.elements[elementKey];
|
|
1970
|
-
if (element) {
|
|
1971
|
-
const propPath = "/" + pathParts.slice(1).join("/");
|
|
1972
|
-
const newElement = { ...element };
|
|
1973
|
-
(0, import_core7.removeByPath)(
|
|
1974
|
-
newElement,
|
|
1975
|
-
propPath
|
|
1976
|
-
);
|
|
1977
|
-
newSpec.elements[elementKey] = newElement;
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1970
|
+
static getDerivedStateFromError() {
|
|
1971
|
+
return { hasError: true };
|
|
1980
1972
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
setSpecValue(newSpec, patch.path, patch.value);
|
|
1992
|
-
break;
|
|
1993
|
-
}
|
|
1994
|
-
case "remove": {
|
|
1995
|
-
removeSpecValue(newSpec, patch.path);
|
|
1996
|
-
break;
|
|
1997
|
-
}
|
|
1998
|
-
case "move": {
|
|
1999
|
-
if (!patch.from) break;
|
|
2000
|
-
const moveValue = getSpecValue(newSpec, patch.from);
|
|
2001
|
-
removeSpecValue(newSpec, patch.from);
|
|
2002
|
-
setSpecValue(newSpec, patch.path, moveValue);
|
|
2003
|
-
break;
|
|
2004
|
-
}
|
|
2005
|
-
case "copy": {
|
|
2006
|
-
if (!patch.from) break;
|
|
2007
|
-
const copyValue = getSpecValue(newSpec, patch.from);
|
|
2008
|
-
setSpecValue(newSpec, patch.path, copyValue);
|
|
2009
|
-
break;
|
|
2010
|
-
}
|
|
2011
|
-
case "test": {
|
|
2012
|
-
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;
|
|
2013
1983
|
}
|
|
1984
|
+
return this.props.children;
|
|
2014
1985
|
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
validate: enableValidation = false,
|
|
2023
|
-
maxRetries = 5
|
|
1986
|
+
};
|
|
1987
|
+
var ElementRenderer = import_react7.default.memo(function ElementRenderer2({
|
|
1988
|
+
element,
|
|
1989
|
+
spec,
|
|
1990
|
+
registry,
|
|
1991
|
+
loading,
|
|
1992
|
+
fallback
|
|
2024
1993
|
}) {
|
|
2025
|
-
const
|
|
2026
|
-
const
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
body: JSON.stringify({
|
|
2049
|
-
prompt,
|
|
2050
|
-
context,
|
|
2051
|
-
currentSpec
|
|
2052
|
-
}),
|
|
2053
|
-
signal: abortControllerRef.current.signal
|
|
2054
|
-
});
|
|
2055
|
-
console.log("[useUIStream] response status:", response.status);
|
|
2056
|
-
if (!response.ok) {
|
|
2057
|
-
let errorMessage = `HTTP error: ${response.status}`;
|
|
2058
|
-
try {
|
|
2059
|
-
const errorData = await response.json();
|
|
2060
|
-
if (errorData.message) {
|
|
2061
|
-
errorMessage = errorData.message;
|
|
2062
|
-
} else if (errorData.error) {
|
|
2063
|
-
errorMessage = errorData.error;
|
|
2064
|
-
}
|
|
2065
|
-
} catch {
|
|
2066
|
-
}
|
|
2067
|
-
throw new Error(errorMessage);
|
|
2068
|
-
}
|
|
2069
|
-
const reader = response.body?.getReader();
|
|
2070
|
-
if (!reader) {
|
|
2071
|
-
throw new Error("No response body");
|
|
2072
|
-
}
|
|
2073
|
-
const decoder = new TextDecoder();
|
|
2074
|
-
let buffer = "";
|
|
2075
|
-
let chunkCount = 0;
|
|
2076
|
-
let totalBytes = 0;
|
|
2077
|
-
let aborted = false;
|
|
2078
|
-
while (true) {
|
|
2079
|
-
const { done, value } = await reader.read();
|
|
2080
|
-
if (done) {
|
|
2081
|
-
console.log(
|
|
2082
|
-
"[useUIStream] stream done. Total chunks:",
|
|
2083
|
-
chunkCount,
|
|
2084
|
-
"bytes:",
|
|
2085
|
-
totalBytes
|
|
2086
|
-
);
|
|
2087
|
-
break;
|
|
2088
|
-
}
|
|
2089
|
-
chunkCount++;
|
|
2090
|
-
totalBytes += value?.byteLength ?? 0;
|
|
2091
|
-
const decoded = decoder.decode(value, { stream: true });
|
|
2092
|
-
buffer += decoded;
|
|
2093
|
-
if (chunkCount <= 5) {
|
|
2094
|
-
console.log(
|
|
2095
|
-
`[useUIStream] chunk #${chunkCount} (${value?.byteLength ?? 0}b):`,
|
|
2096
|
-
decoded.slice(0, 200)
|
|
2097
|
-
);
|
|
2098
|
-
}
|
|
2099
|
-
const lines = buffer.split("\n");
|
|
2100
|
-
buffer = lines.pop() ?? "";
|
|
2101
|
-
const newLines = [];
|
|
2102
|
-
for (const line of lines) {
|
|
2103
|
-
if (line.trim()) {
|
|
2104
|
-
newLines.push(line);
|
|
2105
|
-
}
|
|
2106
|
-
const { patch, malformed } = parsePatchLine(line);
|
|
2107
|
-
if (patch) {
|
|
2108
|
-
console.log("[useUIStream] applied patch:", patch.op, patch.path);
|
|
2109
|
-
currentSpec = applyPatch(currentSpec, patch);
|
|
2110
|
-
setSpec({ ...currentSpec });
|
|
2111
|
-
} else if (malformed) {
|
|
2112
|
-
malformedLines.push(line.trim());
|
|
2113
|
-
console.warn(
|
|
2114
|
-
"[useUIStream] malformed JSON line:",
|
|
2115
|
-
line.trim().slice(0, 200)
|
|
2116
|
-
);
|
|
2117
|
-
if (abortOnMalformed) {
|
|
2118
|
-
console.warn(
|
|
2119
|
-
"[useUIStream] aborting stream due to malformed JSON, will retry"
|
|
2120
|
-
);
|
|
2121
|
-
await reader.cancel();
|
|
2122
|
-
aborted = true;
|
|
2123
|
-
break;
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
if (aborted) break;
|
|
2128
|
-
if (newLines.length > 0) {
|
|
2129
|
-
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;
|
|
2130
2017
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
setRawLines((prev) => [...prev, buffer]);
|
|
2135
|
-
const { patch, malformed } = parsePatchLine(buffer);
|
|
2136
|
-
if (patch) {
|
|
2137
|
-
currentSpec = applyPatch(currentSpec, patch);
|
|
2138
|
-
setSpec({ ...currentSpec });
|
|
2139
|
-
} else if (malformed) {
|
|
2140
|
-
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);
|
|
2141
2021
|
}
|
|
2022
|
+
execute({ ...b, params: resolved });
|
|
2142
2023
|
}
|
|
2143
|
-
console.log(
|
|
2144
|
-
"[useUIStream] spec state - root:",
|
|
2145
|
-
currentSpec.root,
|
|
2146
|
-
"elements:",
|
|
2147
|
-
Object.keys(currentSpec.elements).length,
|
|
2148
|
-
"completed:",
|
|
2149
|
-
!aborted,
|
|
2150
|
-
"malformed:",
|
|
2151
|
-
malformedLines.length
|
|
2152
|
-
);
|
|
2153
|
-
return { spec: currentSpec, completed: !aborted, malformedLines };
|
|
2154
2024
|
},
|
|
2155
|
-
[
|
|
2025
|
+
[onBindings, execute, fullCtx]
|
|
2156
2026
|
);
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
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) {
|
|
2181
2106
|
console.warn(
|
|
2182
|
-
|
|
2107
|
+
`[json-render] Missing element "${childKey}" referenced as child of "${element.type}" (repeat). This element will not render.`
|
|
2183
2108
|
);
|
|
2184
|
-
break;
|
|
2185
2109
|
}
|
|
2186
|
-
|
|
2187
|
-
console.warn(
|
|
2188
|
-
`[useUIStream] mid-stream retry ${retriesUsed}/${maxRetries} due to malformed JSON`
|
|
2189
|
-
);
|
|
2190
|
-
currentContext = { ...context, previousSpec: currentSpec };
|
|
2191
|
-
currentPrompt = `The previous generation contained malformed JSON that could not be parsed. The line was:
|
|
2192
|
-
${result.malformedLines[result.malformedLines.length - 1]?.slice(0, 500)}
|
|
2193
|
-
|
|
2194
|
-
The current spec state is provided. Continue generating from where you left off. Output ONLY the remaining patches needed to complete the UI.`;
|
|
2195
|
-
continue;
|
|
2196
|
-
}
|
|
2197
|
-
if (!enableValidation || !currentSpec.root) {
|
|
2198
|
-
break;
|
|
2199
|
-
}
|
|
2200
|
-
const { spec: fixedSpec, fixes } = (0, import_core7.autoFixSpec)(currentSpec);
|
|
2201
|
-
if (fixes.length > 0) {
|
|
2202
|
-
console.log("[useUIStream] auto-fixed spec issues:", fixes);
|
|
2203
|
-
currentSpec = fixedSpec;
|
|
2204
|
-
setSpec({ ...currentSpec });
|
|
2205
|
-
}
|
|
2206
|
-
const validation = (0, import_core7.validateSpec)(currentSpec);
|
|
2207
|
-
if (validation.valid) {
|
|
2208
|
-
console.log("[useUIStream] spec validation passed");
|
|
2209
|
-
break;
|
|
2210
|
-
}
|
|
2211
|
-
const errors = validation.issues.filter(
|
|
2212
|
-
(i) => i.severity === "error"
|
|
2213
|
-
);
|
|
2214
|
-
if (retriesUsed >= maxRetries) {
|
|
2215
|
-
console.warn(
|
|
2216
|
-
"[useUIStream] spec validation failed, max retries exhausted:",
|
|
2217
|
-
errors.map((e) => e.message)
|
|
2218
|
-
);
|
|
2219
|
-
break;
|
|
2110
|
+
return null;
|
|
2220
2111
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
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
|
|
2226
2122
|
);
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
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
|
+
}
|
|
2253
2159
|
);
|
|
2254
|
-
(0, import_react7.useEffect)(() => {
|
|
2255
|
-
return () => {
|
|
2256
|
-
abortControllerRef.current?.abort();
|
|
2257
|
-
};
|
|
2258
|
-
}, []);
|
|
2259
|
-
return {
|
|
2260
|
-
spec,
|
|
2261
|
-
isStreaming,
|
|
2262
|
-
error,
|
|
2263
|
-
rawLines,
|
|
2264
|
-
send,
|
|
2265
|
-
stop,
|
|
2266
|
-
clear
|
|
2267
|
-
};
|
|
2268
2160
|
}
|
|
2269
|
-
function
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
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;
|
|
2279
2179
|
}
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
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);
|
|
2286
2219
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
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);
|
|
2289
2228
|
} else {
|
|
2290
|
-
|
|
2229
|
+
console.warn(`Unknown action: ${actionName}`);
|
|
2291
2230
|
}
|
|
2292
|
-
}
|
|
2293
|
-
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
|
+
};
|
|
2294
2266
|
}
|
|
2295
2267
|
|
|
2296
2268
|
// src/catalog.ts
|
|
@@ -2310,7 +2282,8 @@ var standardComponentDefinitions = {
|
|
|
2310
2282
|
flex: import_zod.z.number().nullable()
|
|
2311
2283
|
}),
|
|
2312
2284
|
slots: ["default"],
|
|
2313
|
-
description: "Generic container wrapper. Use for grouping elements with padding, margin, and background color."
|
|
2285
|
+
description: "Generic container wrapper. Use for grouping elements with padding, margin, and background color.",
|
|
2286
|
+
example: { padding: 16, backgroundColor: "#FFFFFF" }
|
|
2314
2287
|
},
|
|
2315
2288
|
Row: {
|
|
2316
2289
|
props: import_zod.z.object({
|
|
@@ -2329,7 +2302,8 @@ var standardComponentDefinitions = {
|
|
|
2329
2302
|
flex: import_zod.z.number().nullable()
|
|
2330
2303
|
}),
|
|
2331
2304
|
slots: ["default"],
|
|
2332
|
-
description: "Horizontal flex layout. Use for placing elements side by side."
|
|
2305
|
+
description: "Horizontal flex layout. Use for placing elements side by side.",
|
|
2306
|
+
example: { gap: 12, alignItems: "center" }
|
|
2333
2307
|
},
|
|
2334
2308
|
Column: {
|
|
2335
2309
|
props: import_zod.z.object({
|
|
@@ -2347,7 +2321,8 @@ var standardComponentDefinitions = {
|
|
|
2347
2321
|
flex: import_zod.z.number().nullable()
|
|
2348
2322
|
}),
|
|
2349
2323
|
slots: ["default"],
|
|
2350
|
-
description: "Vertical flex layout. Use for stacking elements top to bottom."
|
|
2324
|
+
description: "Vertical flex layout. Use for stacking elements top to bottom.",
|
|
2325
|
+
example: { gap: 12, padding: 16 }
|
|
2351
2326
|
},
|
|
2352
2327
|
ScrollContainer: {
|
|
2353
2328
|
props: import_zod.z.object({
|
|
@@ -2401,7 +2376,8 @@ var standardComponentDefinitions = {
|
|
|
2401
2376
|
align: import_zod.z.enum(["left", "center", "right"]).nullable()
|
|
2402
2377
|
}),
|
|
2403
2378
|
slots: [],
|
|
2404
|
-
description: "Heading text at various levels. h1 is largest, h4 is smallest."
|
|
2379
|
+
description: "Heading text at various levels. h1 is largest, h4 is smallest.",
|
|
2380
|
+
example: { text: "Welcome", level: "h1" }
|
|
2405
2381
|
},
|
|
2406
2382
|
Paragraph: {
|
|
2407
2383
|
props: import_zod.z.object({
|
|
@@ -2412,7 +2388,8 @@ var standardComponentDefinitions = {
|
|
|
2412
2388
|
fontSize: import_zod.z.number().nullable()
|
|
2413
2389
|
}),
|
|
2414
2390
|
slots: [],
|
|
2415
|
-
description: "Body text paragraph. Use for descriptions and longer content."
|
|
2391
|
+
description: "Body text paragraph. Use for descriptions and longer content.",
|
|
2392
|
+
example: { text: "This is a paragraph of body text." }
|
|
2416
2393
|
},
|
|
2417
2394
|
Label: {
|
|
2418
2395
|
props: import_zod.z.object({
|
|
@@ -2422,7 +2399,8 @@ var standardComponentDefinitions = {
|
|
|
2422
2399
|
size: import_zod.z.enum(["xs", "sm", "md"]).nullable()
|
|
2423
2400
|
}),
|
|
2424
2401
|
slots: [],
|
|
2425
|
-
description: "Small utility text. Use for captions, form labels, and secondary information."
|
|
2402
|
+
description: "Small utility text. Use for captions, form labels, and secondary information.",
|
|
2403
|
+
example: { text: "Status", size: "sm" }
|
|
2426
2404
|
},
|
|
2427
2405
|
Image: {
|
|
2428
2406
|
props: import_zod.z.object({
|
|
@@ -2434,7 +2412,13 @@ var standardComponentDefinitions = {
|
|
|
2434
2412
|
borderRadius: import_zod.z.number().nullable()
|
|
2435
2413
|
}),
|
|
2436
2414
|
slots: [],
|
|
2437
|
-
description: "Image display. Provide a source URL and optional dimensions."
|
|
2415
|
+
description: "Image display. Provide a source URL and optional dimensions.",
|
|
2416
|
+
example: {
|
|
2417
|
+
src: "https://picsum.photos/300/200",
|
|
2418
|
+
alt: "Photo",
|
|
2419
|
+
width: 300,
|
|
2420
|
+
height: 200
|
|
2421
|
+
}
|
|
2438
2422
|
},
|
|
2439
2423
|
Avatar: {
|
|
2440
2424
|
props: import_zod.z.object({
|
|
@@ -2444,7 +2428,8 @@ var standardComponentDefinitions = {
|
|
|
2444
2428
|
backgroundColor: import_zod.z.string().nullable()
|
|
2445
2429
|
}),
|
|
2446
2430
|
slots: [],
|
|
2447
|
-
description: "Circular avatar showing an image or initials. Use for user profiles and contacts."
|
|
2431
|
+
description: "Circular avatar showing an image or initials. Use for user profiles and contacts.",
|
|
2432
|
+
example: { initials: "JD", size: "md" }
|
|
2448
2433
|
},
|
|
2449
2434
|
Badge: {
|
|
2450
2435
|
props: import_zod.z.object({
|
|
@@ -2452,7 +2437,8 @@ var standardComponentDefinitions = {
|
|
|
2452
2437
|
variant: import_zod.z.enum(["default", "info", "success", "warning", "error"]).nullable()
|
|
2453
2438
|
}),
|
|
2454
2439
|
slots: [],
|
|
2455
|
-
description: "Small colored indicator with a label. Use for status, counts, and categories."
|
|
2440
|
+
description: "Small colored indicator with a label. Use for status, counts, and categories.",
|
|
2441
|
+
example: { label: "New", variant: "info" }
|
|
2456
2442
|
},
|
|
2457
2443
|
Chip: {
|
|
2458
2444
|
props: import_zod.z.object({
|
|
@@ -2477,13 +2463,13 @@ var standardComponentDefinitions = {
|
|
|
2477
2463
|
}),
|
|
2478
2464
|
events: ["press"],
|
|
2479
2465
|
slots: [],
|
|
2480
|
-
description: "Pressable button with label. Set variant for styling. Bind on.press for the handler to call on press."
|
|
2466
|
+
description: "Pressable button with label. Set variant for styling. Bind on.press for the handler to call on press.",
|
|
2467
|
+
example: { label: "Submit", variant: "primary" }
|
|
2481
2468
|
},
|
|
2482
2469
|
TextInput: {
|
|
2483
2470
|
props: import_zod.z.object({
|
|
2484
2471
|
placeholder: import_zod.z.string().nullable(),
|
|
2485
2472
|
value: import_zod.z.string().nullable(),
|
|
2486
|
-
statePath: import_zod.z.string().nullable(),
|
|
2487
2473
|
secureTextEntry: import_zod.z.boolean().nullable(),
|
|
2488
2474
|
keyboardType: import_zod.z.enum(["default", "email-address", "numeric", "phone-pad", "url"]).nullable(),
|
|
2489
2475
|
multiline: import_zod.z.boolean().nullable(),
|
|
@@ -2493,29 +2479,31 @@ var standardComponentDefinitions = {
|
|
|
2493
2479
|
}),
|
|
2494
2480
|
events: ["submit", "focus", "blur"],
|
|
2495
2481
|
slots: [],
|
|
2496
|
-
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.",
|
|
2483
|
+
example: {
|
|
2484
|
+
placeholder: "Enter text...",
|
|
2485
|
+
label: "Name"
|
|
2486
|
+
}
|
|
2497
2487
|
},
|
|
2498
2488
|
Switch: {
|
|
2499
2489
|
props: import_zod.z.object({
|
|
2500
|
-
|
|
2501
|
-
statePath: import_zod.z.string().nullable(),
|
|
2490
|
+
checked: import_zod.z.boolean().nullable(),
|
|
2502
2491
|
label: import_zod.z.string().nullable(),
|
|
2503
2492
|
disabled: import_zod.z.boolean().nullable()
|
|
2504
2493
|
}),
|
|
2505
2494
|
events: ["change"],
|
|
2506
2495
|
slots: [],
|
|
2507
|
-
description: "Toggle switch. Use
|
|
2496
|
+
description: "Toggle switch. Use $bindState to bind to the state model."
|
|
2508
2497
|
},
|
|
2509
2498
|
Checkbox: {
|
|
2510
2499
|
props: import_zod.z.object({
|
|
2511
2500
|
checked: import_zod.z.boolean().nullable(),
|
|
2512
|
-
statePath: import_zod.z.string().nullable(),
|
|
2513
2501
|
label: import_zod.z.string().nullable(),
|
|
2514
2502
|
disabled: import_zod.z.boolean().nullable()
|
|
2515
2503
|
}),
|
|
2516
2504
|
events: ["change"],
|
|
2517
2505
|
slots: [],
|
|
2518
|
-
description: "Checkbox for boolean selections. Use
|
|
2506
|
+
description: "Checkbox for boolean selections. Use $bindState to bind to the state model."
|
|
2519
2507
|
},
|
|
2520
2508
|
Slider: {
|
|
2521
2509
|
props: import_zod.z.object({
|
|
@@ -2523,18 +2511,16 @@ var standardComponentDefinitions = {
|
|
|
2523
2511
|
max: import_zod.z.number().nullable(),
|
|
2524
2512
|
step: import_zod.z.number().nullable(),
|
|
2525
2513
|
value: import_zod.z.number().nullable(),
|
|
2526
|
-
statePath: import_zod.z.string().nullable(),
|
|
2527
2514
|
label: import_zod.z.string().nullable(),
|
|
2528
2515
|
color: import_zod.z.string().nullable()
|
|
2529
2516
|
}),
|
|
2530
2517
|
slots: [],
|
|
2531
|
-
description: "Range slider for numeric values. Use
|
|
2518
|
+
description: "Range slider for numeric values. Use $bindState to bind to the state model."
|
|
2532
2519
|
},
|
|
2533
2520
|
SearchBar: {
|
|
2534
2521
|
props: import_zod.z.object({
|
|
2535
2522
|
placeholder: import_zod.z.string().nullable(),
|
|
2536
|
-
value: import_zod.z.string().nullable()
|
|
2537
|
-
statePath: import_zod.z.string().nullable()
|
|
2523
|
+
value: import_zod.z.string().nullable()
|
|
2538
2524
|
}),
|
|
2539
2525
|
events: ["submit"],
|
|
2540
2526
|
slots: [],
|
|
@@ -2574,7 +2560,8 @@ var standardComponentDefinitions = {
|
|
|
2574
2560
|
elevated: import_zod.z.boolean().nullable()
|
|
2575
2561
|
}),
|
|
2576
2562
|
slots: ["default"],
|
|
2577
|
-
description: "Elevated card container with optional title. Use for grouping related content."
|
|
2563
|
+
description: "Elevated card container with optional title. Use for grouping related content.",
|
|
2564
|
+
example: { title: "Details", padding: 16, elevated: true }
|
|
2578
2565
|
},
|
|
2579
2566
|
ListItem: {
|
|
2580
2567
|
props: import_zod.z.object({
|
|
@@ -2586,17 +2573,21 @@ var standardComponentDefinitions = {
|
|
|
2586
2573
|
}),
|
|
2587
2574
|
events: ["press"],
|
|
2588
2575
|
slots: [],
|
|
2589
|
-
description: "List row with title, subtitle, and optional leading/trailing text. Bind on.press for the press handler."
|
|
2576
|
+
description: "List row with title, subtitle, and optional leading/trailing text. Bind on.press for the press handler.",
|
|
2577
|
+
example: {
|
|
2578
|
+
title: "Settings",
|
|
2579
|
+
subtitle: "Manage your preferences",
|
|
2580
|
+
showChevron: true
|
|
2581
|
+
}
|
|
2590
2582
|
},
|
|
2591
2583
|
Modal: {
|
|
2592
2584
|
props: import_zod.z.object({
|
|
2593
2585
|
visible: import_zod.z.boolean(),
|
|
2594
2586
|
title: import_zod.z.string().nullable(),
|
|
2595
|
-
animationType: import_zod.z.enum(["slide", "fade", "none"]).nullable()
|
|
2596
|
-
statePath: import_zod.z.string().nullable()
|
|
2587
|
+
animationType: import_zod.z.enum(["slide", "fade", "none"]).nullable()
|
|
2597
2588
|
}),
|
|
2598
2589
|
slots: ["default"],
|
|
2599
|
-
description: "Modal overlay dialog. Use
|
|
2590
|
+
description: "Modal overlay dialog. Use $bindState on visible to bind visibility to the state model."
|
|
2600
2591
|
}
|
|
2601
2592
|
};
|
|
2602
2593
|
var standardActionDefinitions = {
|
|
@@ -2641,22 +2632,22 @@ var standardActionDefinitions = {
|
|
|
2641
2632
|
},
|
|
2642
2633
|
setState: {
|
|
2643
2634
|
params: import_zod.z.object({
|
|
2644
|
-
|
|
2635
|
+
statePath: import_zod.z.string(),
|
|
2645
2636
|
value: import_zod.z.unknown()
|
|
2646
2637
|
}),
|
|
2647
|
-
description: "Update a value in the state model at the given
|
|
2638
|
+
description: "Update a value in the state model at the given statePath."
|
|
2648
2639
|
},
|
|
2649
2640
|
pushState: {
|
|
2650
2641
|
params: import_zod.z.object({
|
|
2651
|
-
|
|
2642
|
+
statePath: import_zod.z.string(),
|
|
2652
2643
|
value: import_zod.z.unknown(),
|
|
2653
|
-
|
|
2644
|
+
clearStatePath: import_zod.z.string().optional()
|
|
2654
2645
|
}),
|
|
2655
|
-
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" }.'
|
|
2656
2647
|
},
|
|
2657
2648
|
removeState: {
|
|
2658
2649
|
params: import_zod.z.object({
|
|
2659
|
-
|
|
2650
|
+
statePath: import_zod.z.string(),
|
|
2660
2651
|
index: import_zod.z.number()
|
|
2661
2652
|
}),
|
|
2662
2653
|
description: "Remove an item from an array in the state model at the given index."
|
|
@@ -2679,18 +2670,17 @@ var standardActionDefinitions = {
|
|
|
2679
2670
|
ValidationProvider,
|
|
2680
2671
|
VisibilityProvider,
|
|
2681
2672
|
createRenderer,
|
|
2682
|
-
createRendererFromCatalog,
|
|
2683
2673
|
createStandardActionHandlers,
|
|
2684
2674
|
defineRegistry,
|
|
2685
2675
|
elementTreeSchema,
|
|
2686
2676
|
flatToTree,
|
|
2687
|
-
rewriteRepeatTokens,
|
|
2688
2677
|
schema,
|
|
2689
2678
|
standardActionDefinitions,
|
|
2690
2679
|
standardComponentDefinitions,
|
|
2691
2680
|
standardComponents,
|
|
2692
2681
|
useAction,
|
|
2693
2682
|
useActions,
|
|
2683
|
+
useBoundProp,
|
|
2694
2684
|
useFieldValidation,
|
|
2695
2685
|
useIsVisible,
|
|
2696
2686
|
useRepeatScope,
|