autoui-react 0.0.3-alpha → 0.0.5-alpha
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 +47 -1
- package/dist/index.css +10 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +135 -93
- package/dist/index.d.ts +135 -93
- package/dist/index.js +2474 -536
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2433 -524
- package/dist/index.mjs.map +1 -1
- package/package.json +44 -6
package/dist/index.js
CHANGED
|
@@ -1,19 +1,130 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var clsx = require('clsx');
|
|
4
|
+
var tailwindMerge = require('tailwind-merge');
|
|
5
|
+
var reactSlot = require('@radix-ui/react-slot');
|
|
6
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
5
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var
|
|
8
|
+
var React = require('react');
|
|
7
9
|
var openai = require('@ai-sdk/openai');
|
|
10
|
+
var ai = require('ai');
|
|
11
|
+
var zod = require('zod');
|
|
12
|
+
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
13
|
+
var lucideReact = require('lucide-react');
|
|
14
|
+
var SelectPrimitive = require('@radix-ui/react-select');
|
|
15
|
+
var CheckboxPrimitive = require('@radix-ui/react-checkbox');
|
|
16
|
+
var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
|
|
17
|
+
var TabsPrimitive = require('@radix-ui/react-tabs');
|
|
18
|
+
var LabelPrimitive = require('@radix-ui/react-label');
|
|
19
|
+
|
|
20
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
+
|
|
22
|
+
function _interopNamespace(e) {
|
|
23
|
+
if (e && e.__esModule) return e;
|
|
24
|
+
var n = Object.create(null);
|
|
25
|
+
if (e) {
|
|
26
|
+
Object.keys(e).forEach(function (k) {
|
|
27
|
+
if (k !== 'default') {
|
|
28
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
29
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () { return e[k]; }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
n.default = e;
|
|
37
|
+
return Object.freeze(n);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
41
|
+
var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
|
|
42
|
+
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
43
|
+
var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
|
|
44
|
+
var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
|
|
45
|
+
var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
|
|
46
|
+
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
47
|
+
|
|
48
|
+
var __defProp = Object.defineProperty;
|
|
49
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
50
|
+
var __esm = (fn, res) => function __init() {
|
|
51
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
52
|
+
};
|
|
53
|
+
var __export = (target, all) => {
|
|
54
|
+
for (var name in all)
|
|
55
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
56
|
+
};
|
|
57
|
+
function cn(...inputs) {
|
|
58
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
59
|
+
}
|
|
60
|
+
var init_utils = __esm({
|
|
61
|
+
"src/lib/utils.ts"() {
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// components/ui/button.tsx
|
|
66
|
+
var button_exports = {};
|
|
67
|
+
__export(button_exports, {
|
|
68
|
+
Button: () => Button2,
|
|
69
|
+
buttonVariants: () => buttonVariants
|
|
70
|
+
});
|
|
71
|
+
function Button2({
|
|
72
|
+
className,
|
|
73
|
+
variant,
|
|
74
|
+
size,
|
|
75
|
+
asChild = false,
|
|
76
|
+
...props
|
|
77
|
+
}) {
|
|
78
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
Comp,
|
|
81
|
+
{
|
|
82
|
+
"data-slot": "button",
|
|
83
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
84
|
+
...props
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
var buttonVariants;
|
|
89
|
+
var init_button = __esm({
|
|
90
|
+
"components/ui/button.tsx"() {
|
|
91
|
+
init_utils();
|
|
92
|
+
buttonVariants = classVarianceAuthority.cva(
|
|
93
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
94
|
+
{
|
|
95
|
+
variants: {
|
|
96
|
+
variant: {
|
|
97
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
98
|
+
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
99
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
100
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
101
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
102
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
103
|
+
},
|
|
104
|
+
size: {
|
|
105
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
106
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
107
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
108
|
+
icon: "size-9"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
defaultVariants: {
|
|
112
|
+
variant: "default",
|
|
113
|
+
size: "default"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
8
119
|
|
|
9
120
|
// src/core/reducer.ts
|
|
10
121
|
function cloneNode(node) {
|
|
11
122
|
return {
|
|
12
123
|
...node,
|
|
13
|
-
props: node.props ? { ...node.props } :
|
|
14
|
-
bindings: node.bindings ? { ...node.bindings } :
|
|
15
|
-
events: node.events ? { ...node.events } :
|
|
16
|
-
children: node.children
|
|
124
|
+
props: node.props ? { ...node.props } : null,
|
|
125
|
+
bindings: node.bindings ? { ...node.bindings } : null,
|
|
126
|
+
events: node.events ? { ...node.events } : null,
|
|
127
|
+
children: node.children ? node.children.map((child) => cloneNode(child)) : null
|
|
17
128
|
};
|
|
18
129
|
}
|
|
19
130
|
function findNodeById(tree, nodeId) {
|
|
@@ -57,18 +168,14 @@ function updateNodeById(tree, nodeId, updater) {
|
|
|
57
168
|
const parent = path[path.length - 2];
|
|
58
169
|
const updatedParent = {
|
|
59
170
|
...parent,
|
|
60
|
-
children: parent.children
|
|
171
|
+
children: parent.children ? parent.children.map(
|
|
61
172
|
(child) => child.id === nodeId ? updatedNode : child
|
|
62
|
-
)
|
|
173
|
+
) : null
|
|
63
174
|
};
|
|
64
175
|
if (path.length === 2) {
|
|
65
176
|
return updatedParent;
|
|
66
177
|
}
|
|
67
|
-
return updateNodeById(
|
|
68
|
-
result,
|
|
69
|
-
parent.id,
|
|
70
|
-
() => updatedParent
|
|
71
|
-
);
|
|
178
|
+
return updateNodeById(result, parent.id, () => updatedParent);
|
|
72
179
|
}
|
|
73
180
|
function replaceNodeById(tree, nodeId, newNode) {
|
|
74
181
|
return updateNodeById(tree, nodeId, () => newNode);
|
|
@@ -110,7 +217,7 @@ function removeNodeById(tree, nodeId) {
|
|
|
110
217
|
return result;
|
|
111
218
|
return updateNodeById(result, parent.id, (node) => ({
|
|
112
219
|
...node,
|
|
113
|
-
children: node.children
|
|
220
|
+
children: node.children ? node.children.filter((child) => child.id !== nodeId) : null
|
|
114
221
|
}));
|
|
115
222
|
}
|
|
116
223
|
function uiReducer(state, action) {
|
|
@@ -127,7 +234,7 @@ function uiReducer(state, action) {
|
|
|
127
234
|
...state,
|
|
128
235
|
layout: action.node,
|
|
129
236
|
loading: false,
|
|
130
|
-
error:
|
|
237
|
+
error: null
|
|
131
238
|
};
|
|
132
239
|
}
|
|
133
240
|
case "PARTIAL_UPDATE": {
|
|
@@ -136,7 +243,7 @@ function uiReducer(state, action) {
|
|
|
136
243
|
...state,
|
|
137
244
|
layout: action.node,
|
|
138
245
|
loading: false,
|
|
139
|
-
error:
|
|
246
|
+
error: null
|
|
140
247
|
};
|
|
141
248
|
}
|
|
142
249
|
if (action.nodeId === "root" || action.nodeId === state.layout.id) {
|
|
@@ -144,19 +251,23 @@ function uiReducer(state, action) {
|
|
|
144
251
|
...state,
|
|
145
252
|
layout: action.node,
|
|
146
253
|
loading: false,
|
|
147
|
-
error:
|
|
254
|
+
error: null
|
|
148
255
|
};
|
|
149
256
|
}
|
|
150
257
|
return {
|
|
151
258
|
...state,
|
|
152
259
|
layout: replaceNodeById(state.layout, action.nodeId, action.node),
|
|
153
260
|
loading: false,
|
|
154
|
-
error:
|
|
261
|
+
error: null
|
|
155
262
|
};
|
|
156
263
|
}
|
|
157
264
|
case "ADD_NODE": {
|
|
158
265
|
if (!state.layout) {
|
|
159
|
-
return
|
|
266
|
+
return {
|
|
267
|
+
...state,
|
|
268
|
+
error: "Cannot add node: Layout is empty.",
|
|
269
|
+
loading: false
|
|
270
|
+
};
|
|
160
271
|
}
|
|
161
272
|
return {
|
|
162
273
|
...state,
|
|
@@ -164,21 +275,41 @@ function uiReducer(state, action) {
|
|
|
164
275
|
state.layout,
|
|
165
276
|
action.parentId,
|
|
166
277
|
action.node,
|
|
167
|
-
action.index
|
|
278
|
+
action.index === null ? void 0 : action.index
|
|
168
279
|
),
|
|
169
280
|
loading: false,
|
|
170
|
-
error:
|
|
281
|
+
error: null
|
|
171
282
|
};
|
|
172
283
|
}
|
|
173
284
|
case "REMOVE_NODE": {
|
|
174
285
|
if (!state.layout) {
|
|
175
|
-
return
|
|
286
|
+
return {
|
|
287
|
+
...state,
|
|
288
|
+
error: "Cannot remove node: Layout is empty.",
|
|
289
|
+
loading: false
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
return {
|
|
294
|
+
...state,
|
|
295
|
+
layout: removeNodeById(state.layout, action.nodeId),
|
|
296
|
+
loading: false,
|
|
297
|
+
error: null
|
|
298
|
+
};
|
|
299
|
+
} catch (e) {
|
|
300
|
+
const errorMessage = e instanceof Error ? e.message : "Failed to remove node.";
|
|
301
|
+
return {
|
|
302
|
+
...state,
|
|
303
|
+
error: errorMessage,
|
|
304
|
+
loading: false
|
|
305
|
+
};
|
|
176
306
|
}
|
|
307
|
+
}
|
|
308
|
+
case "ERROR": {
|
|
177
309
|
return {
|
|
178
310
|
...state,
|
|
179
|
-
|
|
180
|
-
loading: false
|
|
181
|
-
error: void 0
|
|
311
|
+
error: action.message,
|
|
312
|
+
loading: false
|
|
182
313
|
};
|
|
183
314
|
}
|
|
184
315
|
case "LOADING": {
|
|
@@ -187,23 +318,84 @@ function uiReducer(state, action) {
|
|
|
187
318
|
loading: action.isLoading
|
|
188
319
|
};
|
|
189
320
|
}
|
|
190
|
-
case "ERROR": {
|
|
191
|
-
return {
|
|
192
|
-
...state,
|
|
193
|
-
error: action.message,
|
|
194
|
-
loading: false
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
321
|
default:
|
|
198
322
|
return state;
|
|
199
323
|
}
|
|
200
324
|
}
|
|
201
325
|
var initialState = {
|
|
202
|
-
|
|
203
|
-
|
|
326
|
+
layout: null,
|
|
327
|
+
loading: false,
|
|
328
|
+
history: [],
|
|
329
|
+
error: null
|
|
204
330
|
};
|
|
205
331
|
|
|
206
332
|
// src/core/action-router.ts
|
|
333
|
+
var UI_GUIDANCE_BASE = `
|
|
334
|
+
UI Guidance:
|
|
335
|
+
1. Create a focused interface that directly addresses the goal
|
|
336
|
+
2. Use appropriate UI patterns (lists, forms, details, etc.)
|
|
337
|
+
3. Include navigation between related views when needed
|
|
338
|
+
4. Keep the interface simple and intuitive
|
|
339
|
+
5. Bind to schema data where appropriate
|
|
340
|
+
6. Provide event handlers for user interactions - make sure to always include both action and target properties`;
|
|
341
|
+
var LIST_BINDING_GUIDANCE = `7. **CRITICAL:** For \`ListView\` or \`Table\` nodes, the \`data\` binding key **MUST** point to the *exact path* of the data *array* within the context.`;
|
|
342
|
+
var LIST_BINDING_EXAMPLE = `Example: If the context has \`{ tasks: { data: [...] } }\`, the binding **MUST** be \`{ "bindings": { "data": "tasks.data" } }\`. If the context has \`{ userList: [...] }\`, the binding **MUST** be \`{ "bindings": { "data": "userList" } }\`. **NEVER** bind to the parent object containing the array (e.g., DO NOT USE \`{ "bindings": { "data": "tasks" } }\`).`;
|
|
343
|
+
var COMMON_UI_GUIDANCE = UI_GUIDANCE_BASE + "\n" + // Add a newline separator
|
|
344
|
+
LIST_BINDING_GUIDANCE + // Add the specific list binding rule
|
|
345
|
+
"\n" + // Add a newline separator
|
|
346
|
+
LIST_BINDING_EXAMPLE;
|
|
347
|
+
function processTemplate(template, values) {
|
|
348
|
+
return template.replace(/\${(.*?)}/g, (match, key) => {
|
|
349
|
+
const trimmedKey = key.trim();
|
|
350
|
+
return trimmedKey in values ? String(values[trimmedKey]) : match;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function buildPrompt(input, promptTemplate, templateValues) {
|
|
354
|
+
const { schema, goal, history, userContext } = input;
|
|
355
|
+
const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
|
|
356
|
+
const schemaString = typeof tableSchema === "object" && tableSchema !== null ? JSON.stringify(tableSchema) : String(tableSchema);
|
|
357
|
+
return `Table: ${tableName}
|
|
358
|
+
Schema: ${schemaString}`;
|
|
359
|
+
}).join("\n\n");
|
|
360
|
+
const recentEvents = history && history.length > 0 ? history.slice(-5).map(
|
|
361
|
+
(event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
|
|
362
|
+
).join("\n") : "No recent events";
|
|
363
|
+
const userContextSection = userContext ? `
|
|
364
|
+
|
|
365
|
+
User Context:
|
|
366
|
+
${JSON.stringify(userContext)}` : "";
|
|
367
|
+
if (promptTemplate && templateValues) {
|
|
368
|
+
const fullTemplateValues = {
|
|
369
|
+
...templateValues,
|
|
370
|
+
schemaInfo,
|
|
371
|
+
recentEvents,
|
|
372
|
+
userContextString: userContextSection.trim(),
|
|
373
|
+
// Use trimmed version
|
|
374
|
+
commonUIGuidance: COMMON_UI_GUIDANCE,
|
|
375
|
+
goal
|
|
376
|
+
// Ensure goal is always available to templates
|
|
377
|
+
};
|
|
378
|
+
return processTemplate(promptTemplate, fullTemplateValues);
|
|
379
|
+
}
|
|
380
|
+
const interactionDescription = history && history.length > 0 ? `The user's last action was: ${history[history.length - 1].type} on node ${history[history.length - 1].nodeId}` : "The user initiated the session for the goal";
|
|
381
|
+
return `
|
|
382
|
+
You are an expert UI generator.
|
|
383
|
+
Create a user interface that achieves the following goal: "${goal}".
|
|
384
|
+
${interactionDescription}.
|
|
385
|
+
|
|
386
|
+
Available data schema:
|
|
387
|
+
${schemaInfo}
|
|
388
|
+
|
|
389
|
+
Recent user interactions:
|
|
390
|
+
${recentEvents}${userContextSection}
|
|
391
|
+
|
|
392
|
+
Generate a complete UI specification in JSON format that matches the following TypeScript type:
|
|
393
|
+
type UISpecNode = { id: string; node_type: string; props?: Record<string, unknown>; bindings?: Record<string, unknown>; events?: Record<string, { action: string; target: string; payload?: Record<string, unknown>; }>; children?: UISpecNode[]; };
|
|
394
|
+
${COMMON_UI_GUIDANCE}
|
|
395
|
+
|
|
396
|
+
Respond ONLY with the JSON UI specification and no other text.
|
|
397
|
+
`;
|
|
398
|
+
}
|
|
207
399
|
var ActionType = /* @__PURE__ */ ((ActionType2) => {
|
|
208
400
|
ActionType2["FULL_REFRESH"] = "FULL_REFRESH";
|
|
209
401
|
ActionType2["UPDATE_NODE"] = "UPDATE_NODE";
|
|
@@ -238,19 +430,39 @@ var ActionRouter = class {
|
|
|
238
430
|
* @returns Route resolution or null if no match
|
|
239
431
|
*/
|
|
240
432
|
resolveRoute(event, schema, layout, dataContext, goal, userContext) {
|
|
433
|
+
console.log(
|
|
434
|
+
`[ActionRouter Debug] resolveRoute called for event type: ${event.type}`
|
|
435
|
+
);
|
|
241
436
|
const routes = this.routes[event.type] || [];
|
|
437
|
+
console.log(
|
|
438
|
+
`[ActionRouter Debug] Found ${routes.length} routes for ${event.type}`
|
|
439
|
+
);
|
|
242
440
|
if (routes.length === 0) {
|
|
243
|
-
|
|
441
|
+
console.log(
|
|
442
|
+
`[ActionRouter Debug] No specific route found for ${event.type}, using default FULL_REFRESH.`
|
|
443
|
+
);
|
|
444
|
+
const defaultPlannerInput = {
|
|
445
|
+
schema,
|
|
446
|
+
goal,
|
|
447
|
+
history: [event],
|
|
448
|
+
userContext: userContext || null
|
|
449
|
+
};
|
|
450
|
+
const defaultPrompt = buildPrompt(
|
|
451
|
+
defaultPlannerInput,
|
|
452
|
+
void 0,
|
|
453
|
+
void 0
|
|
454
|
+
);
|
|
455
|
+
const defaultResolution = {
|
|
244
456
|
actionType: "FULL_REFRESH" /* FULL_REFRESH */,
|
|
245
457
|
targetNodeId: layout?.id || "root",
|
|
246
|
-
plannerInput:
|
|
247
|
-
|
|
248
|
-
goal,
|
|
249
|
-
history: [event],
|
|
250
|
-
userContext
|
|
251
|
-
},
|
|
252
|
-
prompt: `Generate a new UI for the goal: "${goal}". The user just triggered: ${event.type} on node ${event.nodeId}`
|
|
458
|
+
plannerInput: defaultPlannerInput,
|
|
459
|
+
prompt: defaultPrompt
|
|
253
460
|
};
|
|
461
|
+
console.log(
|
|
462
|
+
"[ActionRouter Debug] Default Resolution:",
|
|
463
|
+
defaultResolution
|
|
464
|
+
);
|
|
465
|
+
return defaultResolution;
|
|
254
466
|
}
|
|
255
467
|
const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
|
|
256
468
|
const nodeConfig = sourceNode?.events?.[event.type];
|
|
@@ -263,6 +475,7 @@ var ActionRouter = class {
|
|
|
263
475
|
if (!matchingRoute) {
|
|
264
476
|
matchingRoute = routes[0];
|
|
265
477
|
}
|
|
478
|
+
console.log("[ActionRouter Debug] Matching Route Config:", matchingRoute);
|
|
266
479
|
const targetNodeId = nodeConfig?.target || matchingRoute.targetNodeId || event.nodeId;
|
|
267
480
|
const additionalContext = {};
|
|
268
481
|
if (matchingRoute.contextKeys) {
|
|
@@ -296,23 +509,35 @@ var ActionRouter = class {
|
|
|
296
509
|
...additionalContext
|
|
297
510
|
}
|
|
298
511
|
};
|
|
299
|
-
const
|
|
512
|
+
const templateValues = {
|
|
513
|
+
goal,
|
|
514
|
+
eventType: event.type,
|
|
515
|
+
nodeId: event.nodeId,
|
|
516
|
+
targetNodeId,
|
|
517
|
+
actionType: matchingRoute.actionType,
|
|
518
|
+
...userContext || {},
|
|
519
|
+
// Spread the original userContext (passed to resolveRoute)
|
|
520
|
+
...additionalContext
|
|
521
|
+
// Spread additionalContext afterwards (can override userContext keys)
|
|
522
|
+
};
|
|
523
|
+
console.log("[ActionRouter Debug] Template Values:", templateValues);
|
|
524
|
+
const finalPrompt = buildPrompt(
|
|
525
|
+
plannerInput2,
|
|
300
526
|
matchingRoute.promptTemplate,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
nodeId: event.nodeId,
|
|
305
|
-
targetNodeId,
|
|
306
|
-
actionType: matchingRoute.actionType,
|
|
307
|
-
...additionalContext
|
|
308
|
-
}
|
|
527
|
+
// Pass template if it exists (can be undefined)
|
|
528
|
+
templateValues
|
|
529
|
+
// Pass templateValues (used only if promptTemplate exists)
|
|
309
530
|
);
|
|
310
|
-
|
|
531
|
+
console.log("[ActionRouter Debug] Generated Prompt:", finalPrompt);
|
|
532
|
+
const finalResolution = {
|
|
311
533
|
actionType: matchingRoute.actionType,
|
|
312
534
|
targetNodeId,
|
|
313
535
|
plannerInput: plannerInput2,
|
|
314
|
-
prompt
|
|
536
|
+
prompt: finalPrompt
|
|
537
|
+
// Use the generated prompt
|
|
315
538
|
};
|
|
539
|
+
console.log("[ActionRouter Debug] Final Resolution:", finalResolution);
|
|
540
|
+
return finalResolution;
|
|
316
541
|
}
|
|
317
542
|
/**
|
|
318
543
|
* Process a prompt template with variables
|
|
@@ -330,8 +555,11 @@ function createDefaultRouter() {
|
|
|
330
555
|
const router = new ActionRouter();
|
|
331
556
|
router.registerRoute("CLICK", {
|
|
332
557
|
actionType: "FULL_REFRESH" /* FULL_REFRESH */,
|
|
333
|
-
targetNodeId: "root"
|
|
334
|
-
|
|
558
|
+
targetNodeId: "root"
|
|
559
|
+
});
|
|
560
|
+
router.registerRoute("INIT", {
|
|
561
|
+
actionType: "FULL_REFRESH" /* FULL_REFRESH */,
|
|
562
|
+
targetNodeId: "root"
|
|
335
563
|
});
|
|
336
564
|
router.registerRoute("CLICK", {
|
|
337
565
|
actionType: "SHOW_DETAIL" /* SHOW_DETAIL */,
|
|
@@ -361,7 +589,31 @@ function createDefaultRouter() {
|
|
|
361
589
|
});
|
|
362
590
|
return router;
|
|
363
591
|
}
|
|
592
|
+
var componentType = zod.z.enum([
|
|
593
|
+
// Layout components
|
|
594
|
+
"Container",
|
|
595
|
+
"Card",
|
|
596
|
+
"Header",
|
|
597
|
+
// Input components
|
|
598
|
+
"Button",
|
|
599
|
+
"Input",
|
|
600
|
+
"Select",
|
|
601
|
+
"Textarea",
|
|
602
|
+
"Checkbox",
|
|
603
|
+
"RadioGroup",
|
|
604
|
+
// Data display components
|
|
605
|
+
"ListView",
|
|
606
|
+
"Detail",
|
|
607
|
+
"Tabs",
|
|
608
|
+
"Dialog",
|
|
609
|
+
// Typography
|
|
610
|
+
"Heading",
|
|
611
|
+
"Text"
|
|
612
|
+
]);
|
|
613
|
+
|
|
614
|
+
// src/schema/ui.ts
|
|
364
615
|
var uiEventType = zod.z.enum([
|
|
616
|
+
"INIT",
|
|
365
617
|
"CLICK",
|
|
366
618
|
"CHANGE",
|
|
367
619
|
"SUBMIT",
|
|
@@ -373,27 +625,62 @@ var uiEventType = zod.z.enum([
|
|
|
373
625
|
var uiEvent = zod.z.object({
|
|
374
626
|
type: uiEventType,
|
|
375
627
|
nodeId: zod.z.string(),
|
|
376
|
-
timestamp: zod.z.number().
|
|
377
|
-
payload: zod.z.record(zod.z.
|
|
628
|
+
timestamp: zod.z.number().nullable(),
|
|
629
|
+
payload: zod.z.record(zod.z.unknown()).nullable()
|
|
378
630
|
});
|
|
379
|
-
zod.z.enum([
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
var
|
|
631
|
+
zod.z.enum(["AI_RESPONSE", "ERROR"]);
|
|
632
|
+
var runtimeRecord = zod.z.record(zod.z.any()).nullable();
|
|
633
|
+
var openAISimplifiedValue = zod.z.string().nullable();
|
|
634
|
+
var openAIRecordSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
635
|
+
var openAIEventPayloadSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
636
|
+
var openAIBaseNode = zod.z.object({
|
|
637
|
+
id: zod.z.string().describe("Unique identifier for the UI node."),
|
|
638
|
+
node_type: componentType.describe(
|
|
639
|
+
"The type of UI component (e.g., Container, Text, Button, ListView)."
|
|
640
|
+
),
|
|
641
|
+
props: openAIRecordSimplifiedNullable.describe(
|
|
642
|
+
'Component-specific properties (attributes). Values should be strings or null. E.g., for Header use { "title": "My Title" }; for Text use { "text": "My Text" }.'
|
|
643
|
+
),
|
|
644
|
+
bindings: openAIRecordSimplifiedNullable.describe(
|
|
645
|
+
'Data bindings map context paths to component props. Values are paths (e.g., "user.name") or templates (e.g., "{{item.title}}"). **CRITICAL for ListView/Table:** the `data` key MUST point to the *exact array path* (e.g., { "data": "tasks.data" } or { "data": "userList" }), NOT the parent object.'
|
|
646
|
+
),
|
|
647
|
+
events: zod.z.record(
|
|
648
|
+
zod.z.string(),
|
|
649
|
+
zod.z.object({
|
|
650
|
+
action: zod.z.string(),
|
|
651
|
+
target: zod.z.string(),
|
|
652
|
+
payload: openAIEventPayloadSimplifiedNullable
|
|
653
|
+
})
|
|
654
|
+
).nullable(),
|
|
655
|
+
// Entire events object is nullable
|
|
656
|
+
children: zod.z.null()
|
|
657
|
+
// Base children are null. When extended, it will be an array or null.
|
|
658
|
+
});
|
|
659
|
+
var openAINodeL4 = openAIBaseNode;
|
|
660
|
+
var openAINodeL3 = openAIBaseNode.extend({
|
|
661
|
+
children: zod.z.array(openAINodeL4).nullable()
|
|
662
|
+
});
|
|
663
|
+
var openAINodeL2 = openAIBaseNode.extend({
|
|
664
|
+
children: zod.z.array(openAINodeL3).nullable()
|
|
665
|
+
});
|
|
666
|
+
var openAIUISpec = openAIBaseNode.extend({
|
|
667
|
+
children: zod.z.array(openAINodeL2).nullable()
|
|
668
|
+
});
|
|
669
|
+
var uiSpecNode = zod.z.object({
|
|
384
670
|
id: zod.z.string(),
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
671
|
+
node_type: zod.z.string(),
|
|
672
|
+
props: runtimeRecord,
|
|
673
|
+
bindings: runtimeRecord,
|
|
674
|
+
events: zod.z.record(
|
|
675
|
+
zod.z.string(),
|
|
676
|
+
zod.z.object({
|
|
677
|
+
action: zod.z.string(),
|
|
678
|
+
target: zod.z.string(),
|
|
679
|
+
payload: runtimeRecord
|
|
680
|
+
})
|
|
681
|
+
).nullable(),
|
|
682
|
+
children: zod.z.lazy(() => zod.z.array(uiSpecNode)).nullable()
|
|
683
|
+
});
|
|
397
684
|
zod.z.discriminatedUnion("type", [
|
|
398
685
|
zod.z.object({
|
|
399
686
|
type: zod.z.literal("UI_EVENT"),
|
|
@@ -412,7 +699,7 @@ zod.z.discriminatedUnion("type", [
|
|
|
412
699
|
type: zod.z.literal("ADD_NODE"),
|
|
413
700
|
parentId: zod.z.string(),
|
|
414
701
|
node: uiSpecNode,
|
|
415
|
-
index: zod.z.number().
|
|
702
|
+
index: zod.z.number().nullable()
|
|
416
703
|
}),
|
|
417
704
|
zod.z.object({
|
|
418
705
|
type: zod.z.literal("REMOVE_NODE"),
|
|
@@ -428,16 +715,16 @@ zod.z.discriminatedUnion("type", [
|
|
|
428
715
|
})
|
|
429
716
|
]);
|
|
430
717
|
zod.z.object({
|
|
431
|
-
layout: uiSpecNode.
|
|
718
|
+
layout: uiSpecNode.nullable(),
|
|
432
719
|
loading: zod.z.boolean(),
|
|
433
720
|
history: zod.z.array(uiEvent),
|
|
434
|
-
error: zod.z.string().
|
|
721
|
+
error: zod.z.string().nullable()
|
|
435
722
|
});
|
|
436
723
|
zod.z.object({
|
|
437
724
|
schema: zod.z.record(zod.z.unknown()),
|
|
438
725
|
goal: zod.z.string(),
|
|
439
|
-
history: zod.z.array(uiEvent).
|
|
440
|
-
userContext: zod.z.record(zod.z.unknown()).optional()
|
|
726
|
+
history: zod.z.array(uiEvent).nullable(),
|
|
727
|
+
userContext: zod.z.record(zod.z.unknown()).nullable().optional()
|
|
441
728
|
});
|
|
442
729
|
|
|
443
730
|
// src/core/system-events.ts
|
|
@@ -463,7 +750,7 @@ var SystemEventManager = class {
|
|
|
463
750
|
}
|
|
464
751
|
/**
|
|
465
752
|
* Register a listener for a specific system event type
|
|
466
|
-
*
|
|
753
|
+
*
|
|
467
754
|
* @param eventType - The system event type to listen for
|
|
468
755
|
* @param listener - The listener function
|
|
469
756
|
* @returns Function to unregister the listener
|
|
@@ -483,7 +770,7 @@ var SystemEventManager = class {
|
|
|
483
770
|
}
|
|
484
771
|
/**
|
|
485
772
|
* Emit a system event to all registered listeners
|
|
486
|
-
*
|
|
773
|
+
*
|
|
487
774
|
* @param event - The system event to emit
|
|
488
775
|
*/
|
|
489
776
|
async emit(event) {
|
|
@@ -503,268 +790,917 @@ function createSystemEvent(type, data) {
|
|
|
503
790
|
}
|
|
504
791
|
|
|
505
792
|
// src/env.ts
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
//
|
|
510
|
-
|
|
793
|
+
var rawApiKeyFromEnv = process.env.VITE_OPENAI_API_KEY;
|
|
794
|
+
var env = {
|
|
795
|
+
MOCK_PLANNER: process.env.VITE_MOCK_PLANNER || "0",
|
|
796
|
+
// Simplified MOCK_PLANNER assignment
|
|
797
|
+
NODE_ENV: process.env.VITE_NODE_ENV || "production",
|
|
798
|
+
// Simplified NODE_ENV assignment
|
|
799
|
+
OPENAI_API_KEY: rawApiKeyFromEnv || ""
|
|
800
|
+
};
|
|
511
801
|
|
|
512
802
|
// src/core/planner.ts
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
})
|
|
519
|
-
const recentEvents = history?.slice(-5).map(
|
|
520
|
-
(event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
|
|
521
|
-
).join("\n") || "No recent events";
|
|
522
|
-
const userContextSection = userContext ? `
|
|
523
|
-
|
|
524
|
-
User Context:
|
|
525
|
-
${JSON.stringify(userContext)}` : "";
|
|
526
|
-
return `
|
|
527
|
-
You are an expert UI generator.
|
|
528
|
-
Create a user interface that achieves the following goal: "${goal}"
|
|
529
|
-
|
|
530
|
-
Available data schema:
|
|
531
|
-
${schemaInfo}
|
|
532
|
-
|
|
533
|
-
Recent user interactions:
|
|
534
|
-
${recentEvents}${userContextSection}
|
|
535
|
-
|
|
536
|
-
Generate a complete UI specification in JSON format that matches the following TypeScript type:
|
|
537
|
-
type UISpecNode = {
|
|
538
|
-
id: string;
|
|
539
|
-
type: string;
|
|
540
|
-
props?: Record<string, any>;
|
|
541
|
-
bindings?: Record<string, any>;
|
|
542
|
-
events?: Record<string, { action: string; target?: string; payload?: Record<string, any>; }>;
|
|
543
|
-
children?: UISpecNode[];
|
|
803
|
+
var getOpenAIClient = (apiKey) => {
|
|
804
|
+
return openai.createOpenAI({
|
|
805
|
+
apiKey,
|
|
806
|
+
// Use the provided key directly
|
|
807
|
+
compatibility: "strict"
|
|
808
|
+
});
|
|
544
809
|
};
|
|
545
|
-
|
|
546
|
-
UI Guidance:
|
|
547
|
-
1. Create a focused interface that directly addresses the goal
|
|
548
|
-
2. Use appropriate UI patterns (lists, forms, details, etc.)
|
|
549
|
-
3. Include navigation between related views when needed
|
|
550
|
-
4. Keep the interface simple and intuitive
|
|
551
|
-
5. Bind to schema data where appropriate
|
|
552
|
-
6. Provide event handlers for user interactions
|
|
553
|
-
|
|
554
|
-
Respond ONLY with the JSON UI specification and no other text.
|
|
555
|
-
`;
|
|
556
|
-
}
|
|
557
810
|
function mockPlanner(input, targetNodeId, customPrompt) {
|
|
811
|
+
if (customPrompt) {
|
|
812
|
+
console.log("mockPlanner received customPrompt:", customPrompt);
|
|
813
|
+
}
|
|
814
|
+
const taskSchema = input.schema.tasks;
|
|
815
|
+
const taskData = taskSchema?.sampleData || [
|
|
816
|
+
{
|
|
817
|
+
id: "1",
|
|
818
|
+
title: "Example Task 1",
|
|
819
|
+
description: "This is a sample task",
|
|
820
|
+
status: "pending",
|
|
821
|
+
priority: "medium"
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
id: "2",
|
|
825
|
+
title: "Example Task 2",
|
|
826
|
+
description: "Another sample task",
|
|
827
|
+
status: "completed",
|
|
828
|
+
priority: "high"
|
|
829
|
+
}
|
|
830
|
+
];
|
|
558
831
|
const mockNode = {
|
|
559
832
|
id: targetNodeId || "root",
|
|
560
|
-
|
|
561
|
-
props: {
|
|
833
|
+
node_type: "Container",
|
|
834
|
+
props: {
|
|
835
|
+
className: "p-4 space-y-6"
|
|
836
|
+
},
|
|
837
|
+
bindings: null,
|
|
838
|
+
events: null,
|
|
562
839
|
children: [
|
|
563
840
|
{
|
|
564
|
-
id: "
|
|
565
|
-
|
|
566
|
-
props: {
|
|
841
|
+
id: "header-1",
|
|
842
|
+
node_type: "Header",
|
|
843
|
+
props: {
|
|
844
|
+
title: "Task Management Dashboard",
|
|
845
|
+
className: "mb-4"
|
|
846
|
+
},
|
|
847
|
+
bindings: null,
|
|
848
|
+
events: null,
|
|
849
|
+
children: null
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
id: "main-content",
|
|
853
|
+
node_type: "Container",
|
|
854
|
+
props: {
|
|
855
|
+
className: "grid grid-cols-1 gap-6 md:grid-cols-3"
|
|
856
|
+
},
|
|
857
|
+
bindings: null,
|
|
858
|
+
events: null,
|
|
859
|
+
children: [
|
|
860
|
+
{
|
|
861
|
+
id: "tasks-container",
|
|
862
|
+
node_type: "Container",
|
|
863
|
+
props: {
|
|
864
|
+
className: "md:col-span-2"
|
|
865
|
+
},
|
|
866
|
+
bindings: null,
|
|
867
|
+
events: null,
|
|
868
|
+
children: [
|
|
869
|
+
{
|
|
870
|
+
id: "list-heading",
|
|
871
|
+
node_type: "Container",
|
|
872
|
+
props: {
|
|
873
|
+
className: "flex justify-between items-center mb-4"
|
|
874
|
+
},
|
|
875
|
+
bindings: null,
|
|
876
|
+
events: null,
|
|
877
|
+
children: [
|
|
878
|
+
{
|
|
879
|
+
id: "list-title",
|
|
880
|
+
node_type: "Header",
|
|
881
|
+
props: {
|
|
882
|
+
title: "Tasks",
|
|
883
|
+
className: "border-none p-0 m-0"
|
|
884
|
+
},
|
|
885
|
+
bindings: null,
|
|
886
|
+
events: null,
|
|
887
|
+
children: null
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
id: "add-task-button",
|
|
891
|
+
node_type: "Button",
|
|
892
|
+
props: {
|
|
893
|
+
label: "Add Task",
|
|
894
|
+
variant: "default"
|
|
895
|
+
},
|
|
896
|
+
bindings: null,
|
|
897
|
+
events: {
|
|
898
|
+
onClick: {
|
|
899
|
+
action: "ADD_TASK",
|
|
900
|
+
target: "tasks-container",
|
|
901
|
+
payload: {}
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
children: null
|
|
905
|
+
}
|
|
906
|
+
]
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
id: "task-list",
|
|
910
|
+
node_type: "ListView",
|
|
911
|
+
props: {
|
|
912
|
+
selectable: "true"
|
|
913
|
+
},
|
|
914
|
+
bindings: {
|
|
915
|
+
data: "tasks.data",
|
|
916
|
+
fields: JSON.stringify([
|
|
917
|
+
{ key: "id", label: "ID" },
|
|
918
|
+
{ key: "title", label: "Title" },
|
|
919
|
+
{ key: "status", label: "Status" },
|
|
920
|
+
{ key: "priority", label: "Priority" }
|
|
921
|
+
])
|
|
922
|
+
},
|
|
923
|
+
events: {
|
|
924
|
+
onSelect: {
|
|
925
|
+
action: "SELECT_TASK",
|
|
926
|
+
target: "task-detail",
|
|
927
|
+
payload: {
|
|
928
|
+
source: "task-list"
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
children: null
|
|
933
|
+
}
|
|
934
|
+
]
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
id: "task-detail",
|
|
938
|
+
node_type: "Detail",
|
|
939
|
+
props: {
|
|
940
|
+
title: "Task Details",
|
|
941
|
+
visible: "true"
|
|
942
|
+
},
|
|
943
|
+
bindings: {
|
|
944
|
+
data: JSON.stringify(taskData[0]),
|
|
945
|
+
fields: JSON.stringify([
|
|
946
|
+
{ key: "title", label: "Title", type: "heading" },
|
|
947
|
+
{ key: "description", label: "Description", type: "content" },
|
|
948
|
+
{ key: "status", label: "Status" },
|
|
949
|
+
{ key: "priority", label: "Priority" },
|
|
950
|
+
{ key: "dueDate", label: "Due Date" }
|
|
951
|
+
])
|
|
952
|
+
},
|
|
953
|
+
events: {
|
|
954
|
+
onBack: {
|
|
955
|
+
action: "CLOSE_DETAIL",
|
|
956
|
+
target: "task-detail",
|
|
957
|
+
payload: {}
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
children: null
|
|
961
|
+
}
|
|
962
|
+
]
|
|
567
963
|
}
|
|
568
964
|
]
|
|
569
965
|
};
|
|
570
966
|
return mockNode;
|
|
571
967
|
}
|
|
968
|
+
async function callPlannerLLM(input, openaiApiKey, routeResolution) {
|
|
969
|
+
await systemEvents.emit(
|
|
970
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
|
|
971
|
+
);
|
|
972
|
+
if (env.MOCK_PLANNER === "1") {
|
|
973
|
+
console.warn(
|
|
974
|
+
`Using mock planner because MOCK_PLANNER environment variable is set to "1".`
|
|
975
|
+
);
|
|
976
|
+
return mockPlanner(input);
|
|
977
|
+
}
|
|
978
|
+
if (!openaiApiKey) {
|
|
979
|
+
console.warn(
|
|
980
|
+
`OpenAI API key was not provided to callPlannerLLM. Falling back to mock planner.`
|
|
981
|
+
);
|
|
982
|
+
return mockPlanner(input);
|
|
983
|
+
}
|
|
984
|
+
const startTime = Date.now();
|
|
985
|
+
const prompt = routeResolution?.prompt;
|
|
986
|
+
if (!prompt) {
|
|
987
|
+
throw new Error("ActionRouter did not provide a prompt to callPlannerLLM.");
|
|
988
|
+
}
|
|
989
|
+
await systemEvents.emit(
|
|
990
|
+
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
991
|
+
);
|
|
992
|
+
try {
|
|
993
|
+
const { object: uiSpec } = await ai.generateObject({
|
|
994
|
+
model: getOpenAIClient(openaiApiKey)("gpt-4o", {
|
|
995
|
+
structuredOutputs: true
|
|
996
|
+
}),
|
|
997
|
+
schema: openAIUISpec,
|
|
998
|
+
messages: [{ role: "user", content: prompt }],
|
|
999
|
+
temperature: 0.2,
|
|
1000
|
+
maxTokens: 4e3
|
|
1001
|
+
});
|
|
1002
|
+
await systemEvents.emit(
|
|
1003
|
+
createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
|
|
1004
|
+
layout: uiSpec,
|
|
1005
|
+
executionTimeMs: Date.now() - startTime
|
|
1006
|
+
})
|
|
1007
|
+
);
|
|
1008
|
+
return uiSpec;
|
|
1009
|
+
} catch (error) {
|
|
1010
|
+
console.error("Error calling LLM planner:", error);
|
|
1011
|
+
await systemEvents.emit(
|
|
1012
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
1013
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
1014
|
+
})
|
|
1015
|
+
);
|
|
1016
|
+
throw error;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
async function processEvent(event, router, schema, layout, dataContext, goal, userContext, openaiApiKey) {
|
|
1020
|
+
const routeResolution = await router.resolveRoute(
|
|
1021
|
+
event,
|
|
1022
|
+
schema,
|
|
1023
|
+
layout || null,
|
|
1024
|
+
dataContext,
|
|
1025
|
+
goal,
|
|
1026
|
+
userContext
|
|
1027
|
+
);
|
|
1028
|
+
if (!routeResolution) {
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
`No route found for event type: ${event.type}, node: ${event.nodeId}`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
if (routeResolution.actionType.toString() === "NoOp") {
|
|
1034
|
+
if (!layout)
|
|
1035
|
+
throw new Error("Layout is undefined and action is NoOp");
|
|
1036
|
+
return layout;
|
|
1037
|
+
}
|
|
1038
|
+
const plannerInputForLLM = routeResolution.plannerInput;
|
|
1039
|
+
const newLayout = await callPlannerLLM(
|
|
1040
|
+
plannerInputForLLM,
|
|
1041
|
+
openaiApiKey || "",
|
|
1042
|
+
routeResolution
|
|
1043
|
+
);
|
|
1044
|
+
return newLayout;
|
|
1045
|
+
}
|
|
572
1046
|
|
|
573
1047
|
// src/core/state.ts
|
|
574
|
-
var useChat = (config) => ({
|
|
575
|
-
append: async (message) => {
|
|
576
|
-
},
|
|
577
|
-
data: { content: "{}" },
|
|
578
|
-
isLoading: false,
|
|
579
|
-
error: null,
|
|
580
|
-
stop: () => {
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
1048
|
function useUIStateEngine({
|
|
584
1049
|
schema,
|
|
585
1050
|
goal,
|
|
1051
|
+
openaiApiKey,
|
|
586
1052
|
userContext,
|
|
587
1053
|
mockMode = false,
|
|
588
|
-
planningConfig
|
|
1054
|
+
planningConfig,
|
|
589
1055
|
router = createDefaultRouter(),
|
|
590
1056
|
dataContext = {},
|
|
591
1057
|
enablePartialUpdates = false
|
|
592
1058
|
}) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
state.layout,
|
|
603
|
-
dataContext,
|
|
604
|
-
goal,
|
|
605
|
-
userContext
|
|
606
|
-
);
|
|
607
|
-
if (route) {
|
|
608
|
-
console.log("Resolved route:", route);
|
|
609
|
-
systemEvents.emit(
|
|
610
|
-
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
611
|
-
plannerInput: route.plannerInput
|
|
612
|
-
})
|
|
613
|
-
);
|
|
614
|
-
if (mockMode) {
|
|
615
|
-
const node = mockPlanner(route.plannerInput, route.targetNodeId, route.prompt);
|
|
616
|
-
switch (route.actionType) {
|
|
617
|
-
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
618
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
619
|
-
break;
|
|
620
|
-
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
621
|
-
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
622
|
-
case "HIDE_DETAIL" /* HIDE_DETAIL */:
|
|
623
|
-
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
624
|
-
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
625
|
-
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
626
|
-
case "NAVIGATE" /* NAVIGATE */:
|
|
627
|
-
dispatch({
|
|
628
|
-
type: "PARTIAL_UPDATE",
|
|
629
|
-
nodeId: route.targetNodeId,
|
|
630
|
-
node
|
|
631
|
-
});
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
634
|
-
} else {
|
|
635
|
-
const prompt = route.prompt;
|
|
636
|
-
systemEvents.emit(
|
|
637
|
-
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
638
|
-
);
|
|
639
|
-
append({
|
|
640
|
-
content: prompt,
|
|
641
|
-
role: "user"
|
|
642
|
-
});
|
|
643
|
-
sessionStorage.setItem("currentRoute", JSON.stringify({
|
|
644
|
-
actionType: route.actionType,
|
|
645
|
-
targetNodeId: route.targetNodeId
|
|
646
|
-
}));
|
|
647
|
-
}
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
const input = {
|
|
652
|
-
schema,
|
|
653
|
-
goal,
|
|
654
|
-
history: [...state.history, event],
|
|
655
|
-
userContext
|
|
656
|
-
};
|
|
657
|
-
if (mockMode) {
|
|
658
|
-
const node = mockPlanner();
|
|
659
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
660
|
-
} else {
|
|
661
|
-
const prompt = buildPrompt(input);
|
|
662
|
-
append({
|
|
663
|
-
content: prompt,
|
|
664
|
-
role: "user"
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
}, [append, goal, schema, state.history, state.layout, stop, userContext, router, mockMode, dataContext, enablePartialUpdates]);
|
|
668
|
-
react.useEffect(() => {
|
|
669
|
-
if (isLoading) {
|
|
1059
|
+
if (userContext === null) {
|
|
1060
|
+
console.warn(
|
|
1061
|
+
"useUIStateEngine: userContext was explicitly set to null. This is an allowed but discouraged value. Consider using undefined if you intend to omit the user context."
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
const [state, dispatch] = React.useReducer(uiReducer, initialState);
|
|
1065
|
+
const handleEvent = React.useCallback(
|
|
1066
|
+
async (event) => {
|
|
1067
|
+
dispatch({ type: "UI_EVENT", event });
|
|
670
1068
|
dispatch({ type: "LOADING", isLoading: true });
|
|
671
|
-
} else if (error) {
|
|
672
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
673
|
-
dispatch({ type: "ERROR", message: errorMessage });
|
|
674
|
-
systemEvents.emit(
|
|
675
|
-
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
676
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
677
|
-
})
|
|
678
|
-
);
|
|
679
|
-
} else if (data.content) {
|
|
680
1069
|
try {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1070
|
+
let resolvedNode;
|
|
1071
|
+
let actionTypeForDispatch = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1072
|
+
let targetNodeIdForDispatch = "root";
|
|
1073
|
+
if (enablePartialUpdates) {
|
|
1074
|
+
const route = router.resolveRoute(
|
|
1075
|
+
event,
|
|
1076
|
+
schema,
|
|
1077
|
+
state.layout,
|
|
1078
|
+
dataContext,
|
|
1079
|
+
goal,
|
|
1080
|
+
userContext
|
|
1081
|
+
);
|
|
1082
|
+
if (route) {
|
|
1083
|
+
console.log("Resolved route:", route);
|
|
1084
|
+
actionTypeForDispatch = route.actionType;
|
|
1085
|
+
targetNodeIdForDispatch = route.targetNodeId;
|
|
1086
|
+
systemEvents.emit(
|
|
1087
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
1088
|
+
plannerInput: route.plannerInput
|
|
1089
|
+
})
|
|
1090
|
+
);
|
|
1091
|
+
if (mockMode) {
|
|
1092
|
+
resolvedNode = mockPlanner(
|
|
1093
|
+
route.plannerInput,
|
|
1094
|
+
route.targetNodeId,
|
|
1095
|
+
route.prompt
|
|
1096
|
+
);
|
|
1097
|
+
} else {
|
|
1098
|
+
resolvedNode = await callPlannerLLM(
|
|
1099
|
+
route.plannerInput,
|
|
1100
|
+
openaiApiKey || "",
|
|
1101
|
+
route
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
} else {
|
|
1105
|
+
const input = {
|
|
1106
|
+
schema,
|
|
1107
|
+
goal,
|
|
1108
|
+
history: [...state.history, event],
|
|
1109
|
+
userContext
|
|
1110
|
+
};
|
|
1111
|
+
if (mockMode) {
|
|
1112
|
+
resolvedNode = mockPlanner(input);
|
|
1113
|
+
} else {
|
|
1114
|
+
resolvedNode = await callPlannerLLM(
|
|
1115
|
+
input,
|
|
1116
|
+
openaiApiKey || "",
|
|
1117
|
+
void 0
|
|
1118
|
+
);
|
|
714
1119
|
}
|
|
715
|
-
sessionStorage.removeItem("currentRoute");
|
|
716
|
-
} catch (e) {
|
|
717
|
-
console.error("Error parsing route info:", e);
|
|
718
|
-
dispatch({ type: "AI_RESPONSE", node: validatedNode });
|
|
719
1120
|
}
|
|
720
1121
|
} else {
|
|
721
|
-
|
|
1122
|
+
const input = {
|
|
1123
|
+
schema,
|
|
1124
|
+
goal,
|
|
1125
|
+
history: [...state.history, event],
|
|
1126
|
+
// event is already in history from UI_EVENT dispatch
|
|
1127
|
+
userContext
|
|
1128
|
+
};
|
|
1129
|
+
if (mockMode) {
|
|
1130
|
+
resolvedNode = mockPlanner(input);
|
|
1131
|
+
} else {
|
|
1132
|
+
resolvedNode = await callPlannerLLM(
|
|
1133
|
+
input,
|
|
1134
|
+
openaiApiKey || "",
|
|
1135
|
+
void 0
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
722
1138
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
1139
|
+
switch (actionTypeForDispatch) {
|
|
1140
|
+
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
1141
|
+
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
1142
|
+
case "HIDE_DETAIL" /* HIDE_DETAIL */:
|
|
1143
|
+
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
1144
|
+
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
1145
|
+
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
1146
|
+
case "NAVIGATE" /* NAVIGATE */:
|
|
1147
|
+
dispatch({
|
|
1148
|
+
type: "PARTIAL_UPDATE",
|
|
1149
|
+
nodeId: targetNodeIdForDispatch,
|
|
1150
|
+
node: resolvedNode
|
|
1151
|
+
});
|
|
1152
|
+
break;
|
|
1153
|
+
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
1154
|
+
default:
|
|
1155
|
+
dispatch({ type: "AI_RESPONSE", node: resolvedNode });
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
} catch (e) {
|
|
1159
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1160
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
736
1161
|
systemEvents.emit(
|
|
737
1162
|
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
738
|
-
error:
|
|
1163
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
739
1164
|
})
|
|
740
1165
|
);
|
|
1166
|
+
} finally {
|
|
1167
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
741
1168
|
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const input = {
|
|
746
|
-
schema,
|
|
1169
|
+
},
|
|
1170
|
+
[
|
|
1171
|
+
// append, // REMOVE
|
|
747
1172
|
goal,
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1173
|
+
schema,
|
|
1174
|
+
state.history,
|
|
1175
|
+
// Keep state.history if input preparation needs it
|
|
1176
|
+
state.layout,
|
|
1177
|
+
// stop, // REMOVE
|
|
1178
|
+
userContext,
|
|
1179
|
+
router,
|
|
1180
|
+
mockMode,
|
|
1181
|
+
dataContext,
|
|
1182
|
+
openaiApiKey,
|
|
1183
|
+
enablePartialUpdates,
|
|
1184
|
+
dispatch
|
|
1185
|
+
// Add dispatch
|
|
1186
|
+
]
|
|
1187
|
+
);
|
|
1188
|
+
React.useEffect(() => {
|
|
1189
|
+
const initialFetch = async () => {
|
|
1190
|
+
dispatch({ type: "LOADING", isLoading: true });
|
|
1191
|
+
try {
|
|
1192
|
+
const input = {
|
|
1193
|
+
schema,
|
|
1194
|
+
goal,
|
|
1195
|
+
history: [],
|
|
1196
|
+
// Initial history is empty
|
|
1197
|
+
userContext
|
|
1198
|
+
};
|
|
1199
|
+
let node;
|
|
1200
|
+
if (mockMode) {
|
|
1201
|
+
node = mockPlanner(input);
|
|
1202
|
+
} else {
|
|
1203
|
+
const initEvent = {
|
|
1204
|
+
type: "INIT",
|
|
1205
|
+
// Assuming "INIT" is your initial event type
|
|
1206
|
+
nodeId: "system",
|
|
1207
|
+
// Or some other appropriate initial nodeId
|
|
1208
|
+
timestamp: Date.now(),
|
|
1209
|
+
payload: null
|
|
1210
|
+
};
|
|
1211
|
+
const route = router.resolveRoute(
|
|
1212
|
+
initEvent,
|
|
1213
|
+
schema,
|
|
1214
|
+
null,
|
|
1215
|
+
// No existing layout on initial fetch
|
|
1216
|
+
dataContext,
|
|
1217
|
+
goal,
|
|
1218
|
+
userContext
|
|
1219
|
+
);
|
|
1220
|
+
if (!route || !route.prompt) {
|
|
1221
|
+
console.error(
|
|
1222
|
+
"[UIStateEngine] Initial fetch: Failed to resolve route or get prompt for INIT event."
|
|
1223
|
+
);
|
|
1224
|
+
throw new Error("Failed to initialize UI due to routing error.");
|
|
1225
|
+
}
|
|
1226
|
+
systemEvents.emit(
|
|
1227
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
1228
|
+
plannerInput: route.plannerInput
|
|
1229
|
+
})
|
|
1230
|
+
);
|
|
1231
|
+
node = await callPlannerLLM(
|
|
1232
|
+
route.plannerInput,
|
|
1233
|
+
// Use plannerInput from the resolved route
|
|
1234
|
+
openaiApiKey || "",
|
|
1235
|
+
route
|
|
1236
|
+
// Pass the entire route object
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
dispatch({ type: "AI_RESPONSE", node });
|
|
1240
|
+
} catch (e) {
|
|
1241
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1242
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
1243
|
+
systemEvents.emit(
|
|
1244
|
+
// Also emit system event for initial load error
|
|
1245
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
1246
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
1247
|
+
})
|
|
1248
|
+
);
|
|
1249
|
+
} finally {
|
|
1250
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
initialFetch();
|
|
1254
|
+
}, [goal, schema, userContext, mockMode, dispatch, openaiApiKey]);
|
|
1255
|
+
return {
|
|
1256
|
+
state,
|
|
1257
|
+
dispatch,
|
|
765
1258
|
handleEvent
|
|
766
1259
|
};
|
|
767
1260
|
}
|
|
1261
|
+
|
|
1262
|
+
// src/adapters/shadcn.tsx
|
|
1263
|
+
init_utils();
|
|
1264
|
+
|
|
1265
|
+
// components/ui/dialog.tsx
|
|
1266
|
+
init_utils();
|
|
1267
|
+
function Dialog({
|
|
1268
|
+
...props
|
|
1269
|
+
}) {
|
|
1270
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Root, { "data-slot": "dialog", ...props });
|
|
1271
|
+
}
|
|
1272
|
+
function DialogPortal({
|
|
1273
|
+
...props
|
|
1274
|
+
}) {
|
|
1275
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Portal, { "data-slot": "dialog-portal", ...props });
|
|
1276
|
+
}
|
|
1277
|
+
function DialogOverlay({
|
|
1278
|
+
className,
|
|
1279
|
+
...props
|
|
1280
|
+
}) {
|
|
1281
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1282
|
+
DialogPrimitive__namespace.Overlay,
|
|
1283
|
+
{
|
|
1284
|
+
"data-slot": "dialog-overlay",
|
|
1285
|
+
className: cn(
|
|
1286
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
1287
|
+
className
|
|
1288
|
+
),
|
|
1289
|
+
...props
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
function DialogContent({
|
|
1294
|
+
className,
|
|
1295
|
+
children,
|
|
1296
|
+
...props
|
|
1297
|
+
}) {
|
|
1298
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
1299
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
1300
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1301
|
+
DialogPrimitive__namespace.Content,
|
|
1302
|
+
{
|
|
1303
|
+
"data-slot": "dialog-content",
|
|
1304
|
+
className: cn(
|
|
1305
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
1306
|
+
className
|
|
1307
|
+
),
|
|
1308
|
+
...props,
|
|
1309
|
+
children: [
|
|
1310
|
+
children,
|
|
1311
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
|
|
1312
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {}),
|
|
1313
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
1314
|
+
] })
|
|
1315
|
+
]
|
|
1316
|
+
}
|
|
1317
|
+
)
|
|
1318
|
+
] });
|
|
1319
|
+
}
|
|
1320
|
+
function DialogHeader({ className, ...props }) {
|
|
1321
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1322
|
+
"div",
|
|
1323
|
+
{
|
|
1324
|
+
"data-slot": "dialog-header",
|
|
1325
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
1326
|
+
...props
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
function DialogTitle({
|
|
1331
|
+
className,
|
|
1332
|
+
...props
|
|
1333
|
+
}) {
|
|
1334
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1335
|
+
DialogPrimitive__namespace.Title,
|
|
1336
|
+
{
|
|
1337
|
+
"data-slot": "dialog-title",
|
|
1338
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
1339
|
+
...props
|
|
1340
|
+
}
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
function DialogDescription({
|
|
1344
|
+
className,
|
|
1345
|
+
...props
|
|
1346
|
+
}) {
|
|
1347
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1348
|
+
DialogPrimitive__namespace.Description,
|
|
1349
|
+
{
|
|
1350
|
+
"data-slot": "dialog-description",
|
|
1351
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
1352
|
+
...props
|
|
1353
|
+
}
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// components/ui/card.tsx
|
|
1358
|
+
init_utils();
|
|
1359
|
+
function Card({ className, ...props }) {
|
|
1360
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1361
|
+
"div",
|
|
1362
|
+
{
|
|
1363
|
+
"data-slot": "card",
|
|
1364
|
+
className: cn(
|
|
1365
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
1366
|
+
className
|
|
1367
|
+
),
|
|
1368
|
+
...props
|
|
1369
|
+
}
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
function CardContent({ className, ...props }) {
|
|
1373
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1374
|
+
"div",
|
|
1375
|
+
{
|
|
1376
|
+
"data-slot": "card-content",
|
|
1377
|
+
className: cn("px-6", className),
|
|
1378
|
+
...props
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// components/ui/input.tsx
|
|
1384
|
+
init_utils();
|
|
1385
|
+
function Input({ className, type, ...props }) {
|
|
1386
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1387
|
+
"input",
|
|
1388
|
+
{
|
|
1389
|
+
type,
|
|
1390
|
+
"data-slot": "input",
|
|
1391
|
+
className: cn(
|
|
1392
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1393
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
1394
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
1395
|
+
className
|
|
1396
|
+
),
|
|
1397
|
+
...props
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// components/ui/textarea.tsx
|
|
1403
|
+
init_utils();
|
|
1404
|
+
function Textarea({ className, ...props }) {
|
|
1405
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1406
|
+
"textarea",
|
|
1407
|
+
{
|
|
1408
|
+
"data-slot": "textarea",
|
|
1409
|
+
className: cn(
|
|
1410
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1411
|
+
className
|
|
1412
|
+
),
|
|
1413
|
+
...props
|
|
1414
|
+
}
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// components/ui/select.tsx
|
|
1419
|
+
init_utils();
|
|
1420
|
+
function Select({
|
|
1421
|
+
...props
|
|
1422
|
+
}) {
|
|
1423
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Root, { "data-slot": "select", ...props });
|
|
1424
|
+
}
|
|
1425
|
+
function SelectValue({
|
|
1426
|
+
...props
|
|
1427
|
+
}) {
|
|
1428
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
|
|
1429
|
+
}
|
|
1430
|
+
function SelectTrigger({
|
|
1431
|
+
className,
|
|
1432
|
+
size = "default",
|
|
1433
|
+
children,
|
|
1434
|
+
...props
|
|
1435
|
+
}) {
|
|
1436
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1437
|
+
SelectPrimitive__namespace.Trigger,
|
|
1438
|
+
{
|
|
1439
|
+
"data-slot": "select-trigger",
|
|
1440
|
+
"data-size": size,
|
|
1441
|
+
className: cn(
|
|
1442
|
+
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1443
|
+
className
|
|
1444
|
+
),
|
|
1445
|
+
...props,
|
|
1446
|
+
children: [
|
|
1447
|
+
children,
|
|
1448
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
1449
|
+
]
|
|
1450
|
+
}
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
function SelectContent({
|
|
1454
|
+
className,
|
|
1455
|
+
children,
|
|
1456
|
+
position = "popper",
|
|
1457
|
+
...props
|
|
1458
|
+
}) {
|
|
1459
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1460
|
+
SelectPrimitive__namespace.Content,
|
|
1461
|
+
{
|
|
1462
|
+
"data-slot": "select-content",
|
|
1463
|
+
className: cn(
|
|
1464
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
1465
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1466
|
+
className
|
|
1467
|
+
),
|
|
1468
|
+
position,
|
|
1469
|
+
...props,
|
|
1470
|
+
children: [
|
|
1471
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
1472
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1473
|
+
SelectPrimitive__namespace.Viewport,
|
|
1474
|
+
{
|
|
1475
|
+
className: cn(
|
|
1476
|
+
"p-1",
|
|
1477
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
1478
|
+
),
|
|
1479
|
+
children
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
1483
|
+
]
|
|
1484
|
+
}
|
|
1485
|
+
) });
|
|
1486
|
+
}
|
|
1487
|
+
function SelectItem({
|
|
1488
|
+
className,
|
|
1489
|
+
children,
|
|
1490
|
+
...props
|
|
1491
|
+
}) {
|
|
1492
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1493
|
+
SelectPrimitive__namespace.Item,
|
|
1494
|
+
{
|
|
1495
|
+
"data-slot": "select-item",
|
|
1496
|
+
className: cn(
|
|
1497
|
+
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
1498
|
+
className
|
|
1499
|
+
),
|
|
1500
|
+
...props,
|
|
1501
|
+
children: [
|
|
1502
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-4" }) }) }),
|
|
1503
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
1504
|
+
]
|
|
1505
|
+
}
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
function SelectScrollUpButton({
|
|
1509
|
+
className,
|
|
1510
|
+
...props
|
|
1511
|
+
}) {
|
|
1512
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1513
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
1514
|
+
{
|
|
1515
|
+
"data-slot": "select-scroll-up-button",
|
|
1516
|
+
className: cn(
|
|
1517
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1518
|
+
className
|
|
1519
|
+
),
|
|
1520
|
+
...props,
|
|
1521
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUpIcon, { className: "size-4" })
|
|
1522
|
+
}
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
function SelectScrollDownButton({
|
|
1526
|
+
className,
|
|
1527
|
+
...props
|
|
1528
|
+
}) {
|
|
1529
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1530
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
1531
|
+
{
|
|
1532
|
+
"data-slot": "select-scroll-down-button",
|
|
1533
|
+
className: cn(
|
|
1534
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1535
|
+
className
|
|
1536
|
+
),
|
|
1537
|
+
...props,
|
|
1538
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4" })
|
|
1539
|
+
}
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// components/ui/checkbox.tsx
|
|
1544
|
+
init_utils();
|
|
1545
|
+
function Checkbox({
|
|
1546
|
+
className,
|
|
1547
|
+
...props
|
|
1548
|
+
}) {
|
|
1549
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1550
|
+
CheckboxPrimitive__namespace.Root,
|
|
1551
|
+
{
|
|
1552
|
+
"data-slot": "checkbox",
|
|
1553
|
+
className: cn(
|
|
1554
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1555
|
+
className
|
|
1556
|
+
),
|
|
1557
|
+
...props,
|
|
1558
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1559
|
+
CheckboxPrimitive__namespace.Indicator,
|
|
1560
|
+
{
|
|
1561
|
+
"data-slot": "checkbox-indicator",
|
|
1562
|
+
className: "flex items-center justify-center text-current transition-none",
|
|
1563
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" })
|
|
1564
|
+
}
|
|
1565
|
+
)
|
|
1566
|
+
}
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// components/ui/radio-group.tsx
|
|
1571
|
+
init_utils();
|
|
1572
|
+
function RadioGroup({
|
|
1573
|
+
className,
|
|
1574
|
+
...props
|
|
1575
|
+
}) {
|
|
1576
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1577
|
+
RadioGroupPrimitive__namespace.Root,
|
|
1578
|
+
{
|
|
1579
|
+
"data-slot": "radio-group",
|
|
1580
|
+
className: cn("grid gap-3", className),
|
|
1581
|
+
...props
|
|
1582
|
+
}
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
function RadioGroupItem({
|
|
1586
|
+
className,
|
|
1587
|
+
...props
|
|
1588
|
+
}) {
|
|
1589
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1590
|
+
RadioGroupPrimitive__namespace.Item,
|
|
1591
|
+
{
|
|
1592
|
+
"data-slot": "radio-group-item",
|
|
1593
|
+
className: cn(
|
|
1594
|
+
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1595
|
+
className
|
|
1596
|
+
),
|
|
1597
|
+
...props,
|
|
1598
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1599
|
+
RadioGroupPrimitive__namespace.Indicator,
|
|
1600
|
+
{
|
|
1601
|
+
"data-slot": "radio-group-indicator",
|
|
1602
|
+
className: "relative flex items-center justify-center",
|
|
1603
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleIcon, { className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" })
|
|
1604
|
+
}
|
|
1605
|
+
)
|
|
1606
|
+
}
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// components/ui/tabs.tsx
|
|
1611
|
+
init_utils();
|
|
1612
|
+
function Tabs({
|
|
1613
|
+
className,
|
|
1614
|
+
...props
|
|
1615
|
+
}) {
|
|
1616
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1617
|
+
TabsPrimitive__namespace.Root,
|
|
1618
|
+
{
|
|
1619
|
+
"data-slot": "tabs",
|
|
1620
|
+
className: cn("flex flex-col gap-2", className),
|
|
1621
|
+
...props
|
|
1622
|
+
}
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
function TabsList({
|
|
1626
|
+
className,
|
|
1627
|
+
...props
|
|
1628
|
+
}) {
|
|
1629
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1630
|
+
TabsPrimitive__namespace.List,
|
|
1631
|
+
{
|
|
1632
|
+
"data-slot": "tabs-list",
|
|
1633
|
+
className: cn(
|
|
1634
|
+
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
|
1635
|
+
className
|
|
1636
|
+
),
|
|
1637
|
+
...props
|
|
1638
|
+
}
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
function TabsTrigger({
|
|
1642
|
+
className,
|
|
1643
|
+
...props
|
|
1644
|
+
}) {
|
|
1645
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1646
|
+
TabsPrimitive__namespace.Trigger,
|
|
1647
|
+
{
|
|
1648
|
+
"data-slot": "tabs-trigger",
|
|
1649
|
+
className: cn(
|
|
1650
|
+
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1651
|
+
className
|
|
1652
|
+
),
|
|
1653
|
+
...props
|
|
1654
|
+
}
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
function TabsContent({
|
|
1658
|
+
className,
|
|
1659
|
+
...props
|
|
1660
|
+
}) {
|
|
1661
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1662
|
+
TabsPrimitive__namespace.Content,
|
|
1663
|
+
{
|
|
1664
|
+
"data-slot": "tabs-content",
|
|
1665
|
+
className: cn("flex-1 outline-none", className),
|
|
1666
|
+
...props
|
|
1667
|
+
}
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// components/ui/label.tsx
|
|
1672
|
+
init_utils();
|
|
1673
|
+
function Label2({
|
|
1674
|
+
className,
|
|
1675
|
+
...props
|
|
1676
|
+
}) {
|
|
1677
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1678
|
+
LabelPrimitive__namespace.Root,
|
|
1679
|
+
{
|
|
1680
|
+
"data-slot": "label",
|
|
1681
|
+
className: cn(
|
|
1682
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
1683
|
+
className
|
|
1684
|
+
),
|
|
1685
|
+
...props
|
|
1686
|
+
}
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
var parseStyleString = (styleString) => {
|
|
1690
|
+
if (typeof styleString !== "string") {
|
|
1691
|
+
return typeof styleString === "object" ? styleString : {};
|
|
1692
|
+
}
|
|
1693
|
+
const style = {};
|
|
1694
|
+
styleString.split(";").forEach((declaration) => {
|
|
1695
|
+
const [property, value] = declaration.split(":");
|
|
1696
|
+
if (property && value) {
|
|
1697
|
+
const camelCasedProperty = property.trim().replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
1698
|
+
style[camelCasedProperty] = value.trim();
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
return style;
|
|
1702
|
+
};
|
|
1703
|
+
var isArrayOf = (guard) => (arr) => Array.isArray(arr) && arr.every(guard);
|
|
768
1704
|
var ShimmerBlock = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-8 bg-gray-200 animate-pulse rounded" });
|
|
769
1705
|
var ShimmerTable = ({ rows = 3 }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-2", children: [
|
|
770
1706
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-10 bg-gray-200 animate-pulse rounded" }),
|
|
@@ -778,119 +1714,696 @@ var ShimmerCard = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-f
|
|
|
778
1714
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5/6 h-4 bg-gray-200 animate-pulse rounded" })
|
|
779
1715
|
] })
|
|
780
1716
|
] });
|
|
781
|
-
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
|
|
782
|
-
var Header = ({
|
|
1717
|
+
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `autoui-mock-container ${props.className || ""}`, children: props.children });
|
|
1718
|
+
var Header = ({
|
|
1719
|
+
title,
|
|
1720
|
+
className
|
|
1721
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1722
|
+
"header",
|
|
1723
|
+
{
|
|
1724
|
+
className: `py-4 px-6 border-b border-gray-300 mb-4 bg-gray-50 dark:bg-gray-800 ${className || ""}`,
|
|
1725
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-800 dark:text-white", children: title })
|
|
1726
|
+
}
|
|
1727
|
+
);
|
|
783
1728
|
var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
784
1729
|
"button",
|
|
785
1730
|
{
|
|
786
|
-
className: `px-4 py-2 rounded font-medium ${variant === "default" ? "bg-blue-600 text-white" : variant === "outline" ? "border border-gray-300 text-gray-700" : "bg-red-600 text-white"}`,
|
|
787
|
-
onClick,
|
|
1731
|
+
className: `px-4 py-2 rounded-md font-medium transition-colors ${variant === "default" ? "bg-blue-600 text-white hover:bg-blue-700" : variant === "outline" ? "border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800" : "bg-red-600 text-white hover:bg-red-700"}`,
|
|
1732
|
+
onClick: () => onClick?.(),
|
|
788
1733
|
children
|
|
789
1734
|
}
|
|
790
1735
|
);
|
|
791
|
-
var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
792
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: field.label }, field.key)) }) }),
|
|
793
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
|
-
"tr",
|
|
795
|
-
{
|
|
796
|
-
onClick: () => selectable && onSelect && onSelect(item),
|
|
797
|
-
className: selectable ? "cursor-pointer hover:bg-gray-50" : "",
|
|
798
|
-
children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-500", children: item[field.key] }, field.key))
|
|
799
|
-
},
|
|
800
|
-
index
|
|
801
|
-
)) })
|
|
802
|
-
] }) });
|
|
803
1736
|
var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
|
|
804
1737
|
if (!visible)
|
|
805
1738
|
return null;
|
|
806
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full border rounded-lg p-6 space-y-4", children: [
|
|
807
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
808
|
-
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium", children: title }),
|
|
1739
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full border border-gray-300 dark:border-gray-700 rounded-lg p-6 space-y-4 bg-white dark:bg-gray-900 shadow-sm", children: [
|
|
1740
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-3", children: [
|
|
1741
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-800 dark:text-white", children: title }),
|
|
809
1742
|
onBack && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
|
|
810
1743
|
] }),
|
|
811
1744
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: fields.map((field) => {
|
|
812
1745
|
if (field.type === "heading") {
|
|
813
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1746
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1747
|
+
"h3",
|
|
1748
|
+
{
|
|
1749
|
+
className: "text-xl font-semibold text-gray-800 dark:text-white",
|
|
1750
|
+
children: data?.[field.key] ?? ""
|
|
1751
|
+
},
|
|
1752
|
+
field.key
|
|
1753
|
+
);
|
|
814
1754
|
}
|
|
815
1755
|
if (field.type === "content") {
|
|
816
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1756
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1757
|
+
"div",
|
|
1758
|
+
{
|
|
1759
|
+
className: "text-sm text-gray-700 dark:text-gray-300",
|
|
1760
|
+
children: data?.[field.key] ?? ""
|
|
1761
|
+
},
|
|
1762
|
+
field.key
|
|
1763
|
+
);
|
|
817
1764
|
}
|
|
818
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1765
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1766
|
+
"div",
|
|
1767
|
+
{
|
|
1768
|
+
className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
|
|
1769
|
+
children: [
|
|
1770
|
+
field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
|
|
1771
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
|
|
1772
|
+
]
|
|
1773
|
+
},
|
|
1774
|
+
field.key
|
|
1775
|
+
);
|
|
822
1776
|
}) })
|
|
823
1777
|
] });
|
|
824
1778
|
};
|
|
825
|
-
var
|
|
826
|
-
|
|
827
|
-
|
|
1779
|
+
var getSafeProp = (props, key, validator, defaultValue) => {
|
|
1780
|
+
if (props && typeof props === "object" && key in props) {
|
|
1781
|
+
const value = props[key];
|
|
1782
|
+
if (validator(value)) {
|
|
1783
|
+
return value;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return defaultValue;
|
|
1787
|
+
};
|
|
1788
|
+
var isObject = (value) => typeof value === "object" && value !== null;
|
|
1789
|
+
var isString = (value) => typeof value === "string";
|
|
1790
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
1791
|
+
var isButtonVariant = (value) => isString(value) && ["default", "outline", "destructive"].includes(value);
|
|
1792
|
+
var getSafeBinding = (bindings, key, validator, defaultValue) => {
|
|
1793
|
+
if (bindings && typeof bindings === "object" && key in bindings) {
|
|
1794
|
+
const value = bindings[key];
|
|
1795
|
+
if (validator(value)) {
|
|
1796
|
+
return value;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return defaultValue;
|
|
1800
|
+
};
|
|
1801
|
+
var isReactNode = (value) => {
|
|
1802
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null || typeof value === "undefined" || typeof value === "object" && value !== null && "$$typeof" in value;
|
|
1803
|
+
};
|
|
1804
|
+
var isRecordWithReactNodeValues = (value) => isObject(value) && Object.values(value).every(isReactNode);
|
|
1805
|
+
var isSelectOptionObject = (item) => isObject(item) && isString(item.value) && isString(item.label);
|
|
1806
|
+
var isTabObject = (item) => (
|
|
1807
|
+
// Allow content to be optional initially
|
|
1808
|
+
isObject(item) && isString(item.value) && isString(item.label) && (item.content === void 0 || isUISpecNode(item.content))
|
|
1809
|
+
);
|
|
1810
|
+
var isUISpecNode = (value) => {
|
|
1811
|
+
if (!isObject(value))
|
|
1812
|
+
return false;
|
|
1813
|
+
return isString(value.id) && isString(value.node_type);
|
|
1814
|
+
};
|
|
1815
|
+
var isDetailFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label) && (item.type === void 0 || isString(item.type));
|
|
1816
|
+
var createEventHandler = (node, eventName, uiEventType2, processEvent2) => {
|
|
1817
|
+
const eventConfig = node.events?.[uiEventType2];
|
|
1818
|
+
if (!processEvent2 || !eventConfig)
|
|
828
1819
|
return void 0;
|
|
829
|
-
return () => {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1820
|
+
return (eventPayload) => {
|
|
1821
|
+
const fullEvent = {
|
|
1822
|
+
type: uiEventType2,
|
|
1823
|
+
nodeId: node.id,
|
|
1824
|
+
timestamp: Date.now(),
|
|
1825
|
+
payload: {
|
|
1826
|
+
...eventConfig.payload || {},
|
|
1827
|
+
...eventPayload || {}
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
processEvent2(fullEvent);
|
|
835
1831
|
};
|
|
836
1832
|
};
|
|
837
1833
|
var adapterMap = {
|
|
838
|
-
Container: (node
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1834
|
+
Container: (node, processEvent2) => {
|
|
1835
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
1836
|
+
const children = node.children?.map(
|
|
1837
|
+
(child) => (
|
|
1838
|
+
// Use React.cloneElement to add the key prop to the element returned by renderNode
|
|
1839
|
+
React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
|
|
1840
|
+
)
|
|
1841
|
+
);
|
|
1842
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
1843
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1844
|
+
"div",
|
|
1845
|
+
{
|
|
1846
|
+
className: cn("autoui-container", className),
|
|
1847
|
+
style,
|
|
1848
|
+
...restProps,
|
|
1849
|
+
"data-id": node.id,
|
|
1850
|
+
children: [
|
|
1851
|
+
(() => {
|
|
1852
|
+
console.log(
|
|
1853
|
+
`[Adapter Debug] Rendering Container: id=${node.id}, props=`,
|
|
1854
|
+
node.props
|
|
1855
|
+
);
|
|
1856
|
+
return null;
|
|
1857
|
+
})(),
|
|
1858
|
+
children
|
|
1859
|
+
]
|
|
1860
|
+
},
|
|
1861
|
+
key
|
|
1862
|
+
);
|
|
1863
|
+
},
|
|
1864
|
+
Header: (node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1865
|
+
Header,
|
|
842
1866
|
{
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
children: node.props?.label || "Button"
|
|
1867
|
+
title: getSafeProp(node.props, "title", isString, "Untitled"),
|
|
1868
|
+
className: getSafeProp(node.props, "className", isString, "")
|
|
846
1869
|
}
|
|
847
1870
|
),
|
|
848
|
-
|
|
849
|
-
|
|
1871
|
+
Button: (node, processEvent2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1872
|
+
Button,
|
|
850
1873
|
{
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
onSelect: createEventHandler(node, "onSelect")
|
|
1874
|
+
variant: getSafeProp(node.props, "variant", isButtonVariant, "default"),
|
|
1875
|
+
onClick: createEventHandler(node, "onClick", "CLICK", processEvent2),
|
|
1876
|
+
children: getSafeProp(node.props, "label", isString, "Button")
|
|
855
1877
|
}
|
|
856
1878
|
),
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
1879
|
+
ListView: (node, processEvent2) => {
|
|
1880
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
1881
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
1882
|
+
console.log(
|
|
1883
|
+
`[Adapter Debug] Rendering ListView: id=${node.id}, props=`,
|
|
1884
|
+
node.props
|
|
1885
|
+
);
|
|
1886
|
+
const children = node.children?.map(
|
|
1887
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
|
|
1888
|
+
);
|
|
1889
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1890
|
+
"div",
|
|
1891
|
+
{
|
|
1892
|
+
className: cn(
|
|
1893
|
+
"autoui-listview-container space-y-2",
|
|
1894
|
+
className
|
|
1895
|
+
),
|
|
1896
|
+
style,
|
|
1897
|
+
...restProps,
|
|
1898
|
+
"data-id": node.id,
|
|
1899
|
+
children
|
|
1900
|
+
},
|
|
1901
|
+
key
|
|
1902
|
+
);
|
|
1903
|
+
},
|
|
1904
|
+
Detail: (node, processEvent2) => {
|
|
1905
|
+
const data = getSafeBinding(
|
|
1906
|
+
node.bindings,
|
|
1907
|
+
"data",
|
|
1908
|
+
isRecordWithReactNodeValues,
|
|
1909
|
+
{}
|
|
1910
|
+
);
|
|
1911
|
+
const fields = getSafeBinding(
|
|
1912
|
+
node.bindings,
|
|
1913
|
+
"fields",
|
|
1914
|
+
isArrayOf(isDetailFieldObject),
|
|
1915
|
+
[]
|
|
1916
|
+
);
|
|
1917
|
+
const title = getSafeProp(node.props, "title", isString, "");
|
|
1918
|
+
const visible = getSafeProp(node.props, "visible", isBoolean, true);
|
|
1919
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1920
|
+
Detail,
|
|
1921
|
+
{
|
|
1922
|
+
data,
|
|
1923
|
+
fields,
|
|
1924
|
+
title,
|
|
1925
|
+
visible,
|
|
1926
|
+
onBack: createEventHandler(node, "onBack", "CLICK", processEvent2)
|
|
1927
|
+
}
|
|
1928
|
+
);
|
|
1929
|
+
},
|
|
1930
|
+
Card: (node, processEvent2) => {
|
|
1931
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
1932
|
+
const children = node.children?.map(
|
|
1933
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
|
|
1934
|
+
);
|
|
1935
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
1936
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1937
|
+
Card,
|
|
1938
|
+
{
|
|
1939
|
+
className: cn("autoui-card", className),
|
|
1940
|
+
style,
|
|
1941
|
+
...restProps,
|
|
1942
|
+
"data-id": node.id,
|
|
1943
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
1944
|
+
" ",
|
|
1945
|
+
children
|
|
1946
|
+
] })
|
|
1947
|
+
},
|
|
1948
|
+
key
|
|
1949
|
+
);
|
|
1950
|
+
},
|
|
1951
|
+
Input: (node, processEvent2) => {
|
|
1952
|
+
const name = getSafeProp(node.props, "name", isString, "inputName");
|
|
1953
|
+
const label = getSafeProp(node.props, "label", isString, "");
|
|
1954
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
1955
|
+
const placeholder = getSafeProp(node.props, "placeholder", isString, "");
|
|
1956
|
+
const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
|
|
1957
|
+
const className = getSafeProp(node.props, "className", isString, "");
|
|
1958
|
+
const handleChange = (e) => {
|
|
1959
|
+
const handler = createEventHandler(
|
|
1960
|
+
node,
|
|
1961
|
+
"onChange",
|
|
1962
|
+
"CHANGE",
|
|
1963
|
+
processEvent2
|
|
1964
|
+
);
|
|
1965
|
+
if (handler)
|
|
1966
|
+
handler({ value: e.target.value });
|
|
1967
|
+
};
|
|
1968
|
+
const handleFocus = () => {
|
|
1969
|
+
const handler = createEventHandler(
|
|
1970
|
+
node,
|
|
1971
|
+
"onFocus",
|
|
1972
|
+
"FOCUS",
|
|
1973
|
+
processEvent2
|
|
1974
|
+
);
|
|
1975
|
+
if (handler)
|
|
1976
|
+
handler({});
|
|
1977
|
+
};
|
|
1978
|
+
const handleBlur = () => {
|
|
1979
|
+
const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
|
|
1980
|
+
if (handler)
|
|
1981
|
+
handler({});
|
|
1982
|
+
};
|
|
1983
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full max-w-sm items-center gap-1.5", children: [
|
|
1984
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
1985
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1986
|
+
Input,
|
|
1987
|
+
{
|
|
1988
|
+
id: name,
|
|
1989
|
+
name,
|
|
1990
|
+
placeholder,
|
|
1991
|
+
disabled,
|
|
1992
|
+
value,
|
|
1993
|
+
onChange: handleChange,
|
|
1994
|
+
onFocus: handleFocus,
|
|
1995
|
+
onBlur: handleBlur,
|
|
1996
|
+
className
|
|
1997
|
+
}
|
|
1998
|
+
)
|
|
1999
|
+
] });
|
|
2000
|
+
},
|
|
2001
|
+
Select: (node, processEvent2) => {
|
|
2002
|
+
const name = getSafeProp(node.props, "name", isString, "selectName");
|
|
2003
|
+
const label = getSafeProp(node.props, "label", isString, "");
|
|
2004
|
+
const placeholder = getSafeProp(
|
|
2005
|
+
node.props,
|
|
2006
|
+
"placeholder",
|
|
2007
|
+
isString,
|
|
2008
|
+
"Select..."
|
|
2009
|
+
);
|
|
2010
|
+
const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
|
|
2011
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2012
|
+
const options = getSafeBinding(
|
|
2013
|
+
node.bindings,
|
|
2014
|
+
"options",
|
|
2015
|
+
isArrayOf(isSelectOptionObject),
|
|
2016
|
+
[]
|
|
2017
|
+
);
|
|
2018
|
+
const className = getSafeProp(node.props, "className", isString, "");
|
|
2019
|
+
const handleValueChange = (selectedValue) => {
|
|
2020
|
+
const handler = createEventHandler(
|
|
2021
|
+
node,
|
|
2022
|
+
"onValueChange",
|
|
2023
|
+
"CHANGE",
|
|
2024
|
+
processEvent2
|
|
2025
|
+
);
|
|
2026
|
+
if (handler)
|
|
2027
|
+
handler({ value: selectedValue });
|
|
2028
|
+
};
|
|
2029
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2030
|
+
"div",
|
|
2031
|
+
{
|
|
2032
|
+
className: cn("grid w-full max-w-sm items-center gap-1.5", className),
|
|
2033
|
+
children: [
|
|
2034
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
2035
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2036
|
+
Select,
|
|
2037
|
+
{
|
|
2038
|
+
name,
|
|
2039
|
+
value,
|
|
2040
|
+
onValueChange: handleValueChange,
|
|
2041
|
+
disabled,
|
|
2042
|
+
children: [
|
|
2043
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: name, children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder }) }),
|
|
2044
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
|
|
2045
|
+
]
|
|
2046
|
+
}
|
|
2047
|
+
)
|
|
2048
|
+
]
|
|
2049
|
+
}
|
|
2050
|
+
);
|
|
2051
|
+
},
|
|
2052
|
+
Textarea: (node, processEvent2) => {
|
|
2053
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2054
|
+
const name = getSafeProp(propsWithoutKey, "name", isString, "textareaName");
|
|
2055
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2056
|
+
const placeholder = getSafeProp(
|
|
2057
|
+
propsWithoutKey,
|
|
2058
|
+
"placeholder",
|
|
2059
|
+
isString,
|
|
2060
|
+
""
|
|
2061
|
+
);
|
|
2062
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2063
|
+
const rows = getSafeProp(
|
|
2064
|
+
propsWithoutKey,
|
|
2065
|
+
"rows",
|
|
2066
|
+
(v) => typeof v === "number",
|
|
2067
|
+
3
|
|
2068
|
+
);
|
|
2069
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2070
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2071
|
+
const handleChange = (e) => {
|
|
2072
|
+
const handler = createEventHandler(
|
|
2073
|
+
node,
|
|
2074
|
+
"onChange",
|
|
2075
|
+
"CHANGE",
|
|
2076
|
+
processEvent2
|
|
2077
|
+
);
|
|
2078
|
+
if (handler)
|
|
2079
|
+
handler({ value: e.target.value });
|
|
2080
|
+
};
|
|
2081
|
+
const handleFocus = () => {
|
|
2082
|
+
const handler = createEventHandler(
|
|
2083
|
+
node,
|
|
2084
|
+
"onFocus",
|
|
2085
|
+
"FOCUS",
|
|
2086
|
+
processEvent2
|
|
2087
|
+
);
|
|
2088
|
+
if (handler)
|
|
2089
|
+
handler({});
|
|
2090
|
+
};
|
|
2091
|
+
const handleBlur = () => {
|
|
2092
|
+
const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
|
|
2093
|
+
if (handler)
|
|
2094
|
+
handler({});
|
|
2095
|
+
};
|
|
2096
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full gap-1.5", children: [
|
|
2097
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2099
|
+
Textarea,
|
|
2100
|
+
{
|
|
2101
|
+
id: name,
|
|
2102
|
+
name,
|
|
2103
|
+
placeholder,
|
|
2104
|
+
disabled,
|
|
2105
|
+
rows,
|
|
2106
|
+
value,
|
|
2107
|
+
onChange: handleChange,
|
|
2108
|
+
onFocus: handleFocus,
|
|
2109
|
+
onBlur: handleBlur,
|
|
2110
|
+
className
|
|
2111
|
+
}
|
|
2112
|
+
)
|
|
2113
|
+
] }, key);
|
|
2114
|
+
},
|
|
2115
|
+
Checkbox: (node, processEvent2) => {
|
|
2116
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2117
|
+
const name = getSafeProp(propsWithoutKey, "name", isString, "checkboxName");
|
|
2118
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2119
|
+
const checked = getSafeBinding(node.bindings, "checked", isBoolean, false);
|
|
2120
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2121
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2122
|
+
const handleCheckedChange = (isChecked) => {
|
|
2123
|
+
if (typeof isChecked === "boolean") {
|
|
2124
|
+
const handler = createEventHandler(
|
|
2125
|
+
node,
|
|
2126
|
+
"onCheckedChange",
|
|
2127
|
+
"CHANGE",
|
|
2128
|
+
processEvent2
|
|
2129
|
+
);
|
|
2130
|
+
if (handler)
|
|
2131
|
+
handler({ checked: isChecked });
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2135
|
+
"div",
|
|
2136
|
+
{
|
|
2137
|
+
className: cn("flex items-center space-x-2", className),
|
|
2138
|
+
children: [
|
|
2139
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2140
|
+
Checkbox,
|
|
2141
|
+
{
|
|
2142
|
+
id: name,
|
|
2143
|
+
name,
|
|
2144
|
+
checked,
|
|
2145
|
+
disabled,
|
|
2146
|
+
onCheckedChange: handleCheckedChange
|
|
2147
|
+
}
|
|
2148
|
+
),
|
|
2149
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, className: "cursor-pointer", children: label })
|
|
2150
|
+
]
|
|
2151
|
+
},
|
|
2152
|
+
key
|
|
2153
|
+
);
|
|
2154
|
+
},
|
|
2155
|
+
RadioGroup: (node, processEvent2) => {
|
|
2156
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2157
|
+
const name = getSafeProp(
|
|
2158
|
+
propsWithoutKey,
|
|
2159
|
+
"name",
|
|
2160
|
+
isString,
|
|
2161
|
+
"radioGroupName"
|
|
2162
|
+
);
|
|
2163
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2164
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2165
|
+
const options = getSafeBinding(
|
|
2166
|
+
node.bindings,
|
|
2167
|
+
"options",
|
|
2168
|
+
isArrayOf(isSelectOptionObject),
|
|
2169
|
+
[]
|
|
2170
|
+
);
|
|
2171
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2172
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2173
|
+
const handleValueChange = (selectedValue) => {
|
|
2174
|
+
const handler = createEventHandler(
|
|
2175
|
+
node,
|
|
2176
|
+
"onValueChange",
|
|
2177
|
+
"CHANGE",
|
|
2178
|
+
processEvent2
|
|
2179
|
+
);
|
|
2180
|
+
if (handler)
|
|
2181
|
+
handler({ value: selectedValue });
|
|
2182
|
+
};
|
|
2183
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2184
|
+
"div",
|
|
2185
|
+
{
|
|
2186
|
+
className: cn("grid gap-1.5", className),
|
|
2187
|
+
children: [
|
|
2188
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { className: "mb-1", children: label }),
|
|
2189
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2190
|
+
RadioGroup,
|
|
2191
|
+
{
|
|
2192
|
+
name,
|
|
2193
|
+
value,
|
|
2194
|
+
onValueChange: handleValueChange,
|
|
2195
|
+
disabled,
|
|
2196
|
+
className: "flex flex-col space-y-1",
|
|
2197
|
+
children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
2198
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2199
|
+
RadioGroupItem,
|
|
2200
|
+
{
|
|
2201
|
+
value: option.value,
|
|
2202
|
+
id: `${name}-${option.value}`
|
|
2203
|
+
}
|
|
2204
|
+
),
|
|
2205
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2206
|
+
Label2,
|
|
2207
|
+
{
|
|
2208
|
+
htmlFor: `${name}-${option.value}`,
|
|
2209
|
+
className: "cursor-pointer",
|
|
2210
|
+
children: option.label
|
|
2211
|
+
}
|
|
2212
|
+
)
|
|
2213
|
+
] }, option.value))
|
|
2214
|
+
}
|
|
2215
|
+
)
|
|
2216
|
+
]
|
|
2217
|
+
},
|
|
2218
|
+
key
|
|
2219
|
+
);
|
|
2220
|
+
},
|
|
2221
|
+
Tabs: (node, processEvent2) => {
|
|
2222
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2223
|
+
const rawTabs = getSafeBinding(
|
|
2224
|
+
node.bindings,
|
|
2225
|
+
"tabs",
|
|
2226
|
+
isArrayOf(isTabObject),
|
|
2227
|
+
[]
|
|
2228
|
+
);
|
|
2229
|
+
const defaultValue = getSafeProp(
|
|
2230
|
+
propsWithoutKey,
|
|
2231
|
+
"defaultValue",
|
|
2232
|
+
isString,
|
|
2233
|
+
rawTabs[0]?.value || ""
|
|
2234
|
+
);
|
|
2235
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2236
|
+
const handleValueChange = (value) => {
|
|
2237
|
+
const handler = createEventHandler(
|
|
2238
|
+
node,
|
|
2239
|
+
"onValueChange",
|
|
2240
|
+
"CHANGE",
|
|
2241
|
+
processEvent2
|
|
2242
|
+
);
|
|
2243
|
+
if (handler)
|
|
2244
|
+
handler({ value });
|
|
2245
|
+
};
|
|
2246
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2247
|
+
Tabs,
|
|
2248
|
+
{
|
|
2249
|
+
defaultValue,
|
|
2250
|
+
onValueChange: handleValueChange,
|
|
2251
|
+
className: cn("autoui-tabs w-full", className),
|
|
2252
|
+
"data-id": node.id,
|
|
2253
|
+
children: [
|
|
2254
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabsList, { children: rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value)) }),
|
|
2255
|
+
rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: tab.value, children: tab.content ? renderNode(tab.content, processEvent2) : null }, tab.value))
|
|
2256
|
+
]
|
|
2257
|
+
},
|
|
2258
|
+
key
|
|
2259
|
+
);
|
|
2260
|
+
},
|
|
2261
|
+
Dialog: (node, processEvent2) => {
|
|
2262
|
+
const isOpen = getSafeBinding(
|
|
2263
|
+
node.bindings,
|
|
2264
|
+
"open",
|
|
2265
|
+
isBoolean,
|
|
2266
|
+
getSafeProp(node.props, "open", isBoolean, false)
|
|
2267
|
+
);
|
|
2268
|
+
const {
|
|
2269
|
+
title,
|
|
2270
|
+
description,
|
|
2271
|
+
className,
|
|
2272
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2273
|
+
style: _styleProp,
|
|
2274
|
+
key,
|
|
2275
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2276
|
+
open: _openProp,
|
|
2277
|
+
...restProps
|
|
2278
|
+
} = node.props || {};
|
|
2279
|
+
const children = node.children?.map(
|
|
2280
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
|
|
2281
|
+
);
|
|
2282
|
+
const handleOpenChange = (open) => {
|
|
2283
|
+
if (!open) {
|
|
2284
|
+
const handler = createEventHandler(
|
|
2285
|
+
node,
|
|
2286
|
+
"onClose",
|
|
2287
|
+
// Assumed event name in UISpec
|
|
2288
|
+
"CLICK",
|
|
2289
|
+
// Use CLICK as the event type for closing dialogs
|
|
2290
|
+
processEvent2
|
|
2291
|
+
);
|
|
2292
|
+
if (handler) {
|
|
2293
|
+
handler({});
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
console.log(
|
|
2298
|
+
`[Adapter Debug] Rendering Dialog: id=${node.id}, props=`,
|
|
2299
|
+
node.props,
|
|
2300
|
+
`isOpen=${isOpen}`
|
|
2301
|
+
);
|
|
2302
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2303
|
+
Dialog,
|
|
2304
|
+
{
|
|
2305
|
+
open: isOpen,
|
|
2306
|
+
onOpenChange: handleOpenChange,
|
|
2307
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2308
|
+
DialogContent,
|
|
2309
|
+
{
|
|
2310
|
+
className: cn("autoui-dialog-content", className),
|
|
2311
|
+
...restProps,
|
|
2312
|
+
"data-id": node.id,
|
|
2313
|
+
children: [
|
|
2314
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
2315
|
+
title && /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: title }),
|
|
2316
|
+
description && /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: description })
|
|
2317
|
+
] }),
|
|
2318
|
+
children
|
|
2319
|
+
]
|
|
2320
|
+
}
|
|
2321
|
+
)
|
|
2322
|
+
},
|
|
2323
|
+
key
|
|
2324
|
+
);
|
|
2325
|
+
},
|
|
2326
|
+
Heading: (node) => {
|
|
2327
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2328
|
+
const text = getSafeProp(node.props, "text", isString, "Heading");
|
|
2329
|
+
let level = getSafeProp(
|
|
2330
|
+
node.props,
|
|
2331
|
+
"level",
|
|
2332
|
+
(v) => typeof v === "number" && v >= 1 && v <= 6,
|
|
2333
|
+
2
|
|
2334
|
+
);
|
|
2335
|
+
if (typeof level !== "number" || level < 1 || level > 6) {
|
|
2336
|
+
level = 2;
|
|
865
2337
|
}
|
|
866
|
-
|
|
2338
|
+
const Tag = `h${level}`;
|
|
2339
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2340
|
+
const headingStyles = {
|
|
2341
|
+
1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
|
|
2342
|
+
2: "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0",
|
|
2343
|
+
3: "scroll-m-20 text-2xl font-semibold tracking-tight",
|
|
2344
|
+
4: "scroll-m-20 text-xl font-semibold tracking-tight"
|
|
2345
|
+
// Add styles for h5, h6 if needed, using text-lg, text-base etc.
|
|
2346
|
+
}[level] || "text-lg font-semibold";
|
|
2347
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2348
|
+
Tag,
|
|
2349
|
+
{
|
|
2350
|
+
className: cn(headingStyles, className),
|
|
2351
|
+
style,
|
|
2352
|
+
...restProps,
|
|
2353
|
+
children: text
|
|
2354
|
+
},
|
|
2355
|
+
key
|
|
2356
|
+
);
|
|
2357
|
+
},
|
|
2358
|
+
Text: (node) => {
|
|
2359
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2360
|
+
const text = getSafeProp(node.props, "text", isString, "Some text");
|
|
2361
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2362
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2363
|
+
"p",
|
|
2364
|
+
{
|
|
2365
|
+
className: cn("leading-7", className),
|
|
2366
|
+
style,
|
|
2367
|
+
...restProps,
|
|
2368
|
+
children: text
|
|
2369
|
+
},
|
|
2370
|
+
key
|
|
2371
|
+
);
|
|
2372
|
+
}
|
|
867
2373
|
};
|
|
868
|
-
function renderNode(node) {
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
871
|
-
return
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
] });
|
|
2374
|
+
function renderNode(node, processEvent2) {
|
|
2375
|
+
const mappedComponent = adapterMap[node.node_type];
|
|
2376
|
+
if (mappedComponent) {
|
|
2377
|
+
return mappedComponent(node, processEvent2);
|
|
2378
|
+
}
|
|
2379
|
+
console.warn(`Unknown node type: ${node.node_type}`);
|
|
2380
|
+
return React__default.default.createElement(
|
|
2381
|
+
Container,
|
|
2382
|
+
{},
|
|
2383
|
+
`Unknown node type: ${node.node_type}`
|
|
2384
|
+
);
|
|
880
2385
|
}
|
|
881
|
-
|
|
2386
|
+
var renderedNodesCache = /* @__PURE__ */ new Map();
|
|
2387
|
+
var MAX_CACHE_SIZE = 10;
|
|
2388
|
+
var CACHE_TTL = 5e3;
|
|
2389
|
+
async function renderNode2(node, adapter = "shadcn", processEvent2) {
|
|
882
2390
|
const startTime = Date.now();
|
|
2391
|
+
const nodeId = node.id;
|
|
2392
|
+
const cachedItem = renderedNodesCache.get(nodeId);
|
|
2393
|
+
if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
|
|
2394
|
+
return cachedItem.element;
|
|
2395
|
+
}
|
|
883
2396
|
await systemEvents.emit(
|
|
884
2397
|
createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
|
|
885
2398
|
);
|
|
886
2399
|
let result;
|
|
887
2400
|
switch (adapter) {
|
|
888
2401
|
case "shadcn":
|
|
889
|
-
result = renderNode(node);
|
|
2402
|
+
result = renderNode(node, processEvent2);
|
|
890
2403
|
break;
|
|
891
2404
|
default:
|
|
892
2405
|
console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
|
|
893
|
-
result = renderNode(node);
|
|
2406
|
+
result = renderNode(node, processEvent2);
|
|
894
2407
|
}
|
|
895
2408
|
await systemEvents.emit(
|
|
896
2409
|
createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
|
|
@@ -898,13 +2411,23 @@ async function renderNode2(node, adapter = "shadcn") {
|
|
|
898
2411
|
renderTimeMs: Date.now() - startTime
|
|
899
2412
|
})
|
|
900
2413
|
);
|
|
2414
|
+
renderedNodesCache.set(nodeId, {
|
|
2415
|
+
element: result,
|
|
2416
|
+
timestamp: startTime
|
|
2417
|
+
});
|
|
2418
|
+
if (renderedNodesCache.size > MAX_CACHE_SIZE) {
|
|
2419
|
+
const oldestKey = [...renderedNodesCache.entries()].sort(
|
|
2420
|
+
([, a], [, b]) => a.timestamp - b.timestamp
|
|
2421
|
+
)[0][0];
|
|
2422
|
+
renderedNodesCache.delete(oldestKey);
|
|
2423
|
+
}
|
|
901
2424
|
return result;
|
|
902
2425
|
}
|
|
903
2426
|
function renderShimmer(node, adapter = "shadcn") {
|
|
904
2427
|
if (!node) {
|
|
905
2428
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
|
|
906
2429
|
}
|
|
907
|
-
switch (node.
|
|
2430
|
+
switch (node.node_type) {
|
|
908
2431
|
case "ListView":
|
|
909
2432
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerTable, { rows: 3 });
|
|
910
2433
|
case "Detail":
|
|
@@ -917,6 +2440,16 @@ function renderShimmer(node, adapter = "shadcn") {
|
|
|
917
2440
|
}
|
|
918
2441
|
|
|
919
2442
|
// src/core/bindings.ts
|
|
2443
|
+
var bindingsCache = /* @__PURE__ */ new Map();
|
|
2444
|
+
var MAX_CACHE_SIZE2 = 50;
|
|
2445
|
+
var CACHE_TTL2 = 2e3;
|
|
2446
|
+
var nodeCacheTimestamps = /* @__PURE__ */ new Map();
|
|
2447
|
+
function hashDataContext(context) {
|
|
2448
|
+
return JSON.stringify(context);
|
|
2449
|
+
}
|
|
2450
|
+
function createCacheKey(nodeId, context) {
|
|
2451
|
+
return `${nodeId}:${hashDataContext(context)}`;
|
|
2452
|
+
}
|
|
920
2453
|
function getValueByPath(context, path) {
|
|
921
2454
|
const parts = path.split(".");
|
|
922
2455
|
let current = context;
|
|
@@ -932,68 +2465,265 @@ function getValueByPath(context, path) {
|
|
|
932
2465
|
return current;
|
|
933
2466
|
}
|
|
934
2467
|
function setValueByPath(context, path, value) {
|
|
2468
|
+
if (!path) {
|
|
2469
|
+
return context;
|
|
2470
|
+
}
|
|
935
2471
|
const result = { ...context };
|
|
936
2472
|
const parts = path.split(".");
|
|
937
2473
|
let current = result;
|
|
938
2474
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
939
2475
|
const part = parts[i];
|
|
940
|
-
if (
|
|
941
|
-
|
|
2476
|
+
if (typeof current !== "object" || current === null) {
|
|
2477
|
+
console.warn(
|
|
2478
|
+
`setValueByPath: Cannot traverse path "${path}". Parent segment "${parts[i - 1] || "(root)"}" is not an object.`
|
|
2479
|
+
);
|
|
2480
|
+
return context;
|
|
942
2481
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
2482
|
+
const currentAsObject = current;
|
|
2483
|
+
const nextPartValue = currentAsObject[part];
|
|
2484
|
+
if (nextPartValue === void 0 || nextPartValue === null) {
|
|
2485
|
+
currentAsObject[part] = {};
|
|
2486
|
+
} else if (typeof nextPartValue !== "object") {
|
|
2487
|
+
console.warn(
|
|
2488
|
+
`setValueByPath: Cannot create nested path "${path}". Segment "${part}" is not an object.`
|
|
2489
|
+
);
|
|
2490
|
+
return context;
|
|
2491
|
+
} else {
|
|
2492
|
+
currentAsObject[part] = { ...nextPartValue };
|
|
946
2493
|
}
|
|
2494
|
+
current = currentAsObject[part];
|
|
947
2495
|
}
|
|
948
2496
|
const lastPart = parts[parts.length - 1];
|
|
949
|
-
current
|
|
2497
|
+
if (typeof current === "object" && current !== null) {
|
|
2498
|
+
current[lastPart] = value;
|
|
2499
|
+
} else {
|
|
2500
|
+
console.warn(
|
|
2501
|
+
`setValueByPath: Could not set value for path "${path}". Final segment parent is not an object.`
|
|
2502
|
+
);
|
|
2503
|
+
return context;
|
|
2504
|
+
}
|
|
950
2505
|
return result;
|
|
951
2506
|
}
|
|
952
|
-
function processBinding(binding, context) {
|
|
2507
|
+
function processBinding(binding, context, itemData) {
|
|
953
2508
|
if (typeof binding === "string") {
|
|
954
|
-
|
|
2509
|
+
const exactMatchArr = binding.match(/^{{(.*)}}$/);
|
|
2510
|
+
const pathInsideExact = exactMatchArr ? exactMatchArr[1].trim() : null;
|
|
2511
|
+
if (pathInsideExact !== null && !pathInsideExact.includes("{{") && !pathInsideExact.includes("}}")) {
|
|
2512
|
+
const pathToResolve = pathInsideExact;
|
|
2513
|
+
let resolvedValue = void 0;
|
|
2514
|
+
console.log(
|
|
2515
|
+
`[processBinding Debug] Processing EXACT template: "${binding}", Path: "${pathToResolve}", Has itemData: ${!!itemData}`
|
|
2516
|
+
);
|
|
2517
|
+
if (itemData) {
|
|
2518
|
+
try {
|
|
2519
|
+
console.log(
|
|
2520
|
+
`[processBinding Debug] itemData content (EXACT):`,
|
|
2521
|
+
JSON.parse(JSON.stringify(itemData))
|
|
2522
|
+
);
|
|
2523
|
+
} catch {
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
|
|
2527
|
+
if (pathToResolve.startsWith("item.")) {
|
|
2528
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
|
|
2529
|
+
} else {
|
|
2530
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
|
|
2531
|
+
}
|
|
2532
|
+
} else if (itemData && pathToResolve in itemData) {
|
|
2533
|
+
resolvedValue = getValueByPath(itemData, pathToResolve);
|
|
2534
|
+
}
|
|
2535
|
+
if (resolvedValue === void 0) {
|
|
2536
|
+
resolvedValue = getValueByPath(context, pathToResolve);
|
|
2537
|
+
}
|
|
2538
|
+
return resolvedValue;
|
|
2539
|
+
} else if (binding.includes("{{") && binding.includes("}}")) {
|
|
2540
|
+
console.log(
|
|
2541
|
+
`[processBinding Debug] Processing EMBEDDED templates: "${binding}", Has itemData: ${!!itemData}`
|
|
2542
|
+
);
|
|
2543
|
+
if (itemData) {
|
|
2544
|
+
try {
|
|
2545
|
+
console.log(
|
|
2546
|
+
`[processBinding Debug] itemData content (EMBEDDED):`,
|
|
2547
|
+
JSON.parse(JSON.stringify(itemData))
|
|
2548
|
+
);
|
|
2549
|
+
} catch {
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
const resolvedString = binding.replaceAll(
|
|
2553
|
+
/{{(.*?)}}/g,
|
|
2554
|
+
// Non-greedy match inside braces
|
|
2555
|
+
(match, path) => {
|
|
2556
|
+
const trimmedPath = path.trim();
|
|
2557
|
+
let resolvedValue = void 0;
|
|
2558
|
+
if ((trimmedPath.startsWith("item.") || trimmedPath.startsWith("row.")) && itemData) {
|
|
2559
|
+
if (trimmedPath.startsWith("item.")) {
|
|
2560
|
+
resolvedValue = getValueByPath(
|
|
2561
|
+
itemData,
|
|
2562
|
+
trimmedPath.substring(5)
|
|
2563
|
+
);
|
|
2564
|
+
} else {
|
|
2565
|
+
resolvedValue = getValueByPath(
|
|
2566
|
+
itemData,
|
|
2567
|
+
trimmedPath.substring(4)
|
|
2568
|
+
);
|
|
2569
|
+
}
|
|
2570
|
+
} else if (itemData && trimmedPath in itemData) {
|
|
2571
|
+
resolvedValue = getValueByPath(itemData, trimmedPath);
|
|
2572
|
+
}
|
|
2573
|
+
if (resolvedValue === void 0) {
|
|
2574
|
+
resolvedValue = getValueByPath(context, trimmedPath);
|
|
2575
|
+
}
|
|
2576
|
+
return resolvedValue === null || resolvedValue === void 0 ? "" : String(resolvedValue);
|
|
2577
|
+
}
|
|
2578
|
+
);
|
|
2579
|
+
return resolvedString;
|
|
2580
|
+
} else {
|
|
2581
|
+
const pathToResolve = binding;
|
|
2582
|
+
let resolvedValue = void 0;
|
|
2583
|
+
console.log(
|
|
2584
|
+
`[processBinding Debug] Processing PATH string: "${pathToResolve}", Has itemData: ${!!itemData}`
|
|
2585
|
+
);
|
|
2586
|
+
if (itemData && !pathToResolve.includes(".") && pathToResolve in itemData) {
|
|
2587
|
+
resolvedValue = getValueByPath(itemData, pathToResolve);
|
|
2588
|
+
}
|
|
2589
|
+
if (resolvedValue === void 0) {
|
|
2590
|
+
resolvedValue = getValueByPath(context, pathToResolve);
|
|
2591
|
+
}
|
|
2592
|
+
return resolvedValue;
|
|
2593
|
+
}
|
|
955
2594
|
}
|
|
956
2595
|
if (Array.isArray(binding)) {
|
|
957
|
-
return binding.map((item) => processBinding(item, context));
|
|
2596
|
+
return binding.map((item) => processBinding(item, context, itemData));
|
|
958
2597
|
}
|
|
959
2598
|
if (binding !== null && typeof binding === "object") {
|
|
960
2599
|
const result = {};
|
|
961
2600
|
for (const [key, value] of Object.entries(binding)) {
|
|
962
|
-
result[key] = processBinding(value, context);
|
|
2601
|
+
result[key] = processBinding(value, context, itemData);
|
|
963
2602
|
}
|
|
964
2603
|
return result;
|
|
965
2604
|
}
|
|
966
2605
|
return binding;
|
|
967
2606
|
}
|
|
968
|
-
async function resolveBindings(node, context) {
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
);
|
|
2607
|
+
async function resolveBindings(node, context, itemData) {
|
|
2608
|
+
const effectiveContext = itemData ? { ...context, item: itemData } : context;
|
|
2609
|
+
const currentTime = Date.now();
|
|
2610
|
+
const cacheKey = createCacheKey(node.id, effectiveContext);
|
|
2611
|
+
const cachedNode = bindingsCache.get(cacheKey);
|
|
2612
|
+
const cachedTimestamp = nodeCacheTimestamps.get(cacheKey);
|
|
2613
|
+
if (cachedNode && cachedTimestamp && currentTime - cachedTimestamp < CACHE_TTL2) {
|
|
2614
|
+
return cachedNode;
|
|
2615
|
+
}
|
|
2616
|
+
if (!itemData) {
|
|
2617
|
+
await systemEvents.emit(
|
|
2618
|
+
createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
|
|
2619
|
+
layout: node
|
|
2620
|
+
})
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
972
2623
|
const result = {
|
|
973
2624
|
...node,
|
|
974
|
-
props: node.props ?
|
|
975
|
-
events: node.events ?
|
|
2625
|
+
props: node.props ? JSON.parse(JSON.stringify(node.props)) : null,
|
|
2626
|
+
events: node.events ? JSON.parse(JSON.stringify(node.events)) : null,
|
|
2627
|
+
bindings: node.bindings ? JSON.parse(JSON.stringify(node.bindings)) : null,
|
|
2628
|
+
children: null
|
|
2629
|
+
// Initialize children to null
|
|
976
2630
|
};
|
|
2631
|
+
const resolvedBindings = {};
|
|
977
2632
|
if (node.bindings) {
|
|
978
|
-
for (const [key,
|
|
979
|
-
const
|
|
980
|
-
|
|
981
|
-
|
|
2633
|
+
for (const [key, bindingValue] of Object.entries(node.bindings)) {
|
|
2634
|
+
const resolvedValue = processBinding(bindingValue, context, itemData);
|
|
2635
|
+
resolvedBindings[key] = resolvedValue;
|
|
2636
|
+
if (resolvedValue !== void 0) {
|
|
2637
|
+
if (!result.props)
|
|
982
2638
|
result.props = {};
|
|
983
|
-
|
|
984
|
-
result.props[key] = value;
|
|
2639
|
+
result.props[key] = resolvedValue;
|
|
985
2640
|
}
|
|
986
2641
|
}
|
|
987
2642
|
}
|
|
988
|
-
|
|
989
|
-
|
|
2643
|
+
result.bindings = null;
|
|
2644
|
+
if (node.events) {
|
|
2645
|
+
result.events = processBinding(
|
|
2646
|
+
node.events,
|
|
2647
|
+
context,
|
|
2648
|
+
itemData
|
|
2649
|
+
);
|
|
2650
|
+
} else {
|
|
2651
|
+
result.events = null;
|
|
2652
|
+
}
|
|
2653
|
+
const dataBindingValue = resolvedBindings["data"] ?? resolvedBindings["items"];
|
|
2654
|
+
if ((node.node_type === "ListView" || node.node_type === "Table") && Array.isArray(dataBindingValue) && node.children && node.children.length > 0) {
|
|
2655
|
+
const templateChild = node.children[0];
|
|
2656
|
+
const mappedChildren = await Promise.all(
|
|
2657
|
+
dataBindingValue.map(async (currentItemData, index) => {
|
|
2658
|
+
try {
|
|
2659
|
+
if (typeof currentItemData !== "object" || currentItemData === null) {
|
|
2660
|
+
console.warn(
|
|
2661
|
+
`List item at index ${index} for node ${node.id} is not an object:`,
|
|
2662
|
+
currentItemData
|
|
2663
|
+
);
|
|
2664
|
+
return null;
|
|
2665
|
+
}
|
|
2666
|
+
const currentItemAsRecord = currentItemData;
|
|
2667
|
+
const itemId = currentItemAsRecord.id;
|
|
2668
|
+
const instanceId = `${templateChild.id}-${itemId || index}`;
|
|
2669
|
+
const childNodeInstance = JSON.parse(
|
|
2670
|
+
JSON.stringify(templateChild)
|
|
2671
|
+
);
|
|
2672
|
+
childNodeInstance.id = instanceId;
|
|
2673
|
+
const resolvedChild = await resolveBindings(
|
|
2674
|
+
childNodeInstance,
|
|
2675
|
+
context,
|
|
2676
|
+
currentItemAsRecord
|
|
2677
|
+
);
|
|
2678
|
+
if (!resolvedChild.props)
|
|
2679
|
+
resolvedChild.props = {};
|
|
2680
|
+
resolvedChild.props.key = itemId || `${node.id}-item-${index}`;
|
|
2681
|
+
return resolvedChild;
|
|
2682
|
+
} catch (error) {
|
|
2683
|
+
console.error(
|
|
2684
|
+
`[resolveBindings Error] Error processing item at index ${index} for node ${node.id}:`,
|
|
2685
|
+
error,
|
|
2686
|
+
"Item Data:",
|
|
2687
|
+
currentItemData
|
|
2688
|
+
);
|
|
2689
|
+
return null;
|
|
2690
|
+
}
|
|
2691
|
+
})
|
|
2692
|
+
);
|
|
2693
|
+
result.children = mappedChildren.filter(
|
|
2694
|
+
(child) => child !== null
|
|
2695
|
+
);
|
|
2696
|
+
} else if (node.children && node.children.length > 0) {
|
|
2697
|
+
result.children = await Promise.all(
|
|
2698
|
+
node.children.map((child) => resolveBindings(child, context, itemData))
|
|
2699
|
+
);
|
|
2700
|
+
} else {
|
|
2701
|
+
result.children = [];
|
|
2702
|
+
}
|
|
2703
|
+
if (!itemData) {
|
|
2704
|
+
await systemEvents.emit(
|
|
2705
|
+
createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
|
|
2706
|
+
originalLayout: node,
|
|
2707
|
+
resolvedLayout: result
|
|
2708
|
+
})
|
|
2709
|
+
);
|
|
2710
|
+
}
|
|
2711
|
+
bindingsCache.set(cacheKey, result);
|
|
2712
|
+
nodeCacheTimestamps.set(cacheKey, currentTime);
|
|
2713
|
+
if (bindingsCache.size > MAX_CACHE_SIZE2) {
|
|
2714
|
+
let oldestKey = null;
|
|
2715
|
+
let oldestTimestamp = currentTime;
|
|
2716
|
+
for (const [key, timestamp] of nodeCacheTimestamps.entries()) {
|
|
2717
|
+
if (timestamp < oldestTimestamp) {
|
|
2718
|
+
oldestTimestamp = timestamp;
|
|
2719
|
+
oldestKey = key;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
if (oldestKey) {
|
|
2723
|
+
bindingsCache.delete(oldestKey);
|
|
2724
|
+
nodeCacheTimestamps.delete(oldestKey);
|
|
2725
|
+
}
|
|
990
2726
|
}
|
|
991
|
-
await systemEvents.emit(
|
|
992
|
-
createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
|
|
993
|
-
originalLayout: node,
|
|
994
|
-
resolvedLayout: result
|
|
995
|
-
})
|
|
996
|
-
);
|
|
997
2727
|
return result;
|
|
998
2728
|
}
|
|
999
2729
|
function executeAction(action, targetId, payload, context = {}, layoutTree) {
|
|
@@ -1029,7 +2759,7 @@ var EventManager = class {
|
|
|
1029
2759
|
}
|
|
1030
2760
|
/**
|
|
1031
2761
|
* Register a hook for specific event types
|
|
1032
|
-
*
|
|
2762
|
+
*
|
|
1033
2763
|
* @param eventTypes - Event types to register for, or 'all' for all events
|
|
1034
2764
|
* @param hook - Hook function to execute
|
|
1035
2765
|
* @returns Unregister function
|
|
@@ -1062,7 +2792,7 @@ var EventManager = class {
|
|
|
1062
2792
|
}
|
|
1063
2793
|
/**
|
|
1064
2794
|
* Process an event through all registered hooks
|
|
1065
|
-
*
|
|
2795
|
+
*
|
|
1066
2796
|
* @param event - The UI event to process
|
|
1067
2797
|
* @returns Whether the default action should proceed
|
|
1068
2798
|
*/
|
|
@@ -1108,6 +2838,45 @@ function createEventHook(eventTypes, hook, options) {
|
|
|
1108
2838
|
}
|
|
1109
2839
|
};
|
|
1110
2840
|
}
|
|
2841
|
+
|
|
2842
|
+
// src/core/component-detection.ts
|
|
2843
|
+
function areShadcnComponentsAvailable() {
|
|
2844
|
+
try {
|
|
2845
|
+
init_button();
|
|
2846
|
+
return true;
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
return false;
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
function getMissingComponentsMessage() {
|
|
2852
|
+
return `Missing required shadcn components. Please run:
|
|
2853
|
+
> npm run setup-shadcn`;
|
|
2854
|
+
}
|
|
2855
|
+
function correctListBindingsRecursive(node, dataContext) {
|
|
2856
|
+
const correctedNode = JSON.parse(JSON.stringify(node));
|
|
2857
|
+
if ((correctedNode.node_type === "ListView" || correctedNode.node_type === "Table") && correctedNode.bindings?.data) {
|
|
2858
|
+
const bindingPath = correctedNode.bindings.data;
|
|
2859
|
+
if (typeof bindingPath === "string") {
|
|
2860
|
+
const pathSegments = bindingPath.split(".");
|
|
2861
|
+
const mainKey = pathSegments[0];
|
|
2862
|
+
if (pathSegments.length === 1) {
|
|
2863
|
+
const potentialDataContextEntry = dataContext[mainKey];
|
|
2864
|
+
if (potentialDataContextEntry && typeof potentialDataContextEntry === "object" && potentialDataContextEntry !== null && "data" in potentialDataContextEntry && Array.isArray(potentialDataContextEntry.data)) {
|
|
2865
|
+
correctedNode.bindings.data = `${mainKey}.data`;
|
|
2866
|
+
console.log(
|
|
2867
|
+
`[AutoUI Debug] Corrected list binding for node '${correctedNode.id}': from '${mainKey}' to '${mainKey}.data'`
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (correctedNode.children) {
|
|
2874
|
+
correctedNode.children = correctedNode.children.map(
|
|
2875
|
+
(child) => correctListBindingsRecursive(child, dataContext)
|
|
2876
|
+
);
|
|
2877
|
+
}
|
|
2878
|
+
return correctedNode;
|
|
2879
|
+
}
|
|
1111
2880
|
var AutoUI = ({
|
|
1112
2881
|
schema,
|
|
1113
2882
|
goal,
|
|
@@ -1117,25 +2886,34 @@ var AutoUI = ({
|
|
|
1117
2886
|
eventHooks,
|
|
1118
2887
|
systemEventHooks,
|
|
1119
2888
|
debugMode = false,
|
|
1120
|
-
mockMode =
|
|
1121
|
-
databaseConfig,
|
|
2889
|
+
mockMode = false,
|
|
1122
2890
|
planningConfig,
|
|
1123
2891
|
integration = {},
|
|
1124
2892
|
scope = {},
|
|
1125
|
-
enablePartialUpdates = false
|
|
2893
|
+
enablePartialUpdates = false,
|
|
2894
|
+
openaiApiKey
|
|
1126
2895
|
}) => {
|
|
1127
|
-
const [schemaAdapterInstance
|
|
1128
|
-
const [dataContext, setDataContext] =
|
|
2896
|
+
const [schemaAdapterInstance] = React.useState(null);
|
|
2897
|
+
const [dataContext, setDataContext] = React.useState({});
|
|
2898
|
+
const [componentsAvailable, setComponentsAvailable] = React.useState(true);
|
|
1129
2899
|
const effectiveSchema = schema;
|
|
1130
2900
|
const scopedGoal = goal;
|
|
1131
|
-
|
|
2901
|
+
React.useEffect(() => {
|
|
2902
|
+
if (componentAdapter === "shadcn") {
|
|
2903
|
+
setComponentsAvailable(areShadcnComponentsAvailable());
|
|
2904
|
+
}
|
|
2905
|
+
}, [componentAdapter]);
|
|
2906
|
+
React.useEffect(() => {
|
|
1132
2907
|
const unregisters = [];
|
|
1133
2908
|
if (systemEventHooks) {
|
|
1134
2909
|
Object.entries(systemEventHooks).forEach(([eventType, hooks]) => {
|
|
1135
2910
|
if (!hooks)
|
|
1136
2911
|
return;
|
|
1137
2912
|
hooks.forEach((hook) => {
|
|
1138
|
-
const unregister = systemEvents.on(
|
|
2913
|
+
const unregister = systemEvents.on(
|
|
2914
|
+
eventType,
|
|
2915
|
+
hook
|
|
2916
|
+
);
|
|
1139
2917
|
unregisters.push(unregister);
|
|
1140
2918
|
});
|
|
1141
2919
|
});
|
|
@@ -1144,7 +2922,9 @@ var AutoUI = ({
|
|
|
1144
2922
|
const debugHook = (event) => {
|
|
1145
2923
|
console.debug(`[AutoUI Debug] System Event:`, event);
|
|
1146
2924
|
};
|
|
1147
|
-
Object.values(SystemEventType).
|
|
2925
|
+
Object.values(SystemEventType).filter(
|
|
2926
|
+
(eventType) => eventType !== "RENDER_START" /* RENDER_START */ && eventType !== "BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */
|
|
2927
|
+
).forEach((eventType) => {
|
|
1148
2928
|
const unregister = systemEvents.on(eventType, debugHook);
|
|
1149
2929
|
unregisters.push(unregister);
|
|
1150
2930
|
});
|
|
@@ -1153,7 +2933,7 @@ var AutoUI = ({
|
|
|
1153
2933
|
unregisters.forEach((unregister) => unregister());
|
|
1154
2934
|
};
|
|
1155
2935
|
}, [systemEventHooks, debugMode]);
|
|
1156
|
-
|
|
2936
|
+
React.useEffect(() => {
|
|
1157
2937
|
const initializeDataContext = async () => {
|
|
1158
2938
|
let initialData = {};
|
|
1159
2939
|
if (schemaAdapterInstance) {
|
|
@@ -1183,103 +2963,162 @@ var AutoUI = ({
|
|
|
1183
2963
|
planningConfig,
|
|
1184
2964
|
router: void 0,
|
|
1185
2965
|
dataContext,
|
|
1186
|
-
enablePartialUpdates
|
|
2966
|
+
enablePartialUpdates,
|
|
2967
|
+
openaiApiKey
|
|
1187
2968
|
});
|
|
1188
|
-
const eventManagerRef =
|
|
1189
|
-
|
|
2969
|
+
const eventManagerRef = React.useRef(new EventManager());
|
|
2970
|
+
React.useEffect(() => {
|
|
1190
2971
|
if (!eventHooks)
|
|
1191
2972
|
return;
|
|
1192
2973
|
const unregisters = [];
|
|
1193
2974
|
if (eventHooks.all) {
|
|
1194
|
-
const unregister = eventManagerRef.current.register(
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
2975
|
+
const unregister = eventManagerRef.current.register(
|
|
2976
|
+
"all",
|
|
2977
|
+
async (ctx) => {
|
|
2978
|
+
for (const hook of eventHooks.all || []) {
|
|
2979
|
+
await hook(ctx);
|
|
2980
|
+
if (ctx.isPropagationStopped())
|
|
2981
|
+
break;
|
|
2982
|
+
}
|
|
1199
2983
|
}
|
|
1200
|
-
|
|
2984
|
+
);
|
|
1201
2985
|
unregisters.push(unregister);
|
|
1202
2986
|
}
|
|
1203
2987
|
Object.entries(eventHooks).forEach(([type, hooks]) => {
|
|
1204
2988
|
if (type === "all" || !hooks)
|
|
1205
2989
|
return;
|
|
1206
|
-
const unregister = eventManagerRef.current.register(
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
2990
|
+
const unregister = eventManagerRef.current.register(
|
|
2991
|
+
[type],
|
|
2992
|
+
async (ctx) => {
|
|
2993
|
+
for (const hook of hooks) {
|
|
2994
|
+
await hook(ctx);
|
|
2995
|
+
if (ctx.isPropagationStopped())
|
|
2996
|
+
break;
|
|
2997
|
+
}
|
|
1211
2998
|
}
|
|
1212
|
-
|
|
2999
|
+
);
|
|
1213
3000
|
unregisters.push(unregister);
|
|
1214
3001
|
});
|
|
1215
3002
|
return () => {
|
|
1216
3003
|
unregisters.forEach((unregister) => unregister());
|
|
1217
3004
|
};
|
|
1218
3005
|
}, [eventHooks]);
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
onEvent
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
3006
|
+
const processEvent2 = React.useCallback(
|
|
3007
|
+
async (event) => {
|
|
3008
|
+
const shouldProceed = await eventManagerRef.current.processEvent(event);
|
|
3009
|
+
if (onEvent) {
|
|
3010
|
+
onEvent(event);
|
|
3011
|
+
}
|
|
3012
|
+
if (!shouldProceed) {
|
|
3013
|
+
console.info("Event processing was prevented by hooks", event);
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
const findNodeById2 = (node, id) => {
|
|
3017
|
+
if (!node)
|
|
3018
|
+
return void 0;
|
|
3019
|
+
if (node.id === id)
|
|
3020
|
+
return node;
|
|
3021
|
+
if (node.children) {
|
|
3022
|
+
for (const child of node.children) {
|
|
3023
|
+
const found = findNodeById2(child, id);
|
|
3024
|
+
if (found)
|
|
3025
|
+
return found;
|
|
3026
|
+
}
|
|
1238
3027
|
}
|
|
3028
|
+
return void 0;
|
|
3029
|
+
};
|
|
3030
|
+
const sourceNode = findNodeById2(state.layout, event.nodeId);
|
|
3031
|
+
if (!sourceNode) {
|
|
3032
|
+
console.warn(`Node not found for event: ${event.nodeId}`);
|
|
3033
|
+
handleEvent(event);
|
|
3034
|
+
return;
|
|
1239
3035
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
3036
|
+
const eventConfig = sourceNode.events?.[event.type];
|
|
3037
|
+
if (!eventConfig) {
|
|
3038
|
+
console.warn(
|
|
3039
|
+
`No event config found for ${event.type} on node ${event.nodeId}`
|
|
3040
|
+
);
|
|
3041
|
+
handleEvent(event);
|
|
3042
|
+
return;
|
|
3043
|
+
}
|
|
3044
|
+
const newContext = executeAction(
|
|
3045
|
+
eventConfig.action,
|
|
3046
|
+
eventConfig.target || "",
|
|
3047
|
+
// Provide empty string as fallback if target is null
|
|
3048
|
+
{
|
|
3049
|
+
...eventConfig.payload,
|
|
3050
|
+
...event.payload
|
|
3051
|
+
},
|
|
3052
|
+
dataContext,
|
|
3053
|
+
state.layout || void 0
|
|
3054
|
+
);
|
|
3055
|
+
setDataContext(newContext);
|
|
1251
3056
|
handleEvent(event);
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
3057
|
+
},
|
|
3058
|
+
[dataContext, handleEvent, onEvent, state.layout]
|
|
3059
|
+
);
|
|
3060
|
+
const [resolvedLayout, setResolvedLayout] = React.useState(
|
|
3061
|
+
void 0
|
|
3062
|
+
);
|
|
3063
|
+
const [renderedNode, setRenderedNode] = React.useState(
|
|
3064
|
+
null
|
|
3065
|
+
);
|
|
3066
|
+
const resolveLayoutBindings = React.useCallback(async () => {
|
|
3067
|
+
if (state.layout && dataContext) {
|
|
3068
|
+
console.log(
|
|
3069
|
+
"[AutoUI Debug] DataContext before resolving bindings:",
|
|
3070
|
+
JSON.stringify(dataContext, null, 2)
|
|
3071
|
+
);
|
|
3072
|
+
console.log(
|
|
3073
|
+
"[AutoUI Debug] Raw layout before resolving (from planner):",
|
|
3074
|
+
JSON.stringify(state.layout, null, 2)
|
|
3075
|
+
);
|
|
3076
|
+
const correctedLayout = correctListBindingsRecursive(
|
|
3077
|
+
state.layout,
|
|
3078
|
+
dataContext
|
|
3079
|
+
);
|
|
3080
|
+
console.log(
|
|
3081
|
+
"[AutoUI Debug] Layout after binding correction (before resolving):",
|
|
3082
|
+
JSON.stringify(correctedLayout, null, 2)
|
|
3083
|
+
);
|
|
3084
|
+
const resolved = await resolveBindings(correctedLayout, dataContext);
|
|
3085
|
+
setResolvedLayout(resolved);
|
|
3086
|
+
console.log(
|
|
3087
|
+
"[AutoUI Debug] Resolved layout after bindings:",
|
|
3088
|
+
JSON.stringify(resolved, null, 2)
|
|
3089
|
+
);
|
|
1272
3090
|
} else {
|
|
1273
3091
|
setResolvedLayout(void 0);
|
|
1274
3092
|
}
|
|
1275
3093
|
}, [state.layout, dataContext]);
|
|
1276
|
-
|
|
3094
|
+
React.useEffect(() => {
|
|
3095
|
+
resolveLayoutBindings();
|
|
3096
|
+
}, [resolveLayoutBindings]);
|
|
3097
|
+
const renderResolvedLayout = React.useCallback(async () => {
|
|
1277
3098
|
if (resolvedLayout) {
|
|
1278
|
-
|
|
3099
|
+
try {
|
|
3100
|
+
const rendered = await renderNode2(
|
|
3101
|
+
resolvedLayout,
|
|
3102
|
+
componentAdapter,
|
|
3103
|
+
processEvent2
|
|
3104
|
+
);
|
|
3105
|
+
setRenderedNode(rendered);
|
|
3106
|
+
} catch (err) {
|
|
3107
|
+
console.error("Error rendering node:", err);
|
|
3108
|
+
}
|
|
1279
3109
|
} else {
|
|
1280
3110
|
setRenderedNode(null);
|
|
1281
3111
|
}
|
|
1282
|
-
}, [resolvedLayout, componentAdapter]);
|
|
3112
|
+
}, [resolvedLayout, componentAdapter, processEvent2]);
|
|
3113
|
+
React.useEffect(() => {
|
|
3114
|
+
renderResolvedLayout();
|
|
3115
|
+
}, [renderResolvedLayout]);
|
|
3116
|
+
if (!componentsAvailable) {
|
|
3117
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 text-red-700 rounded", children: [
|
|
3118
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Component Library Not Found" }),
|
|
3119
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-line", children: getMissingComponentsMessage() })
|
|
3120
|
+
] });
|
|
3121
|
+
}
|
|
1283
3122
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1284
3123
|
"div",
|
|
1285
3124
|
{
|
|
@@ -1298,9 +3137,22 @@ var AutoUI = ({
|
|
|
1298
3137
|
// Render the resolved layout
|
|
1299
3138
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderedNode })
|
|
1300
3139
|
),
|
|
1301
|
-
state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error", children: [
|
|
1302
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-title", children: "Error generating UI" }),
|
|
1303
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message", children: state.error })
|
|
3140
|
+
state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 dark:bg-red-900 dark:border-red-700 rounded-md", children: [
|
|
3141
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-title text-lg font-semibold text-red-700 dark:text-red-300 mb-2", children: "Error generating UI" }),
|
|
3142
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message text-sm text-red-600 dark:text-red-300", children: state.error }),
|
|
3143
|
+
!mockMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 text-sm text-red-600 dark:text-red-300", children: [
|
|
3144
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "This could be because:" }),
|
|
3145
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "list-disc pl-5 mt-2", children: [
|
|
3146
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your OpenAI API key is missing or invalid" }),
|
|
3147
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "The OpenAI service is experiencing issues" }),
|
|
3148
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your API rate limit has been exceeded" })
|
|
3149
|
+
] }),
|
|
3150
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2", children: [
|
|
3151
|
+
"Try setting ",
|
|
3152
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "mockMode=true" }),
|
|
3153
|
+
" to use sample data instead."
|
|
3154
|
+
] })
|
|
3155
|
+
] })
|
|
1304
3156
|
] })
|
|
1305
3157
|
]
|
|
1306
3158
|
}
|
|
@@ -1353,26 +3205,26 @@ var DrizzleAdapter = class {
|
|
|
1353
3205
|
*/
|
|
1354
3206
|
mapDataType(drizzleType) {
|
|
1355
3207
|
const typeMap = {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
3208
|
+
serial: "integer",
|
|
3209
|
+
integer: "integer",
|
|
3210
|
+
int: "integer",
|
|
3211
|
+
bigint: "integer",
|
|
3212
|
+
text: "string",
|
|
3213
|
+
varchar: "string",
|
|
3214
|
+
char: "string",
|
|
3215
|
+
boolean: "boolean",
|
|
3216
|
+
bool: "boolean",
|
|
3217
|
+
timestamp: "datetime",
|
|
3218
|
+
timestamptz: "datetime",
|
|
3219
|
+
date: "date",
|
|
3220
|
+
time: "time",
|
|
3221
|
+
json: "object",
|
|
3222
|
+
jsonb: "object",
|
|
3223
|
+
real: "number",
|
|
3224
|
+
float: "number",
|
|
3225
|
+
double: "number",
|
|
3226
|
+
numeric: "number",
|
|
3227
|
+
decimal: "number"
|
|
1376
3228
|
};
|
|
1377
3229
|
return typeMap[drizzleType.toLowerCase()] || "string";
|
|
1378
3230
|
}
|
|
@@ -1415,35 +3267,120 @@ function createSchemaAdapter(options) {
|
|
|
1415
3267
|
case "custom":
|
|
1416
3268
|
return options.adapter;
|
|
1417
3269
|
default:
|
|
1418
|
-
throw new Error(
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
async function generateComponent(schema) {
|
|
1422
|
-
try {
|
|
1423
|
-
const { text } = await ai.generateText({
|
|
1424
|
-
model: openai.openai("gpt-4"),
|
|
1425
|
-
prompt: `Generate a React component based on this data schema: ${JSON.stringify(schema)}`
|
|
1426
|
-
});
|
|
1427
|
-
return text;
|
|
1428
|
-
} catch (error) {
|
|
1429
|
-
console.error("Error generating component:", error);
|
|
1430
|
-
throw error;
|
|
3270
|
+
throw new Error(
|
|
3271
|
+
`Unsupported schema adapter type: ${options.type}`
|
|
3272
|
+
);
|
|
1431
3273
|
}
|
|
1432
3274
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
return
|
|
1440
|
-
}
|
|
1441
|
-
async
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
}
|
|
1446
|
-
|
|
3275
|
+
|
|
3276
|
+
// src/ai-utils.ts
|
|
3277
|
+
var generateComponent = async (prompt) => {
|
|
3278
|
+
console.warn(
|
|
3279
|
+
"generateComponent is a placeholder and will be implemented in a future version"
|
|
3280
|
+
);
|
|
3281
|
+
return `<div>Generated Component for: ${prompt}</div>`;
|
|
3282
|
+
};
|
|
3283
|
+
var generateUIDescription = async (prompt) => {
|
|
3284
|
+
console.warn(
|
|
3285
|
+
"generateUIDescription is a placeholder and will be implemented in a future version"
|
|
3286
|
+
);
|
|
3287
|
+
return `Description for ${prompt}`;
|
|
3288
|
+
};
|
|
3289
|
+
var generateUIComponent = async (prompt) => {
|
|
3290
|
+
console.warn(
|
|
3291
|
+
"generateUIComponent is a placeholder and will be implemented in a future version"
|
|
3292
|
+
);
|
|
3293
|
+
return `<div>Generated UI Component for: ${prompt}</div>`;
|
|
3294
|
+
};
|
|
3295
|
+
function usePlanner(options) {
|
|
3296
|
+
const {
|
|
3297
|
+
schema,
|
|
3298
|
+
goal,
|
|
3299
|
+
openaiApiKey,
|
|
3300
|
+
userContext,
|
|
3301
|
+
initialLayout,
|
|
3302
|
+
mockMode: optionsMockMode
|
|
3303
|
+
} = options;
|
|
3304
|
+
const [layout, setLayout] = React.useState(
|
|
3305
|
+
initialLayout || void 0
|
|
3306
|
+
);
|
|
3307
|
+
const [loading, setLoading] = React.useState(false);
|
|
3308
|
+
const [error, setError] = React.useState(null);
|
|
3309
|
+
const router = options.router || createDefaultRouter();
|
|
3310
|
+
const dataContext = {};
|
|
3311
|
+
const initialFetchAttempted = React.useRef(false);
|
|
3312
|
+
const mockMode = optionsMockMode || !openaiApiKey;
|
|
3313
|
+
const generateInitialLayout = React.useCallback(async () => {
|
|
3314
|
+
setLoading(true);
|
|
3315
|
+
setError(null);
|
|
3316
|
+
try {
|
|
3317
|
+
const plannerInput2 = {
|
|
3318
|
+
schema,
|
|
3319
|
+
goal,
|
|
3320
|
+
userContext: userContext || null,
|
|
3321
|
+
history: null
|
|
3322
|
+
};
|
|
3323
|
+
let generatedLayout;
|
|
3324
|
+
if (mockMode) {
|
|
3325
|
+
console.warn(
|
|
3326
|
+
"Using mock planner in usePlanner hook (mockMode enabled or API key missing)."
|
|
3327
|
+
);
|
|
3328
|
+
generatedLayout = mockPlanner(plannerInput2);
|
|
3329
|
+
} else {
|
|
3330
|
+
generatedLayout = await callPlannerLLM(
|
|
3331
|
+
plannerInput2,
|
|
3332
|
+
openaiApiKey,
|
|
3333
|
+
void 0
|
|
3334
|
+
);
|
|
3335
|
+
}
|
|
3336
|
+
setLayout(generatedLayout);
|
|
3337
|
+
} catch (err) {
|
|
3338
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3339
|
+
} finally {
|
|
3340
|
+
setLoading(false);
|
|
3341
|
+
}
|
|
3342
|
+
}, [schema, goal, userContext, openaiApiKey, mockMode]);
|
|
3343
|
+
const handleEvent = React.useCallback(
|
|
3344
|
+
async (event) => {
|
|
3345
|
+
if (!layout) {
|
|
3346
|
+
setError(new Error("Cannot handle event - no layout exists"));
|
|
3347
|
+
return;
|
|
3348
|
+
}
|
|
3349
|
+
setLoading(true);
|
|
3350
|
+
setError(null);
|
|
3351
|
+
try {
|
|
3352
|
+
const updatedLayout = await processEvent(
|
|
3353
|
+
event,
|
|
3354
|
+
router,
|
|
3355
|
+
schema,
|
|
3356
|
+
layout,
|
|
3357
|
+
dataContext,
|
|
3358
|
+
goal,
|
|
3359
|
+
userContext,
|
|
3360
|
+
openaiApiKey
|
|
3361
|
+
);
|
|
3362
|
+
setLayout(updatedLayout);
|
|
3363
|
+
} catch (err) {
|
|
3364
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3365
|
+
} finally {
|
|
3366
|
+
setLoading(false);
|
|
3367
|
+
}
|
|
3368
|
+
},
|
|
3369
|
+
[layout, router, schema, dataContext, goal, userContext, openaiApiKey]
|
|
3370
|
+
);
|
|
3371
|
+
React.useEffect(() => {
|
|
3372
|
+
if (options.initialLayout === void 0 && layout === void 0 && !initialFetchAttempted.current) {
|
|
3373
|
+
initialFetchAttempted.current = true;
|
|
3374
|
+
generateInitialLayout();
|
|
3375
|
+
}
|
|
3376
|
+
}, [options.initialLayout, layout, generateInitialLayout]);
|
|
3377
|
+
return {
|
|
3378
|
+
layout,
|
|
3379
|
+
loading,
|
|
3380
|
+
error,
|
|
3381
|
+
handleEvent,
|
|
3382
|
+
generateInitialLayout
|
|
3383
|
+
};
|
|
1447
3384
|
}
|
|
1448
3385
|
|
|
1449
3386
|
exports.ActionRouter = ActionRouter;
|
|
@@ -1462,5 +3399,6 @@ exports.systemEvents = systemEvents;
|
|
|
1462
3399
|
exports.uiEvent = uiEvent;
|
|
1463
3400
|
exports.uiEventType = uiEventType;
|
|
1464
3401
|
exports.uiSpecNode = uiSpecNode;
|
|
3402
|
+
exports.usePlanner = usePlanner;
|
|
1465
3403
|
//# sourceMappingURL=out.js.map
|
|
1466
3404
|
//# sourceMappingURL=index.js.map
|