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