autoui-react 0.0.3-alpha → 0.0.4-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 +98 -76
- package/dist/index.d.ts +98 -76
- package/dist/index.js +1116 -448
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1105 -442
- package/dist/index.mjs.map +1 -1
- package/package.json +40 -6
package/dist/index.js
CHANGED
|
@@ -1,19 +1,99 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var clsx = require('clsx');
|
|
4
|
+
var tailwindMerge = require('tailwind-merge');
|
|
5
|
+
var reactSlot = require('@radix-ui/react-slot');
|
|
6
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
5
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var
|
|
8
|
+
var React = require('react');
|
|
7
9
|
var openai = require('@ai-sdk/openai');
|
|
10
|
+
var ai = require('ai');
|
|
11
|
+
var zod = require('zod');
|
|
12
|
+
|
|
13
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
|
|
15
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
16
|
+
|
|
17
|
+
var __defProp = Object.defineProperty;
|
|
18
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
19
|
+
var __esm = (fn, res) => function __init() {
|
|
20
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
21
|
+
};
|
|
22
|
+
var __export = (target, all) => {
|
|
23
|
+
for (var name in all)
|
|
24
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
25
|
+
};
|
|
26
|
+
function cn(...inputs) {
|
|
27
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
28
|
+
}
|
|
29
|
+
var init_utils = __esm({
|
|
30
|
+
"src/lib/utils.ts"() {
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// components/ui/button.tsx
|
|
35
|
+
var button_exports = {};
|
|
36
|
+
__export(button_exports, {
|
|
37
|
+
Button: () => Button2,
|
|
38
|
+
buttonVariants: () => buttonVariants
|
|
39
|
+
});
|
|
40
|
+
function Button2({
|
|
41
|
+
className,
|
|
42
|
+
variant,
|
|
43
|
+
size,
|
|
44
|
+
asChild = false,
|
|
45
|
+
...props
|
|
46
|
+
}) {
|
|
47
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
48
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
49
|
+
Comp,
|
|
50
|
+
{
|
|
51
|
+
"data-slot": "button",
|
|
52
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
53
|
+
...props
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
var buttonVariants;
|
|
58
|
+
var init_button = __esm({
|
|
59
|
+
"components/ui/button.tsx"() {
|
|
60
|
+
init_utils();
|
|
61
|
+
buttonVariants = classVarianceAuthority.cva(
|
|
62
|
+
"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",
|
|
63
|
+
{
|
|
64
|
+
variants: {
|
|
65
|
+
variant: {
|
|
66
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
67
|
+
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",
|
|
68
|
+
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",
|
|
69
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
70
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
71
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
72
|
+
},
|
|
73
|
+
size: {
|
|
74
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
75
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
76
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
77
|
+
icon: "size-9"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
defaultVariants: {
|
|
81
|
+
variant: "default",
|
|
82
|
+
size: "default"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
8
88
|
|
|
9
89
|
// src/core/reducer.ts
|
|
10
90
|
function cloneNode(node) {
|
|
11
91
|
return {
|
|
12
92
|
...node,
|
|
13
|
-
props: node.props ? { ...node.props } :
|
|
14
|
-
bindings: node.bindings ? { ...node.bindings } :
|
|
15
|
-
events: node.events ? { ...node.events } :
|
|
16
|
-
children: node.children
|
|
93
|
+
props: node.props ? { ...node.props } : null,
|
|
94
|
+
bindings: node.bindings ? { ...node.bindings } : null,
|
|
95
|
+
events: node.events ? { ...node.events } : null,
|
|
96
|
+
children: node.children ? node.children.map((child) => cloneNode(child)) : null
|
|
17
97
|
};
|
|
18
98
|
}
|
|
19
99
|
function findNodeById(tree, nodeId) {
|
|
@@ -57,18 +137,14 @@ function updateNodeById(tree, nodeId, updater) {
|
|
|
57
137
|
const parent = path[path.length - 2];
|
|
58
138
|
const updatedParent = {
|
|
59
139
|
...parent,
|
|
60
|
-
children: parent.children
|
|
140
|
+
children: parent.children ? parent.children.map(
|
|
61
141
|
(child) => child.id === nodeId ? updatedNode : child
|
|
62
|
-
)
|
|
142
|
+
) : null
|
|
63
143
|
};
|
|
64
144
|
if (path.length === 2) {
|
|
65
145
|
return updatedParent;
|
|
66
146
|
}
|
|
67
|
-
return updateNodeById(
|
|
68
|
-
result,
|
|
69
|
-
parent.id,
|
|
70
|
-
() => updatedParent
|
|
71
|
-
);
|
|
147
|
+
return updateNodeById(result, parent.id, () => updatedParent);
|
|
72
148
|
}
|
|
73
149
|
function replaceNodeById(tree, nodeId, newNode) {
|
|
74
150
|
return updateNodeById(tree, nodeId, () => newNode);
|
|
@@ -110,7 +186,7 @@ function removeNodeById(tree, nodeId) {
|
|
|
110
186
|
return result;
|
|
111
187
|
return updateNodeById(result, parent.id, (node) => ({
|
|
112
188
|
...node,
|
|
113
|
-
children: node.children
|
|
189
|
+
children: node.children ? node.children.filter((child) => child.id !== nodeId) : null
|
|
114
190
|
}));
|
|
115
191
|
}
|
|
116
192
|
function uiReducer(state, action) {
|
|
@@ -127,7 +203,7 @@ function uiReducer(state, action) {
|
|
|
127
203
|
...state,
|
|
128
204
|
layout: action.node,
|
|
129
205
|
loading: false,
|
|
130
|
-
error:
|
|
206
|
+
error: null
|
|
131
207
|
};
|
|
132
208
|
}
|
|
133
209
|
case "PARTIAL_UPDATE": {
|
|
@@ -136,7 +212,7 @@ function uiReducer(state, action) {
|
|
|
136
212
|
...state,
|
|
137
213
|
layout: action.node,
|
|
138
214
|
loading: false,
|
|
139
|
-
error:
|
|
215
|
+
error: null
|
|
140
216
|
};
|
|
141
217
|
}
|
|
142
218
|
if (action.nodeId === "root" || action.nodeId === state.layout.id) {
|
|
@@ -144,19 +220,23 @@ function uiReducer(state, action) {
|
|
|
144
220
|
...state,
|
|
145
221
|
layout: action.node,
|
|
146
222
|
loading: false,
|
|
147
|
-
error:
|
|
223
|
+
error: null
|
|
148
224
|
};
|
|
149
225
|
}
|
|
150
226
|
return {
|
|
151
227
|
...state,
|
|
152
228
|
layout: replaceNodeById(state.layout, action.nodeId, action.node),
|
|
153
229
|
loading: false,
|
|
154
|
-
error:
|
|
230
|
+
error: null
|
|
155
231
|
};
|
|
156
232
|
}
|
|
157
233
|
case "ADD_NODE": {
|
|
158
234
|
if (!state.layout) {
|
|
159
|
-
return
|
|
235
|
+
return {
|
|
236
|
+
...state,
|
|
237
|
+
error: "Cannot add node: Layout is empty.",
|
|
238
|
+
loading: false
|
|
239
|
+
};
|
|
160
240
|
}
|
|
161
241
|
return {
|
|
162
242
|
...state,
|
|
@@ -164,21 +244,41 @@ function uiReducer(state, action) {
|
|
|
164
244
|
state.layout,
|
|
165
245
|
action.parentId,
|
|
166
246
|
action.node,
|
|
167
|
-
action.index
|
|
247
|
+
action.index === null ? void 0 : action.index
|
|
168
248
|
),
|
|
169
249
|
loading: false,
|
|
170
|
-
error:
|
|
250
|
+
error: null
|
|
171
251
|
};
|
|
172
252
|
}
|
|
173
253
|
case "REMOVE_NODE": {
|
|
174
254
|
if (!state.layout) {
|
|
175
|
-
return
|
|
255
|
+
return {
|
|
256
|
+
...state,
|
|
257
|
+
error: "Cannot remove node: Layout is empty.",
|
|
258
|
+
loading: false
|
|
259
|
+
};
|
|
176
260
|
}
|
|
261
|
+
try {
|
|
262
|
+
return {
|
|
263
|
+
...state,
|
|
264
|
+
layout: removeNodeById(state.layout, action.nodeId),
|
|
265
|
+
loading: false,
|
|
266
|
+
error: null
|
|
267
|
+
};
|
|
268
|
+
} catch (e) {
|
|
269
|
+
const errorMessage = e instanceof Error ? e.message : "Failed to remove node.";
|
|
270
|
+
return {
|
|
271
|
+
...state,
|
|
272
|
+
error: errorMessage,
|
|
273
|
+
loading: false
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
case "ERROR": {
|
|
177
278
|
return {
|
|
178
279
|
...state,
|
|
179
|
-
|
|
180
|
-
loading: false
|
|
181
|
-
error: void 0
|
|
280
|
+
error: action.message,
|
|
281
|
+
loading: false
|
|
182
282
|
};
|
|
183
283
|
}
|
|
184
284
|
case "LOADING": {
|
|
@@ -187,20 +287,15 @@ function uiReducer(state, action) {
|
|
|
187
287
|
loading: action.isLoading
|
|
188
288
|
};
|
|
189
289
|
}
|
|
190
|
-
case "ERROR": {
|
|
191
|
-
return {
|
|
192
|
-
...state,
|
|
193
|
-
error: action.message,
|
|
194
|
-
loading: false
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
290
|
default:
|
|
198
291
|
return state;
|
|
199
292
|
}
|
|
200
293
|
}
|
|
201
294
|
var initialState = {
|
|
202
|
-
|
|
203
|
-
|
|
295
|
+
layout: null,
|
|
296
|
+
loading: false,
|
|
297
|
+
history: [],
|
|
298
|
+
error: null
|
|
204
299
|
};
|
|
205
300
|
|
|
206
301
|
// src/core/action-router.ts
|
|
@@ -247,7 +342,7 @@ var ActionRouter = class {
|
|
|
247
342
|
schema,
|
|
248
343
|
goal,
|
|
249
344
|
history: [event],
|
|
250
|
-
userContext
|
|
345
|
+
userContext: userContext || null
|
|
251
346
|
},
|
|
252
347
|
prompt: `Generate a new UI for the goal: "${goal}". The user just triggered: ${event.type} on node ${event.nodeId}`
|
|
253
348
|
};
|
|
@@ -296,16 +391,20 @@ var ActionRouter = class {
|
|
|
296
391
|
...additionalContext
|
|
297
392
|
}
|
|
298
393
|
};
|
|
394
|
+
const templateValues = {
|
|
395
|
+
goal,
|
|
396
|
+
eventType: event.type,
|
|
397
|
+
nodeId: event.nodeId,
|
|
398
|
+
targetNodeId,
|
|
399
|
+
actionType: matchingRoute.actionType,
|
|
400
|
+
...userContext || {},
|
|
401
|
+
// Spread the original userContext (passed to resolveRoute)
|
|
402
|
+
...additionalContext
|
|
403
|
+
// Spread additionalContext afterwards (can override userContext keys)
|
|
404
|
+
};
|
|
299
405
|
const prompt = this.processTemplate(
|
|
300
406
|
matchingRoute.promptTemplate,
|
|
301
|
-
|
|
302
|
-
goal,
|
|
303
|
-
eventType: event.type,
|
|
304
|
-
nodeId: event.nodeId,
|
|
305
|
-
targetNodeId,
|
|
306
|
-
actionType: matchingRoute.actionType,
|
|
307
|
-
...additionalContext
|
|
308
|
-
}
|
|
407
|
+
templateValues
|
|
309
408
|
);
|
|
310
409
|
return {
|
|
311
410
|
actionType: matchingRoute.actionType,
|
|
@@ -373,27 +472,58 @@ var uiEventType = zod.z.enum([
|
|
|
373
472
|
var uiEvent = zod.z.object({
|
|
374
473
|
type: uiEventType,
|
|
375
474
|
nodeId: zod.z.string(),
|
|
376
|
-
timestamp: zod.z.number().
|
|
377
|
-
payload: zod.z.record(zod.z.
|
|
475
|
+
timestamp: zod.z.number().nullable(),
|
|
476
|
+
payload: zod.z.record(zod.z.unknown()).nullable()
|
|
378
477
|
});
|
|
379
|
-
zod.z.enum([
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
var
|
|
478
|
+
zod.z.enum(["AI_RESPONSE", "ERROR"]);
|
|
479
|
+
var runtimeRecord = zod.z.record(zod.z.any()).nullable();
|
|
480
|
+
var openAISimplifiedValue = zod.z.string().nullable();
|
|
481
|
+
var openAIRecordSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
482
|
+
var openAIEventPayloadSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
483
|
+
var openAIBaseNode = zod.z.object({
|
|
484
|
+
id: zod.z.string(),
|
|
485
|
+
node_type: zod.z.string(),
|
|
486
|
+
props: openAIRecordSimplifiedNullable,
|
|
487
|
+
// Nullable record
|
|
488
|
+
bindings: openAIRecordSimplifiedNullable,
|
|
489
|
+
// Nullable record
|
|
490
|
+
events: zod.z.record(
|
|
491
|
+
zod.z.string(),
|
|
492
|
+
zod.z.object({
|
|
493
|
+
action: zod.z.string(),
|
|
494
|
+
target: zod.z.string(),
|
|
495
|
+
payload: openAIEventPayloadSimplifiedNullable
|
|
496
|
+
})
|
|
497
|
+
).nullable(),
|
|
498
|
+
// Entire events object is nullable
|
|
499
|
+
children: zod.z.null()
|
|
500
|
+
// Base children are null. When extended, it will be an array or null.
|
|
501
|
+
});
|
|
502
|
+
var openAINodeL4 = openAIBaseNode;
|
|
503
|
+
var openAINodeL3 = openAIBaseNode.extend({
|
|
504
|
+
children: zod.z.array(openAINodeL4).nullable()
|
|
505
|
+
});
|
|
506
|
+
var openAINodeL2 = openAIBaseNode.extend({
|
|
507
|
+
children: zod.z.array(openAINodeL3).nullable()
|
|
508
|
+
});
|
|
509
|
+
var openAIUISpec = openAIBaseNode.extend({
|
|
510
|
+
children: zod.z.array(openAINodeL2).nullable()
|
|
511
|
+
});
|
|
512
|
+
var uiSpecNode = zod.z.object({
|
|
384
513
|
id: zod.z.string(),
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
514
|
+
node_type: zod.z.string(),
|
|
515
|
+
props: runtimeRecord,
|
|
516
|
+
bindings: runtimeRecord,
|
|
517
|
+
events: zod.z.record(
|
|
518
|
+
zod.z.string(),
|
|
519
|
+
zod.z.object({
|
|
520
|
+
action: zod.z.string(),
|
|
521
|
+
target: zod.z.string(),
|
|
522
|
+
payload: runtimeRecord
|
|
523
|
+
})
|
|
524
|
+
).nullable(),
|
|
525
|
+
children: zod.z.lazy(() => zod.z.array(uiSpecNode)).nullable()
|
|
526
|
+
});
|
|
397
527
|
zod.z.discriminatedUnion("type", [
|
|
398
528
|
zod.z.object({
|
|
399
529
|
type: zod.z.literal("UI_EVENT"),
|
|
@@ -412,7 +542,7 @@ zod.z.discriminatedUnion("type", [
|
|
|
412
542
|
type: zod.z.literal("ADD_NODE"),
|
|
413
543
|
parentId: zod.z.string(),
|
|
414
544
|
node: uiSpecNode,
|
|
415
|
-
index: zod.z.number().
|
|
545
|
+
index: zod.z.number().nullable()
|
|
416
546
|
}),
|
|
417
547
|
zod.z.object({
|
|
418
548
|
type: zod.z.literal("REMOVE_NODE"),
|
|
@@ -428,16 +558,16 @@ zod.z.discriminatedUnion("type", [
|
|
|
428
558
|
})
|
|
429
559
|
]);
|
|
430
560
|
zod.z.object({
|
|
431
|
-
layout: uiSpecNode.
|
|
561
|
+
layout: uiSpecNode.nullable(),
|
|
432
562
|
loading: zod.z.boolean(),
|
|
433
563
|
history: zod.z.array(uiEvent),
|
|
434
|
-
error: zod.z.string().
|
|
564
|
+
error: zod.z.string().nullable()
|
|
435
565
|
});
|
|
436
566
|
zod.z.object({
|
|
437
567
|
schema: zod.z.record(zod.z.unknown()),
|
|
438
568
|
goal: zod.z.string(),
|
|
439
|
-
history: zod.z.array(uiEvent).
|
|
440
|
-
userContext: zod.z.record(zod.z.unknown()).optional()
|
|
569
|
+
history: zod.z.array(uiEvent).nullable(),
|
|
570
|
+
userContext: zod.z.record(zod.z.unknown()).nullable().optional()
|
|
441
571
|
});
|
|
442
572
|
|
|
443
573
|
// src/core/system-events.ts
|
|
@@ -463,7 +593,7 @@ var SystemEventManager = class {
|
|
|
463
593
|
}
|
|
464
594
|
/**
|
|
465
595
|
* Register a listener for a specific system event type
|
|
466
|
-
*
|
|
596
|
+
*
|
|
467
597
|
* @param eventType - The system event type to listen for
|
|
468
598
|
* @param listener - The listener function
|
|
469
599
|
* @returns Function to unregister the listener
|
|
@@ -483,7 +613,7 @@ var SystemEventManager = class {
|
|
|
483
613
|
}
|
|
484
614
|
/**
|
|
485
615
|
* Emit a system event to all registered listeners
|
|
486
|
-
*
|
|
616
|
+
*
|
|
487
617
|
* @param event - The system event to emit
|
|
488
618
|
*/
|
|
489
619
|
async emit(event) {
|
|
@@ -503,14 +633,22 @@ function createSystemEvent(type, data) {
|
|
|
503
633
|
}
|
|
504
634
|
|
|
505
635
|
// src/env.ts
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
636
|
+
var rawApiKeyFromEnv = process.env.VITE_OPENAI_API_KEY;
|
|
637
|
+
var defaultApiKeyLiteral = "sk-proj-literal-default-for-debug-in-env-ts";
|
|
638
|
+
var env = {
|
|
639
|
+
MOCK_PLANNER: process.env.VITE_MOCK_PLANNER || "1",
|
|
640
|
+
// Simplified MOCK_PLANNER assignment
|
|
641
|
+
NODE_ENV: process.env.VITE_NODE_ENV || "development",
|
|
642
|
+
// Simplified NODE_ENV assignment
|
|
643
|
+
OPENAI_API_KEY: rawApiKeyFromEnv === void 0 ? defaultApiKeyLiteral : rawApiKeyFromEnv
|
|
644
|
+
};
|
|
511
645
|
|
|
512
646
|
// src/core/planner.ts
|
|
513
|
-
|
|
647
|
+
var strictOpenAI = openai.createOpenAI({
|
|
648
|
+
compatibility: "strict"
|
|
649
|
+
// Required for structured outputs with OpenAI API
|
|
650
|
+
});
|
|
651
|
+
function buildPrompt(input, customPrompt) {
|
|
514
652
|
const { schema, goal, history, userContext } = input;
|
|
515
653
|
const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
|
|
516
654
|
return `Table: ${tableName}
|
|
@@ -523,6 +661,9 @@ Schema: ${JSON.stringify(tableSchema)}`;
|
|
|
523
661
|
|
|
524
662
|
User Context:
|
|
525
663
|
${JSON.stringify(userContext)}` : "";
|
|
664
|
+
if (customPrompt) {
|
|
665
|
+
return customPrompt;
|
|
666
|
+
}
|
|
526
667
|
return `
|
|
527
668
|
You are an expert UI generator.
|
|
528
669
|
Create a user interface that achieves the following goal: "${goal}"
|
|
@@ -536,10 +677,10 @@ ${recentEvents}${userContextSection}
|
|
|
536
677
|
Generate a complete UI specification in JSON format that matches the following TypeScript type:
|
|
537
678
|
type UISpecNode = {
|
|
538
679
|
id: string;
|
|
539
|
-
|
|
540
|
-
props?: Record<string,
|
|
541
|
-
bindings?: Record<string,
|
|
542
|
-
events?: Record<string, { action: string; target
|
|
680
|
+
node_type: string;
|
|
681
|
+
props?: Record<string, unknown>;
|
|
682
|
+
bindings?: Record<string, unknown>;
|
|
683
|
+
events?: Record<string, { action: string; target: string; payload?: Record<string, unknown>; }>;
|
|
543
684
|
children?: UISpecNode[];
|
|
544
685
|
};
|
|
545
686
|
|
|
@@ -549,222 +690,419 @@ UI Guidance:
|
|
|
549
690
|
3. Include navigation between related views when needed
|
|
550
691
|
4. Keep the interface simple and intuitive
|
|
551
692
|
5. Bind to schema data where appropriate
|
|
552
|
-
6. Provide event handlers for user interactions
|
|
693
|
+
6. Provide event handlers for user interactions - make sure to always include both action and target properties
|
|
553
694
|
|
|
554
695
|
Respond ONLY with the JSON UI specification and no other text.
|
|
555
696
|
`;
|
|
556
697
|
}
|
|
557
698
|
function mockPlanner(input, targetNodeId, customPrompt) {
|
|
699
|
+
if (customPrompt) {
|
|
700
|
+
console.log("mockPlanner received customPrompt:", customPrompt);
|
|
701
|
+
}
|
|
702
|
+
const taskSchema = input.schema.tasks;
|
|
703
|
+
const taskData = taskSchema?.sampleData || [
|
|
704
|
+
{
|
|
705
|
+
id: "1",
|
|
706
|
+
title: "Example Task 1",
|
|
707
|
+
description: "This is a sample task",
|
|
708
|
+
status: "pending",
|
|
709
|
+
priority: "medium"
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
id: "2",
|
|
713
|
+
title: "Example Task 2",
|
|
714
|
+
description: "Another sample task",
|
|
715
|
+
status: "completed",
|
|
716
|
+
priority: "high"
|
|
717
|
+
}
|
|
718
|
+
];
|
|
558
719
|
const mockNode = {
|
|
559
720
|
id: targetNodeId || "root",
|
|
560
|
-
|
|
561
|
-
props: {
|
|
721
|
+
node_type: "Container",
|
|
722
|
+
props: {
|
|
723
|
+
className: "p-4 space-y-6"
|
|
724
|
+
},
|
|
725
|
+
bindings: null,
|
|
726
|
+
events: null,
|
|
562
727
|
children: [
|
|
563
728
|
{
|
|
564
|
-
id: "
|
|
565
|
-
|
|
566
|
-
props: {
|
|
729
|
+
id: "header-1",
|
|
730
|
+
node_type: "Header",
|
|
731
|
+
props: {
|
|
732
|
+
title: "Task Management Dashboard",
|
|
733
|
+
className: "mb-4"
|
|
734
|
+
},
|
|
735
|
+
bindings: null,
|
|
736
|
+
events: null,
|
|
737
|
+
children: null
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
id: "main-content",
|
|
741
|
+
node_type: "Container",
|
|
742
|
+
props: {
|
|
743
|
+
className: "grid grid-cols-1 gap-6 md:grid-cols-3"
|
|
744
|
+
},
|
|
745
|
+
bindings: null,
|
|
746
|
+
events: null,
|
|
747
|
+
children: [
|
|
748
|
+
{
|
|
749
|
+
id: "tasks-container",
|
|
750
|
+
node_type: "Container",
|
|
751
|
+
props: {
|
|
752
|
+
className: "md:col-span-2"
|
|
753
|
+
},
|
|
754
|
+
bindings: null,
|
|
755
|
+
events: null,
|
|
756
|
+
children: [
|
|
757
|
+
{
|
|
758
|
+
id: "list-heading",
|
|
759
|
+
node_type: "Container",
|
|
760
|
+
props: {
|
|
761
|
+
className: "flex justify-between items-center mb-4"
|
|
762
|
+
},
|
|
763
|
+
bindings: null,
|
|
764
|
+
events: null,
|
|
765
|
+
children: [
|
|
766
|
+
{
|
|
767
|
+
id: "list-title",
|
|
768
|
+
node_type: "Header",
|
|
769
|
+
props: {
|
|
770
|
+
title: "Tasks",
|
|
771
|
+
className: "border-none p-0 m-0"
|
|
772
|
+
},
|
|
773
|
+
bindings: null,
|
|
774
|
+
events: null,
|
|
775
|
+
children: null
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
id: "add-task-button",
|
|
779
|
+
node_type: "Button",
|
|
780
|
+
props: {
|
|
781
|
+
label: "Add Task",
|
|
782
|
+
variant: "default"
|
|
783
|
+
},
|
|
784
|
+
bindings: null,
|
|
785
|
+
events: {
|
|
786
|
+
onClick: {
|
|
787
|
+
action: "ADD_TASK",
|
|
788
|
+
target: "tasks-container",
|
|
789
|
+
payload: {}
|
|
790
|
+
}
|
|
791
|
+
},
|
|
792
|
+
children: null
|
|
793
|
+
}
|
|
794
|
+
]
|
|
795
|
+
},
|
|
796
|
+
{
|
|
797
|
+
id: "task-list",
|
|
798
|
+
node_type: "ListView",
|
|
799
|
+
props: {
|
|
800
|
+
selectable: "true"
|
|
801
|
+
},
|
|
802
|
+
bindings: {
|
|
803
|
+
items: JSON.stringify(taskData),
|
|
804
|
+
fields: JSON.stringify([
|
|
805
|
+
{ key: "id", label: "ID" },
|
|
806
|
+
{ key: "title", label: "Title" },
|
|
807
|
+
{ key: "status", label: "Status" },
|
|
808
|
+
{ key: "priority", label: "Priority" }
|
|
809
|
+
])
|
|
810
|
+
},
|
|
811
|
+
events: {
|
|
812
|
+
onSelect: {
|
|
813
|
+
action: "SELECT_TASK",
|
|
814
|
+
target: "task-detail",
|
|
815
|
+
payload: {
|
|
816
|
+
source: "task-list"
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
children: null
|
|
821
|
+
}
|
|
822
|
+
]
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
id: "task-detail",
|
|
826
|
+
node_type: "Detail",
|
|
827
|
+
props: {
|
|
828
|
+
title: "Task Details",
|
|
829
|
+
visible: "true"
|
|
830
|
+
},
|
|
831
|
+
bindings: {
|
|
832
|
+
data: JSON.stringify(taskData[0]),
|
|
833
|
+
fields: JSON.stringify([
|
|
834
|
+
{ key: "title", label: "Title", type: "heading" },
|
|
835
|
+
{ key: "description", label: "Description", type: "content" },
|
|
836
|
+
{ key: "status", label: "Status" },
|
|
837
|
+
{ key: "priority", label: "Priority" },
|
|
838
|
+
{ key: "dueDate", label: "Due Date" }
|
|
839
|
+
])
|
|
840
|
+
},
|
|
841
|
+
events: {
|
|
842
|
+
onBack: {
|
|
843
|
+
action: "CLOSE_DETAIL",
|
|
844
|
+
target: "task-detail",
|
|
845
|
+
payload: {}
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
children: null
|
|
849
|
+
}
|
|
850
|
+
]
|
|
567
851
|
}
|
|
568
852
|
]
|
|
569
853
|
};
|
|
570
854
|
return mockNode;
|
|
571
855
|
}
|
|
856
|
+
async function callPlannerLLM(input, routeResolution) {
|
|
857
|
+
await systemEvents.emit(
|
|
858
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
|
|
859
|
+
);
|
|
860
|
+
if (env.MOCK_PLANNER === "1" || !env.OPENAI_API_KEY) {
|
|
861
|
+
console.warn(
|
|
862
|
+
"Using mock planner because MOCK_PLANNER is enabled or OPENAI_API_KEY is not available"
|
|
863
|
+
);
|
|
864
|
+
return mockPlanner(input);
|
|
865
|
+
}
|
|
866
|
+
const startTime = Date.now();
|
|
867
|
+
const prompt = routeResolution?.prompt || buildPrompt(input);
|
|
868
|
+
await systemEvents.emit(
|
|
869
|
+
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
870
|
+
);
|
|
871
|
+
try {
|
|
872
|
+
const { object: uiSpec } = await ai.generateObject({
|
|
873
|
+
model: strictOpenAI("gpt-4o", { structuredOutputs: true }),
|
|
874
|
+
schema: openAIUISpec,
|
|
875
|
+
messages: [{ role: "user", content: prompt }],
|
|
876
|
+
temperature: 0.2,
|
|
877
|
+
maxTokens: 4e3
|
|
878
|
+
});
|
|
879
|
+
await systemEvents.emit(
|
|
880
|
+
createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
|
|
881
|
+
layout: uiSpec,
|
|
882
|
+
executionTimeMs: Date.now() - startTime
|
|
883
|
+
})
|
|
884
|
+
);
|
|
885
|
+
return uiSpec;
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.error("Error calling LLM planner:", error);
|
|
888
|
+
await systemEvents.emit(
|
|
889
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
890
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
891
|
+
})
|
|
892
|
+
);
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
async function processEvent(event, router, schema, layout, dataContext, goal, userContext) {
|
|
897
|
+
const routeResolution = await router.resolveRoute(
|
|
898
|
+
event,
|
|
899
|
+
schema,
|
|
900
|
+
layout || null,
|
|
901
|
+
dataContext,
|
|
902
|
+
goal,
|
|
903
|
+
userContext
|
|
904
|
+
);
|
|
905
|
+
if (!routeResolution) {
|
|
906
|
+
throw new Error(
|
|
907
|
+
`No route found for event type: ${event.type}, node: ${event.nodeId}`
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
if (routeResolution.actionType.toString() === "NoOp") {
|
|
911
|
+
if (!layout)
|
|
912
|
+
throw new Error("Layout is undefined and action is NoOp");
|
|
913
|
+
return layout;
|
|
914
|
+
}
|
|
915
|
+
const plannerInputForLLM = routeResolution.plannerInput;
|
|
916
|
+
const newLayout = await callPlannerLLM(plannerInputForLLM, routeResolution);
|
|
917
|
+
return newLayout;
|
|
918
|
+
}
|
|
572
919
|
|
|
573
920
|
// src/core/state.ts
|
|
574
|
-
var useChat = (config) => ({
|
|
575
|
-
append: async (message) => {
|
|
576
|
-
},
|
|
577
|
-
data: { content: "{}" },
|
|
578
|
-
isLoading: false,
|
|
579
|
-
error: null,
|
|
580
|
-
stop: () => {
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
921
|
function useUIStateEngine({
|
|
584
922
|
schema,
|
|
585
923
|
goal,
|
|
586
924
|
userContext,
|
|
587
925
|
mockMode = false,
|
|
588
|
-
planningConfig
|
|
926
|
+
planningConfig,
|
|
589
927
|
router = createDefaultRouter(),
|
|
590
928
|
dataContext = {},
|
|
591
929
|
enablePartialUpdates = false
|
|
592
930
|
}) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
state.layout,
|
|
603
|
-
dataContext,
|
|
604
|
-
goal,
|
|
605
|
-
userContext
|
|
606
|
-
);
|
|
607
|
-
if (route) {
|
|
608
|
-
console.log("Resolved route:", route);
|
|
609
|
-
systemEvents.emit(
|
|
610
|
-
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
611
|
-
plannerInput: route.plannerInput
|
|
612
|
-
})
|
|
613
|
-
);
|
|
614
|
-
if (mockMode) {
|
|
615
|
-
const node = mockPlanner(route.plannerInput, route.targetNodeId, route.prompt);
|
|
616
|
-
switch (route.actionType) {
|
|
617
|
-
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
618
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
619
|
-
break;
|
|
620
|
-
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
621
|
-
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
622
|
-
case "HIDE_DETAIL" /* HIDE_DETAIL */:
|
|
623
|
-
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
624
|
-
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
625
|
-
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
626
|
-
case "NAVIGATE" /* NAVIGATE */:
|
|
627
|
-
dispatch({
|
|
628
|
-
type: "PARTIAL_UPDATE",
|
|
629
|
-
nodeId: route.targetNodeId,
|
|
630
|
-
node
|
|
631
|
-
});
|
|
632
|
-
break;
|
|
633
|
-
}
|
|
634
|
-
} else {
|
|
635
|
-
const prompt = route.prompt;
|
|
636
|
-
systemEvents.emit(
|
|
637
|
-
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
638
|
-
);
|
|
639
|
-
append({
|
|
640
|
-
content: prompt,
|
|
641
|
-
role: "user"
|
|
642
|
-
});
|
|
643
|
-
sessionStorage.setItem("currentRoute", JSON.stringify({
|
|
644
|
-
actionType: route.actionType,
|
|
645
|
-
targetNodeId: route.targetNodeId
|
|
646
|
-
}));
|
|
647
|
-
}
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
const input = {
|
|
652
|
-
schema,
|
|
653
|
-
goal,
|
|
654
|
-
history: [...state.history, event],
|
|
655
|
-
userContext
|
|
656
|
-
};
|
|
657
|
-
if (mockMode) {
|
|
658
|
-
const node = mockPlanner();
|
|
659
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
660
|
-
} else {
|
|
661
|
-
const prompt = buildPrompt(input);
|
|
662
|
-
append({
|
|
663
|
-
content: prompt,
|
|
664
|
-
role: "user"
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
}, [append, goal, schema, state.history, state.layout, stop, userContext, router, mockMode, dataContext, enablePartialUpdates]);
|
|
668
|
-
react.useEffect(() => {
|
|
669
|
-
if (isLoading) {
|
|
931
|
+
if (userContext === null) {
|
|
932
|
+
console.warn(
|
|
933
|
+
"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."
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
const [state, dispatch] = React.useReducer(uiReducer, initialState);
|
|
937
|
+
const handleEvent = React.useCallback(
|
|
938
|
+
async (event) => {
|
|
939
|
+
dispatch({ type: "UI_EVENT", event });
|
|
670
940
|
dispatch({ type: "LOADING", isLoading: true });
|
|
671
|
-
} else if (error) {
|
|
672
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
673
|
-
dispatch({ type: "ERROR", message: errorMessage });
|
|
674
|
-
systemEvents.emit(
|
|
675
|
-
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
676
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
677
|
-
})
|
|
678
|
-
);
|
|
679
|
-
} else if (data.content) {
|
|
680
941
|
try {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
942
|
+
let resolvedNode;
|
|
943
|
+
let actionTypeForDispatch = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
944
|
+
let targetNodeIdForDispatch = "root";
|
|
945
|
+
if (enablePartialUpdates) {
|
|
946
|
+
const route = router.resolveRoute(
|
|
947
|
+
event,
|
|
948
|
+
schema,
|
|
949
|
+
state.layout,
|
|
950
|
+
dataContext,
|
|
951
|
+
goal,
|
|
952
|
+
userContext
|
|
953
|
+
);
|
|
954
|
+
if (route) {
|
|
955
|
+
console.log("Resolved route:", route);
|
|
956
|
+
actionTypeForDispatch = route.actionType;
|
|
957
|
+
targetNodeIdForDispatch = route.targetNodeId;
|
|
958
|
+
systemEvents.emit(
|
|
959
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
960
|
+
plannerInput: route.plannerInput
|
|
961
|
+
})
|
|
962
|
+
);
|
|
963
|
+
if (mockMode) {
|
|
964
|
+
resolvedNode = mockPlanner(
|
|
965
|
+
route.plannerInput,
|
|
966
|
+
route.targetNodeId,
|
|
967
|
+
route.prompt
|
|
968
|
+
);
|
|
969
|
+
} else {
|
|
970
|
+
resolvedNode = await callPlannerLLM(route.plannerInput, route);
|
|
971
|
+
}
|
|
972
|
+
} else {
|
|
973
|
+
const input = {
|
|
974
|
+
schema,
|
|
975
|
+
goal,
|
|
976
|
+
history: [...state.history, event],
|
|
977
|
+
userContext
|
|
978
|
+
};
|
|
979
|
+
if (mockMode) {
|
|
980
|
+
resolvedNode = mockPlanner(input);
|
|
981
|
+
} else {
|
|
982
|
+
resolvedNode = await callPlannerLLM(input);
|
|
714
983
|
}
|
|
715
|
-
sessionStorage.removeItem("currentRoute");
|
|
716
|
-
} catch (e) {
|
|
717
|
-
console.error("Error parsing route info:", e);
|
|
718
|
-
dispatch({ type: "AI_RESPONSE", node: validatedNode });
|
|
719
984
|
}
|
|
720
985
|
} else {
|
|
721
|
-
|
|
986
|
+
const input = {
|
|
987
|
+
schema,
|
|
988
|
+
goal,
|
|
989
|
+
history: [...state.history, event],
|
|
990
|
+
// event is already in history from UI_EVENT dispatch
|
|
991
|
+
userContext
|
|
992
|
+
};
|
|
993
|
+
if (mockMode) {
|
|
994
|
+
resolvedNode = mockPlanner(input);
|
|
995
|
+
} else {
|
|
996
|
+
resolvedNode = await callPlannerLLM(input);
|
|
997
|
+
}
|
|
722
998
|
}
|
|
999
|
+
switch (actionTypeForDispatch) {
|
|
1000
|
+
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
1001
|
+
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
1002
|
+
case "HIDE_DETAIL" /* HIDE_DETAIL */:
|
|
1003
|
+
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
1004
|
+
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
1005
|
+
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
1006
|
+
case "NAVIGATE" /* NAVIGATE */:
|
|
1007
|
+
dispatch({
|
|
1008
|
+
type: "PARTIAL_UPDATE",
|
|
1009
|
+
nodeId: targetNodeIdForDispatch,
|
|
1010
|
+
node: resolvedNode
|
|
1011
|
+
});
|
|
1012
|
+
break;
|
|
1013
|
+
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
1014
|
+
default:
|
|
1015
|
+
dispatch({ type: "AI_RESPONSE", node: resolvedNode });
|
|
1016
|
+
break;
|
|
1017
|
+
}
|
|
1018
|
+
} catch (e) {
|
|
1019
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1020
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
723
1021
|
systemEvents.emit(
|
|
724
|
-
createSystemEvent("
|
|
725
|
-
|
|
726
|
-
executionTimeMs: 0
|
|
727
|
-
// Not available here
|
|
1022
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
1023
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
728
1024
|
})
|
|
729
1025
|
);
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
1026
|
+
} finally {
|
|
1027
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
[
|
|
1031
|
+
// append, // REMOVE
|
|
1032
|
+
goal,
|
|
1033
|
+
schema,
|
|
1034
|
+
state.history,
|
|
1035
|
+
// Keep state.history if input preparation needs it
|
|
1036
|
+
state.layout,
|
|
1037
|
+
// stop, // REMOVE
|
|
1038
|
+
userContext,
|
|
1039
|
+
router,
|
|
1040
|
+
mockMode,
|
|
1041
|
+
dataContext,
|
|
1042
|
+
enablePartialUpdates,
|
|
1043
|
+
dispatch
|
|
1044
|
+
// Add dispatch
|
|
1045
|
+
]
|
|
1046
|
+
);
|
|
1047
|
+
React.useEffect(() => {
|
|
1048
|
+
const initialFetch = async () => {
|
|
1049
|
+
dispatch({ type: "LOADING", isLoading: true });
|
|
1050
|
+
try {
|
|
1051
|
+
const input = {
|
|
1052
|
+
schema,
|
|
1053
|
+
goal,
|
|
1054
|
+
history: [],
|
|
1055
|
+
userContext
|
|
1056
|
+
};
|
|
1057
|
+
let node;
|
|
1058
|
+
if (mockMode) {
|
|
1059
|
+
node = mockPlanner(input);
|
|
1060
|
+
} else {
|
|
1061
|
+
node = await callPlannerLLM(input);
|
|
1062
|
+
}
|
|
1063
|
+
dispatch({ type: "AI_RESPONSE", node });
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1066
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
736
1067
|
systemEvents.emit(
|
|
1068
|
+
// Also emit system event for initial load error
|
|
737
1069
|
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
738
|
-
error:
|
|
1070
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
739
1071
|
})
|
|
740
1072
|
);
|
|
1073
|
+
} finally {
|
|
1074
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
741
1075
|
}
|
|
742
|
-
}
|
|
743
|
-
}, [data.content, error, isLoading, enablePartialUpdates]);
|
|
744
|
-
react.useEffect(() => {
|
|
745
|
-
const input = {
|
|
746
|
-
schema,
|
|
747
|
-
goal,
|
|
748
|
-
history: [],
|
|
749
|
-
userContext
|
|
750
1076
|
};
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
754
|
-
} else {
|
|
755
|
-
const prompt = buildPrompt(input);
|
|
756
|
-
append({
|
|
757
|
-
content: prompt,
|
|
758
|
-
role: "user"
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
}, [append, goal, schema, userContext, mockMode]);
|
|
1077
|
+
initialFetch();
|
|
1078
|
+
}, [goal, schema, userContext, mockMode, dispatch]);
|
|
762
1079
|
return {
|
|
763
1080
|
state,
|
|
764
1081
|
dispatch,
|
|
765
1082
|
handleEvent
|
|
766
1083
|
};
|
|
767
1084
|
}
|
|
1085
|
+
zod.z.enum([
|
|
1086
|
+
// Layout components
|
|
1087
|
+
"Container",
|
|
1088
|
+
"Card",
|
|
1089
|
+
"Header",
|
|
1090
|
+
// Input components
|
|
1091
|
+
"Button",
|
|
1092
|
+
"Input",
|
|
1093
|
+
"Select",
|
|
1094
|
+
"Textarea",
|
|
1095
|
+
"Checkbox",
|
|
1096
|
+
"RadioGroup",
|
|
1097
|
+
// Data display components
|
|
1098
|
+
"ListView",
|
|
1099
|
+
"Detail",
|
|
1100
|
+
"Tabs",
|
|
1101
|
+
"Dialog",
|
|
1102
|
+
// Typography
|
|
1103
|
+
"Heading",
|
|
1104
|
+
"Text"
|
|
1105
|
+
]);
|
|
768
1106
|
var ShimmerBlock = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-8 bg-gray-200 animate-pulse rounded" });
|
|
769
1107
|
var ShimmerTable = ({ rows = 3 }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-2", children: [
|
|
770
1108
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-10 bg-gray-200 animate-pulse rounded" }),
|
|
@@ -778,24 +1116,47 @@ var ShimmerCard = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-f
|
|
|
778
1116
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5/6 h-4 bg-gray-200 animate-pulse rounded" })
|
|
779
1117
|
] })
|
|
780
1118
|
] });
|
|
781
|
-
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
|
|
782
|
-
var Header = ({
|
|
1119
|
+
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `autoui-mock-container ${props.className || ""}`, children: props.children });
|
|
1120
|
+
var Header = ({
|
|
1121
|
+
title,
|
|
1122
|
+
className
|
|
1123
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1124
|
+
"header",
|
|
1125
|
+
{
|
|
1126
|
+
className: `py-4 px-6 border-b border-gray-300 mb-4 bg-gray-50 dark:bg-gray-800 ${className || ""}`,
|
|
1127
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-800 dark:text-white", children: title })
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
783
1130
|
var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
784
1131
|
"button",
|
|
785
1132
|
{
|
|
786
|
-
className: `px-4 py-2 rounded font-medium ${variant === "default" ? "bg-blue-600 text-white" : variant === "outline" ? "border border-gray-300 text-gray-700" : "bg-red-600 text-white"}`,
|
|
1133
|
+
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"}`,
|
|
787
1134
|
onClick,
|
|
788
1135
|
children
|
|
789
1136
|
}
|
|
790
1137
|
);
|
|
791
|
-
var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
792
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-
|
|
793
|
-
|
|
1138
|
+
var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full border border-gray-300 dark:border-gray-700 rounded-lg overflow-hidden shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
1139
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-100 dark:bg-gray-800 border-b border-gray-300 dark:border-gray-700", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1140
|
+
"th",
|
|
1141
|
+
{
|
|
1142
|
+
className: "px-6 py-3 text-left text-xs font-medium text-gray-700 dark:text-gray-300 uppercase tracking-wider",
|
|
1143
|
+
children: field.label
|
|
1144
|
+
},
|
|
1145
|
+
field.key
|
|
1146
|
+
)) }) }),
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700", children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
1148
|
"tr",
|
|
795
1149
|
{
|
|
796
1150
|
onClick: () => selectable && onSelect && onSelect(item),
|
|
797
|
-
className: selectable ? "cursor-pointer hover:bg-gray-
|
|
798
|
-
children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1151
|
+
className: selectable ? "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800" : "",
|
|
1152
|
+
children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1153
|
+
"td",
|
|
1154
|
+
{
|
|
1155
|
+
className: "px-6 py-4 whitespace-nowrap text-sm text-gray-800 dark:text-gray-300",
|
|
1156
|
+
children: item[field.key] ?? ""
|
|
1157
|
+
},
|
|
1158
|
+
field.key
|
|
1159
|
+
))
|
|
799
1160
|
},
|
|
800
1161
|
index
|
|
801
1162
|
)) })
|
|
@@ -803,94 +1164,211 @@ var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__
|
|
|
803
1164
|
var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
|
|
804
1165
|
if (!visible)
|
|
805
1166
|
return null;
|
|
806
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full border rounded-lg p-6 space-y-4", children: [
|
|
807
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
|
|
808
|
-
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium", children: title }),
|
|
1167
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full border border-gray-300 dark:border-gray-700 rounded-lg p-6 space-y-4 bg-white dark:bg-gray-900 shadow-sm", children: [
|
|
1168
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-3", children: [
|
|
1169
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-800 dark:text-white", children: title }),
|
|
809
1170
|
onBack && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
|
|
810
1171
|
] }),
|
|
811
1172
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: fields.map((field) => {
|
|
812
1173
|
if (field.type === "heading") {
|
|
813
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1174
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1175
|
+
"h3",
|
|
1176
|
+
{
|
|
1177
|
+
className: "text-xl font-semibold text-gray-800 dark:text-white",
|
|
1178
|
+
children: data?.[field.key] ?? ""
|
|
1179
|
+
},
|
|
1180
|
+
field.key
|
|
1181
|
+
);
|
|
814
1182
|
}
|
|
815
1183
|
if (field.type === "content") {
|
|
816
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1184
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1185
|
+
"div",
|
|
1186
|
+
{
|
|
1187
|
+
className: "text-sm text-gray-700 dark:text-gray-300",
|
|
1188
|
+
children: data?.[field.key] ?? ""
|
|
1189
|
+
},
|
|
1190
|
+
field.key
|
|
1191
|
+
);
|
|
817
1192
|
}
|
|
818
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
1193
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1194
|
+
"div",
|
|
1195
|
+
{
|
|
1196
|
+
className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
|
|
1197
|
+
children: [
|
|
1198
|
+
field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
|
|
1199
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
|
|
1200
|
+
]
|
|
1201
|
+
},
|
|
1202
|
+
field.key
|
|
1203
|
+
);
|
|
822
1204
|
}) })
|
|
823
1205
|
] });
|
|
824
1206
|
};
|
|
825
|
-
var
|
|
826
|
-
|
|
827
|
-
|
|
1207
|
+
var getSafeProp = (props, key, validator, defaultValue) => {
|
|
1208
|
+
if (props && typeof props === "object" && key in props) {
|
|
1209
|
+
const value = props[key];
|
|
1210
|
+
if (validator(value)) {
|
|
1211
|
+
return value;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
return defaultValue;
|
|
1215
|
+
};
|
|
1216
|
+
var isObject = (value) => typeof value === "object" && value !== null;
|
|
1217
|
+
var isString = (value) => typeof value === "string";
|
|
1218
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
1219
|
+
var isCSSProperties = (value) => isObject(value);
|
|
1220
|
+
var isButtonVariant = (value) => isString(value) && ["default", "outline", "destructive"].includes(value);
|
|
1221
|
+
var getSafeBinding = (bindings, key, validator, defaultValue) => {
|
|
1222
|
+
if (bindings && typeof bindings === "object" && key in bindings) {
|
|
1223
|
+
const value = bindings[key];
|
|
1224
|
+
if (validator(value)) {
|
|
1225
|
+
return value;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return defaultValue;
|
|
1229
|
+
};
|
|
1230
|
+
var isArrayOf = (itemValidator) => (arr) => Array.isArray(arr) && arr.every(itemValidator);
|
|
1231
|
+
var isReactNode = (value) => {
|
|
1232
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null || typeof value === "undefined" || typeof value === "object" && value !== null && "$$typeof" in value;
|
|
1233
|
+
};
|
|
1234
|
+
var isRecordWithReactNodeValues = (value) => isObject(value) && Object.values(value).every(isReactNode);
|
|
1235
|
+
var isFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label);
|
|
1236
|
+
var isDetailFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label) && (item.type === void 0 || isString(item.type));
|
|
1237
|
+
var createEventHandler = (node, eventName, uiEventType2, processEvent2) => {
|
|
1238
|
+
const eventConfig = node.events?.[uiEventType2];
|
|
1239
|
+
if (!processEvent2 || !eventConfig)
|
|
828
1240
|
return void 0;
|
|
829
|
-
return () => {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1241
|
+
return (eventPayload) => {
|
|
1242
|
+
const fullEvent = {
|
|
1243
|
+
type: uiEventType2,
|
|
1244
|
+
nodeId: node.id,
|
|
1245
|
+
timestamp: Date.now(),
|
|
1246
|
+
payload: {
|
|
1247
|
+
...eventConfig.payload || {},
|
|
1248
|
+
...eventPayload || {}
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
processEvent2(fullEvent);
|
|
835
1252
|
};
|
|
836
1253
|
};
|
|
837
1254
|
var adapterMap = {
|
|
838
|
-
Container: (node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
839
|
-
|
|
840
|
-
Button: (node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
841
|
-
Button,
|
|
1255
|
+
Container: (node, processEvent2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1256
|
+
Container,
|
|
842
1257
|
{
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
children: node.
|
|
1258
|
+
style: getSafeProp(node.props, "style", isCSSProperties, {}),
|
|
1259
|
+
className: getSafeProp(node.props, "className", isString, ""),
|
|
1260
|
+
children: node.children?.map((child) => renderNode(child, processEvent2))
|
|
846
1261
|
}
|
|
847
1262
|
),
|
|
848
|
-
|
|
849
|
-
|
|
1263
|
+
Header: (node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1264
|
+
Header,
|
|
850
1265
|
{
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
selectable: node.props?.selectable,
|
|
854
|
-
onSelect: createEventHandler(node, "onSelect")
|
|
1266
|
+
title: getSafeProp(node.props, "title", isString, "Untitled"),
|
|
1267
|
+
className: getSafeProp(node.props, "className", isString, "")
|
|
855
1268
|
}
|
|
856
1269
|
),
|
|
857
|
-
|
|
858
|
-
|
|
1270
|
+
Button: (node, processEvent2) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1271
|
+
Button,
|
|
859
1272
|
{
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
visible: node.props?.visible !== false,
|
|
864
|
-
onBack: createEventHandler(node, "onBack")
|
|
1273
|
+
variant: getSafeProp(node.props, "variant", isButtonVariant, "default"),
|
|
1274
|
+
onClick: createEventHandler(node, "onClick", "CLICK", processEvent2),
|
|
1275
|
+
children: getSafeProp(node.props, "label", isString, "Button")
|
|
865
1276
|
}
|
|
866
|
-
)
|
|
1277
|
+
),
|
|
1278
|
+
ListView: (node, processEvent2) => {
|
|
1279
|
+
const items = getSafeBinding(
|
|
1280
|
+
node.bindings,
|
|
1281
|
+
"items",
|
|
1282
|
+
isArrayOf(isRecordWithReactNodeValues),
|
|
1283
|
+
[]
|
|
1284
|
+
);
|
|
1285
|
+
const fields = getSafeBinding(
|
|
1286
|
+
node.bindings,
|
|
1287
|
+
"fields",
|
|
1288
|
+
isArrayOf(isFieldObject),
|
|
1289
|
+
[]
|
|
1290
|
+
);
|
|
1291
|
+
const selectable = getSafeProp(node.props, "selectable", isBoolean, false);
|
|
1292
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1293
|
+
Table,
|
|
1294
|
+
{
|
|
1295
|
+
items,
|
|
1296
|
+
fields,
|
|
1297
|
+
selectable,
|
|
1298
|
+
onSelect: (item) => {
|
|
1299
|
+
const handler = createEventHandler(
|
|
1300
|
+
node,
|
|
1301
|
+
"onSelect",
|
|
1302
|
+
"CLICK",
|
|
1303
|
+
processEvent2
|
|
1304
|
+
);
|
|
1305
|
+
if (handler) {
|
|
1306
|
+
handler({ selectedItem: item });
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
},
|
|
1312
|
+
Detail: (node, processEvent2) => {
|
|
1313
|
+
const data = getSafeBinding(
|
|
1314
|
+
node.bindings,
|
|
1315
|
+
"data",
|
|
1316
|
+
isRecordWithReactNodeValues,
|
|
1317
|
+
{}
|
|
1318
|
+
);
|
|
1319
|
+
const fields = getSafeBinding(
|
|
1320
|
+
node.bindings,
|
|
1321
|
+
"fields",
|
|
1322
|
+
isArrayOf(isDetailFieldObject),
|
|
1323
|
+
[]
|
|
1324
|
+
);
|
|
1325
|
+
const title = getSafeProp(node.props, "title", isString, "");
|
|
1326
|
+
const visible = getSafeProp(node.props, "visible", isBoolean, true);
|
|
1327
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1328
|
+
Detail,
|
|
1329
|
+
{
|
|
1330
|
+
data,
|
|
1331
|
+
fields,
|
|
1332
|
+
title,
|
|
1333
|
+
visible,
|
|
1334
|
+
onBack: createEventHandler(node, "onBack", "CLICK", processEvent2)
|
|
1335
|
+
}
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
867
1338
|
};
|
|
868
|
-
function renderNode(node) {
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
871
|
-
return
|
|
1339
|
+
function renderNode(node, processEvent2) {
|
|
1340
|
+
const mappedComponent = adapterMap[node.node_type];
|
|
1341
|
+
if (mappedComponent) {
|
|
1342
|
+
return mappedComponent(node, processEvent2);
|
|
872
1343
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
] });
|
|
1344
|
+
console.warn(`Unknown node type: ${node.node_type}`);
|
|
1345
|
+
return React__default.default.createElement(
|
|
1346
|
+
Container,
|
|
1347
|
+
{},
|
|
1348
|
+
`Unknown node type: ${node.node_type}`
|
|
1349
|
+
);
|
|
880
1350
|
}
|
|
881
|
-
|
|
1351
|
+
var renderedNodesCache = /* @__PURE__ */ new Map();
|
|
1352
|
+
var MAX_CACHE_SIZE = 10;
|
|
1353
|
+
var CACHE_TTL = 5e3;
|
|
1354
|
+
async function renderNode2(node, adapter = "shadcn", processEvent2) {
|
|
882
1355
|
const startTime = Date.now();
|
|
1356
|
+
const nodeId = node.id;
|
|
1357
|
+
const cachedItem = renderedNodesCache.get(nodeId);
|
|
1358
|
+
if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
|
|
1359
|
+
return cachedItem.element;
|
|
1360
|
+
}
|
|
883
1361
|
await systemEvents.emit(
|
|
884
1362
|
createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
|
|
885
1363
|
);
|
|
886
1364
|
let result;
|
|
887
1365
|
switch (adapter) {
|
|
888
1366
|
case "shadcn":
|
|
889
|
-
result = renderNode(node);
|
|
1367
|
+
result = renderNode(node, processEvent2);
|
|
890
1368
|
break;
|
|
891
1369
|
default:
|
|
892
1370
|
console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
|
|
893
|
-
result = renderNode(node);
|
|
1371
|
+
result = renderNode(node, processEvent2);
|
|
894
1372
|
}
|
|
895
1373
|
await systemEvents.emit(
|
|
896
1374
|
createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
|
|
@@ -898,13 +1376,23 @@ async function renderNode2(node, adapter = "shadcn") {
|
|
|
898
1376
|
renderTimeMs: Date.now() - startTime
|
|
899
1377
|
})
|
|
900
1378
|
);
|
|
1379
|
+
renderedNodesCache.set(nodeId, {
|
|
1380
|
+
element: result,
|
|
1381
|
+
timestamp: startTime
|
|
1382
|
+
});
|
|
1383
|
+
if (renderedNodesCache.size > MAX_CACHE_SIZE) {
|
|
1384
|
+
const oldestKey = [...renderedNodesCache.entries()].sort(
|
|
1385
|
+
([, a], [, b]) => a.timestamp - b.timestamp
|
|
1386
|
+
)[0][0];
|
|
1387
|
+
renderedNodesCache.delete(oldestKey);
|
|
1388
|
+
}
|
|
901
1389
|
return result;
|
|
902
1390
|
}
|
|
903
1391
|
function renderShimmer(node, adapter = "shadcn") {
|
|
904
1392
|
if (!node) {
|
|
905
1393
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
|
|
906
1394
|
}
|
|
907
|
-
switch (node.
|
|
1395
|
+
switch (node.node_type) {
|
|
908
1396
|
case "ListView":
|
|
909
1397
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerTable, { rows: 3 });
|
|
910
1398
|
case "Detail":
|
|
@@ -917,6 +1405,16 @@ function renderShimmer(node, adapter = "shadcn") {
|
|
|
917
1405
|
}
|
|
918
1406
|
|
|
919
1407
|
// src/core/bindings.ts
|
|
1408
|
+
var bindingsCache = /* @__PURE__ */ new Map();
|
|
1409
|
+
var MAX_CACHE_SIZE2 = 50;
|
|
1410
|
+
var CACHE_TTL2 = 2e3;
|
|
1411
|
+
var nodeCacheTimestamps = /* @__PURE__ */ new Map();
|
|
1412
|
+
function hashDataContext(context) {
|
|
1413
|
+
return JSON.stringify(context);
|
|
1414
|
+
}
|
|
1415
|
+
function createCacheKey(nodeId, context) {
|
|
1416
|
+
return `${nodeId}:${hashDataContext(context)}`;
|
|
1417
|
+
}
|
|
920
1418
|
function getValueByPath(context, path) {
|
|
921
1419
|
const parts = path.split(".");
|
|
922
1420
|
let current = context;
|
|
@@ -934,19 +1432,32 @@ function getValueByPath(context, path) {
|
|
|
934
1432
|
function setValueByPath(context, path, value) {
|
|
935
1433
|
const result = { ...context };
|
|
936
1434
|
const parts = path.split(".");
|
|
1435
|
+
if (parts.length === 0)
|
|
1436
|
+
return result;
|
|
937
1437
|
let current = result;
|
|
938
1438
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
939
1439
|
const part = parts[i];
|
|
940
|
-
if (
|
|
941
|
-
|
|
1440
|
+
if (typeof current !== "object" || current === null) {
|
|
1441
|
+
console.error("setValueByPath: Cannot create path in a non-object.");
|
|
1442
|
+
return context;
|
|
942
1443
|
}
|
|
943
|
-
|
|
944
|
-
if (typeof
|
|
945
|
-
|
|
1444
|
+
const currentAsObject = current;
|
|
1445
|
+
if (!(part in currentAsObject) || typeof currentAsObject[part] !== "object" || currentAsObject[part] === null) {
|
|
1446
|
+
currentAsObject[part] = {};
|
|
946
1447
|
}
|
|
1448
|
+
current = currentAsObject[part];
|
|
947
1449
|
}
|
|
948
1450
|
const lastPart = parts[parts.length - 1];
|
|
949
|
-
current
|
|
1451
|
+
if (typeof current === "object" && current !== null) {
|
|
1452
|
+
current[lastPart] = value;
|
|
1453
|
+
} else if (parts.length === 1 && typeof result === "object" && result !== null) {
|
|
1454
|
+
result[lastPart] = value;
|
|
1455
|
+
} else {
|
|
1456
|
+
console.warn(
|
|
1457
|
+
`setValueByPath: Could not set value for path "${path}". Final segment location is not an object.`
|
|
1458
|
+
);
|
|
1459
|
+
return context;
|
|
1460
|
+
}
|
|
950
1461
|
return result;
|
|
951
1462
|
}
|
|
952
1463
|
function processBinding(binding, context) {
|
|
@@ -966,18 +1477,27 @@ function processBinding(binding, context) {
|
|
|
966
1477
|
return binding;
|
|
967
1478
|
}
|
|
968
1479
|
async function resolveBindings(node, context) {
|
|
1480
|
+
const currentTime = Date.now();
|
|
1481
|
+
const cacheKey = createCacheKey(node.id, context);
|
|
1482
|
+
const cachedNode = bindingsCache.get(cacheKey);
|
|
1483
|
+
const cachedTimestamp = nodeCacheTimestamps.get(cacheKey);
|
|
1484
|
+
if (cachedNode && cachedTimestamp && currentTime - cachedTimestamp < CACHE_TTL2) {
|
|
1485
|
+
return cachedNode;
|
|
1486
|
+
}
|
|
969
1487
|
await systemEvents.emit(
|
|
970
|
-
createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
|
|
1488
|
+
createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
|
|
1489
|
+
layout: node
|
|
1490
|
+
})
|
|
971
1491
|
);
|
|
972
1492
|
const result = {
|
|
973
1493
|
...node,
|
|
974
|
-
props: node.props ? { ...node.props } :
|
|
975
|
-
events: node.events ? { ...node.events } :
|
|
1494
|
+
props: node.props ? { ...node.props } : null,
|
|
1495
|
+
events: node.events ? { ...node.events } : null
|
|
976
1496
|
};
|
|
977
1497
|
if (node.bindings) {
|
|
978
1498
|
for (const [key, binding] of Object.entries(node.bindings)) {
|
|
979
1499
|
const value = processBinding(binding, context);
|
|
980
|
-
if (value !== void 0) {
|
|
1500
|
+
if (value !== void 0 && typeof value === "string") {
|
|
981
1501
|
if (!result.props) {
|
|
982
1502
|
result.props = {};
|
|
983
1503
|
}
|
|
@@ -986,7 +1506,9 @@ async function resolveBindings(node, context) {
|
|
|
986
1506
|
}
|
|
987
1507
|
}
|
|
988
1508
|
if (node.children) {
|
|
989
|
-
result.children = await Promise.all(
|
|
1509
|
+
result.children = await Promise.all(
|
|
1510
|
+
node.children.map((child) => resolveBindings(child, context))
|
|
1511
|
+
);
|
|
990
1512
|
}
|
|
991
1513
|
await systemEvents.emit(
|
|
992
1514
|
createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
|
|
@@ -994,6 +1516,17 @@ async function resolveBindings(node, context) {
|
|
|
994
1516
|
resolvedLayout: result
|
|
995
1517
|
})
|
|
996
1518
|
);
|
|
1519
|
+
bindingsCache.set(cacheKey, result);
|
|
1520
|
+
nodeCacheTimestamps.set(cacheKey, currentTime);
|
|
1521
|
+
if (bindingsCache.size > MAX_CACHE_SIZE2) {
|
|
1522
|
+
const entries = [...nodeCacheTimestamps.entries()];
|
|
1523
|
+
if (entries.length > 0) {
|
|
1524
|
+
entries.sort((a, b) => a[1] - b[1]);
|
|
1525
|
+
const oldestKey = entries[0][0];
|
|
1526
|
+
bindingsCache.delete(oldestKey);
|
|
1527
|
+
nodeCacheTimestamps.delete(oldestKey);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
997
1530
|
return result;
|
|
998
1531
|
}
|
|
999
1532
|
function executeAction(action, targetId, payload, context = {}, layoutTree) {
|
|
@@ -1029,7 +1562,7 @@ var EventManager = class {
|
|
|
1029
1562
|
}
|
|
1030
1563
|
/**
|
|
1031
1564
|
* Register a hook for specific event types
|
|
1032
|
-
*
|
|
1565
|
+
*
|
|
1033
1566
|
* @param eventTypes - Event types to register for, or 'all' for all events
|
|
1034
1567
|
* @param hook - Hook function to execute
|
|
1035
1568
|
* @returns Unregister function
|
|
@@ -1062,7 +1595,7 @@ var EventManager = class {
|
|
|
1062
1595
|
}
|
|
1063
1596
|
/**
|
|
1064
1597
|
* Process an event through all registered hooks
|
|
1065
|
-
*
|
|
1598
|
+
*
|
|
1066
1599
|
* @param event - The UI event to process
|
|
1067
1600
|
* @returns Whether the default action should proceed
|
|
1068
1601
|
*/
|
|
@@ -1108,6 +1641,20 @@ function createEventHook(eventTypes, hook, options) {
|
|
|
1108
1641
|
}
|
|
1109
1642
|
};
|
|
1110
1643
|
}
|
|
1644
|
+
|
|
1645
|
+
// src/core/component-detection.ts
|
|
1646
|
+
function areShadcnComponentsAvailable() {
|
|
1647
|
+
try {
|
|
1648
|
+
init_button();
|
|
1649
|
+
return true;
|
|
1650
|
+
} catch (error) {
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
function getMissingComponentsMessage() {
|
|
1655
|
+
return `Missing required shadcn components. Please run:
|
|
1656
|
+
> npm run setup-shadcn`;
|
|
1657
|
+
}
|
|
1111
1658
|
var AutoUI = ({
|
|
1112
1659
|
schema,
|
|
1113
1660
|
goal,
|
|
@@ -1118,24 +1665,32 @@ var AutoUI = ({
|
|
|
1118
1665
|
systemEventHooks,
|
|
1119
1666
|
debugMode = false,
|
|
1120
1667
|
mockMode = true,
|
|
1121
|
-
databaseConfig,
|
|
1122
1668
|
planningConfig,
|
|
1123
1669
|
integration = {},
|
|
1124
1670
|
scope = {},
|
|
1125
1671
|
enablePartialUpdates = false
|
|
1126
1672
|
}) => {
|
|
1127
|
-
const [schemaAdapterInstance
|
|
1128
|
-
const [dataContext, setDataContext] =
|
|
1673
|
+
const [schemaAdapterInstance] = React.useState(null);
|
|
1674
|
+
const [dataContext, setDataContext] = React.useState({});
|
|
1675
|
+
const [componentsAvailable, setComponentsAvailable] = React.useState(true);
|
|
1129
1676
|
const effectiveSchema = schema;
|
|
1130
1677
|
const scopedGoal = goal;
|
|
1131
|
-
|
|
1678
|
+
React.useEffect(() => {
|
|
1679
|
+
if (componentAdapter === "shadcn") {
|
|
1680
|
+
setComponentsAvailable(areShadcnComponentsAvailable());
|
|
1681
|
+
}
|
|
1682
|
+
}, [componentAdapter]);
|
|
1683
|
+
React.useEffect(() => {
|
|
1132
1684
|
const unregisters = [];
|
|
1133
1685
|
if (systemEventHooks) {
|
|
1134
1686
|
Object.entries(systemEventHooks).forEach(([eventType, hooks]) => {
|
|
1135
1687
|
if (!hooks)
|
|
1136
1688
|
return;
|
|
1137
1689
|
hooks.forEach((hook) => {
|
|
1138
|
-
const unregister = systemEvents.on(
|
|
1690
|
+
const unregister = systemEvents.on(
|
|
1691
|
+
eventType,
|
|
1692
|
+
hook
|
|
1693
|
+
);
|
|
1139
1694
|
unregisters.push(unregister);
|
|
1140
1695
|
});
|
|
1141
1696
|
});
|
|
@@ -1144,7 +1699,9 @@ var AutoUI = ({
|
|
|
1144
1699
|
const debugHook = (event) => {
|
|
1145
1700
|
console.debug(`[AutoUI Debug] System Event:`, event);
|
|
1146
1701
|
};
|
|
1147
|
-
Object.values(SystemEventType).
|
|
1702
|
+
Object.values(SystemEventType).filter(
|
|
1703
|
+
(eventType) => eventType !== "RENDER_START" /* RENDER_START */ && eventType !== "BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */
|
|
1704
|
+
).forEach((eventType) => {
|
|
1148
1705
|
const unregister = systemEvents.on(eventType, debugHook);
|
|
1149
1706
|
unregisters.push(unregister);
|
|
1150
1707
|
});
|
|
@@ -1153,7 +1710,7 @@ var AutoUI = ({
|
|
|
1153
1710
|
unregisters.forEach((unregister) => unregister());
|
|
1154
1711
|
};
|
|
1155
1712
|
}, [systemEventHooks, debugMode]);
|
|
1156
|
-
|
|
1713
|
+
React.useEffect(() => {
|
|
1157
1714
|
const initializeDataContext = async () => {
|
|
1158
1715
|
let initialData = {};
|
|
1159
1716
|
if (schemaAdapterInstance) {
|
|
@@ -1185,101 +1742,143 @@ var AutoUI = ({
|
|
|
1185
1742
|
dataContext,
|
|
1186
1743
|
enablePartialUpdates
|
|
1187
1744
|
});
|
|
1188
|
-
const eventManagerRef =
|
|
1189
|
-
|
|
1745
|
+
const eventManagerRef = React.useRef(new EventManager());
|
|
1746
|
+
React.useEffect(() => {
|
|
1190
1747
|
if (!eventHooks)
|
|
1191
1748
|
return;
|
|
1192
1749
|
const unregisters = [];
|
|
1193
1750
|
if (eventHooks.all) {
|
|
1194
|
-
const unregister = eventManagerRef.current.register(
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1751
|
+
const unregister = eventManagerRef.current.register(
|
|
1752
|
+
"all",
|
|
1753
|
+
async (ctx) => {
|
|
1754
|
+
for (const hook of eventHooks.all || []) {
|
|
1755
|
+
await hook(ctx);
|
|
1756
|
+
if (ctx.isPropagationStopped())
|
|
1757
|
+
break;
|
|
1758
|
+
}
|
|
1199
1759
|
}
|
|
1200
|
-
|
|
1760
|
+
);
|
|
1201
1761
|
unregisters.push(unregister);
|
|
1202
1762
|
}
|
|
1203
1763
|
Object.entries(eventHooks).forEach(([type, hooks]) => {
|
|
1204
1764
|
if (type === "all" || !hooks)
|
|
1205
1765
|
return;
|
|
1206
|
-
const unregister = eventManagerRef.current.register(
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1766
|
+
const unregister = eventManagerRef.current.register(
|
|
1767
|
+
[type],
|
|
1768
|
+
async (ctx) => {
|
|
1769
|
+
for (const hook of hooks) {
|
|
1770
|
+
await hook(ctx);
|
|
1771
|
+
if (ctx.isPropagationStopped())
|
|
1772
|
+
break;
|
|
1773
|
+
}
|
|
1211
1774
|
}
|
|
1212
|
-
|
|
1775
|
+
);
|
|
1213
1776
|
unregisters.push(unregister);
|
|
1214
1777
|
});
|
|
1215
1778
|
return () => {
|
|
1216
1779
|
unregisters.forEach((unregister) => unregister());
|
|
1217
1780
|
};
|
|
1218
1781
|
}, [eventHooks]);
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
onEvent
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1782
|
+
const processEvent2 = React.useCallback(
|
|
1783
|
+
async (event) => {
|
|
1784
|
+
const shouldProceed = await eventManagerRef.current.processEvent(event);
|
|
1785
|
+
if (onEvent) {
|
|
1786
|
+
onEvent(event);
|
|
1787
|
+
}
|
|
1788
|
+
if (!shouldProceed) {
|
|
1789
|
+
console.info("Event processing was prevented by hooks", event);
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
const findNodeById2 = (node, id) => {
|
|
1793
|
+
if (!node)
|
|
1794
|
+
return void 0;
|
|
1795
|
+
if (node.id === id)
|
|
1796
|
+
return node;
|
|
1797
|
+
if (node.children) {
|
|
1798
|
+
for (const child of node.children) {
|
|
1799
|
+
const found = findNodeById2(child, id);
|
|
1800
|
+
if (found)
|
|
1801
|
+
return found;
|
|
1802
|
+
}
|
|
1238
1803
|
}
|
|
1804
|
+
return void 0;
|
|
1805
|
+
};
|
|
1806
|
+
const sourceNode = findNodeById2(state.layout, event.nodeId);
|
|
1807
|
+
if (!sourceNode) {
|
|
1808
|
+
console.warn(`Node not found for event: ${event.nodeId}`);
|
|
1809
|
+
handleEvent(event);
|
|
1810
|
+
return;
|
|
1239
1811
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1812
|
+
const eventConfig = sourceNode.events?.[event.type];
|
|
1813
|
+
if (!eventConfig) {
|
|
1814
|
+
console.warn(
|
|
1815
|
+
`No event config found for ${event.type} on node ${event.nodeId}`
|
|
1816
|
+
);
|
|
1817
|
+
handleEvent(event);
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const newContext = executeAction(
|
|
1821
|
+
eventConfig.action,
|
|
1822
|
+
eventConfig.target || "",
|
|
1823
|
+
// Provide empty string as fallback if target is null
|
|
1824
|
+
{
|
|
1825
|
+
...eventConfig.payload,
|
|
1826
|
+
...event.payload
|
|
1827
|
+
},
|
|
1828
|
+
dataContext,
|
|
1829
|
+
state.layout || void 0
|
|
1830
|
+
);
|
|
1831
|
+
setDataContext(newContext);
|
|
1251
1832
|
handleEvent(event);
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
state.layout
|
|
1263
|
-
);
|
|
1264
|
-
setDataContext(newContext);
|
|
1265
|
-
handleEvent(event);
|
|
1266
|
-
}, [dataContext, handleEvent, onEvent, state.layout]);
|
|
1267
|
-
const [resolvedLayout, setResolvedLayout] = react.useState(void 0);
|
|
1268
|
-
const [renderedNode, setRenderedNode] = react.useState(null);
|
|
1269
|
-
react.useEffect(() => {
|
|
1833
|
+
},
|
|
1834
|
+
[dataContext, handleEvent, onEvent, state.layout]
|
|
1835
|
+
);
|
|
1836
|
+
const [resolvedLayout, setResolvedLayout] = React.useState(
|
|
1837
|
+
void 0
|
|
1838
|
+
);
|
|
1839
|
+
const [renderedNode, setRenderedNode] = React.useState(
|
|
1840
|
+
null
|
|
1841
|
+
);
|
|
1842
|
+
const resolveLayoutBindings = React.useCallback(async () => {
|
|
1270
1843
|
if (state.layout) {
|
|
1271
|
-
|
|
1844
|
+
try {
|
|
1845
|
+
const resolved = await resolveBindings(state.layout, dataContext);
|
|
1846
|
+
setResolvedLayout(resolved);
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
console.error("Error resolving bindings:", err);
|
|
1849
|
+
}
|
|
1272
1850
|
} else {
|
|
1273
1851
|
setResolvedLayout(void 0);
|
|
1274
1852
|
}
|
|
1275
1853
|
}, [state.layout, dataContext]);
|
|
1276
|
-
|
|
1854
|
+
React.useEffect(() => {
|
|
1855
|
+
resolveLayoutBindings();
|
|
1856
|
+
}, [resolveLayoutBindings]);
|
|
1857
|
+
const renderResolvedLayout = React.useCallback(async () => {
|
|
1277
1858
|
if (resolvedLayout) {
|
|
1278
|
-
|
|
1859
|
+
try {
|
|
1860
|
+
const rendered = await renderNode2(
|
|
1861
|
+
resolvedLayout,
|
|
1862
|
+
componentAdapter,
|
|
1863
|
+
processEvent2
|
|
1864
|
+
);
|
|
1865
|
+
setRenderedNode(rendered);
|
|
1866
|
+
} catch (err) {
|
|
1867
|
+
console.error("Error rendering node:", err);
|
|
1868
|
+
}
|
|
1279
1869
|
} else {
|
|
1280
1870
|
setRenderedNode(null);
|
|
1281
1871
|
}
|
|
1282
|
-
}, [resolvedLayout, componentAdapter]);
|
|
1872
|
+
}, [resolvedLayout, componentAdapter, processEvent2]);
|
|
1873
|
+
React.useEffect(() => {
|
|
1874
|
+
renderResolvedLayout();
|
|
1875
|
+
}, [renderResolvedLayout]);
|
|
1876
|
+
if (!componentsAvailable) {
|
|
1877
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 text-red-700 rounded", children: [
|
|
1878
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Component Library Not Found" }),
|
|
1879
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-line", children: getMissingComponentsMessage() })
|
|
1880
|
+
] });
|
|
1881
|
+
}
|
|
1283
1882
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1284
1883
|
"div",
|
|
1285
1884
|
{
|
|
@@ -1298,9 +1897,22 @@ var AutoUI = ({
|
|
|
1298
1897
|
// Render the resolved layout
|
|
1299
1898
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderedNode })
|
|
1300
1899
|
),
|
|
1301
|
-
state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error", children: [
|
|
1302
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-title", children: "Error generating UI" }),
|
|
1303
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message", children: state.error })
|
|
1900
|
+
state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 dark:bg-red-900 dark:border-red-700 rounded-md", children: [
|
|
1901
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-title text-lg font-semibold text-red-700 dark:text-red-300 mb-2", children: "Error generating UI" }),
|
|
1902
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message text-sm text-red-600 dark:text-red-300", children: state.error }),
|
|
1903
|
+
!mockMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 text-sm text-red-600 dark:text-red-300", children: [
|
|
1904
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "This could be because:" }),
|
|
1905
|
+
/* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "list-disc pl-5 mt-2", children: [
|
|
1906
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your OpenAI API key is missing or invalid" }),
|
|
1907
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "The OpenAI service is experiencing issues" }),
|
|
1908
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your API rate limit has been exceeded" })
|
|
1909
|
+
] }),
|
|
1910
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2", children: [
|
|
1911
|
+
"Try setting ",
|
|
1912
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "mockMode=true" }),
|
|
1913
|
+
" to use sample data instead."
|
|
1914
|
+
] })
|
|
1915
|
+
] })
|
|
1304
1916
|
] })
|
|
1305
1917
|
]
|
|
1306
1918
|
}
|
|
@@ -1353,26 +1965,26 @@ var DrizzleAdapter = class {
|
|
|
1353
1965
|
*/
|
|
1354
1966
|
mapDataType(drizzleType) {
|
|
1355
1967
|
const typeMap = {
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1968
|
+
serial: "integer",
|
|
1969
|
+
integer: "integer",
|
|
1970
|
+
int: "integer",
|
|
1971
|
+
bigint: "integer",
|
|
1972
|
+
text: "string",
|
|
1973
|
+
varchar: "string",
|
|
1974
|
+
char: "string",
|
|
1975
|
+
boolean: "boolean",
|
|
1976
|
+
bool: "boolean",
|
|
1977
|
+
timestamp: "datetime",
|
|
1978
|
+
timestamptz: "datetime",
|
|
1979
|
+
date: "date",
|
|
1980
|
+
time: "time",
|
|
1981
|
+
json: "object",
|
|
1982
|
+
jsonb: "object",
|
|
1983
|
+
real: "number",
|
|
1984
|
+
float: "number",
|
|
1985
|
+
double: "number",
|
|
1986
|
+
numeric: "number",
|
|
1987
|
+
decimal: "number"
|
|
1376
1988
|
};
|
|
1377
1989
|
return typeMap[drizzleType.toLowerCase()] || "string";
|
|
1378
1990
|
}
|
|
@@ -1415,35 +2027,90 @@ function createSchemaAdapter(options) {
|
|
|
1415
2027
|
case "custom":
|
|
1416
2028
|
return options.adapter;
|
|
1417
2029
|
default:
|
|
1418
|
-
throw new Error(
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
async function generateComponent(schema) {
|
|
1422
|
-
try {
|
|
1423
|
-
const { text } = await ai.generateText({
|
|
1424
|
-
model: openai.openai("gpt-4"),
|
|
1425
|
-
prompt: `Generate a React component based on this data schema: ${JSON.stringify(schema)}`
|
|
1426
|
-
});
|
|
1427
|
-
return text;
|
|
1428
|
-
} catch (error) {
|
|
1429
|
-
console.error("Error generating component:", error);
|
|
1430
|
-
throw error;
|
|
2030
|
+
throw new Error(
|
|
2031
|
+
`Unsupported schema adapter type: ${options.type}`
|
|
2032
|
+
);
|
|
1431
2033
|
}
|
|
1432
2034
|
}
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
return
|
|
1440
|
-
}
|
|
1441
|
-
async
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
}
|
|
1446
|
-
|
|
2035
|
+
|
|
2036
|
+
// src/ai-utils.ts
|
|
2037
|
+
var generateComponent = async (prompt) => {
|
|
2038
|
+
console.warn(
|
|
2039
|
+
"generateComponent is a placeholder and will be implemented in a future version"
|
|
2040
|
+
);
|
|
2041
|
+
return `<div>Generated Component for: ${prompt}</div>`;
|
|
2042
|
+
};
|
|
2043
|
+
var generateUIDescription = async (prompt) => {
|
|
2044
|
+
console.warn(
|
|
2045
|
+
"generateUIDescription is a placeholder and will be implemented in a future version"
|
|
2046
|
+
);
|
|
2047
|
+
return `Description for ${prompt}`;
|
|
2048
|
+
};
|
|
2049
|
+
var generateUIComponent = async (prompt) => {
|
|
2050
|
+
console.warn(
|
|
2051
|
+
"generateUIComponent is a placeholder and will be implemented in a future version"
|
|
2052
|
+
);
|
|
2053
|
+
return `<div>Generated UI Component for: ${prompt}</div>`;
|
|
2054
|
+
};
|
|
2055
|
+
function usePlanner(options) {
|
|
2056
|
+
const { goal, schema, userContext, router: customRouter } = options;
|
|
2057
|
+
const [layout, setLayout] = React.useState(void 0);
|
|
2058
|
+
const [loading, setLoading] = React.useState(false);
|
|
2059
|
+
const [error, setError] = React.useState(null);
|
|
2060
|
+
const router = customRouter || createDefaultRouter();
|
|
2061
|
+
const dataContext = {};
|
|
2062
|
+
const generateInitialLayout = React.useCallback(async () => {
|
|
2063
|
+
setLoading(true);
|
|
2064
|
+
setError(null);
|
|
2065
|
+
try {
|
|
2066
|
+
const plannerInput2 = {
|
|
2067
|
+
schema,
|
|
2068
|
+
goal,
|
|
2069
|
+
userContext: userContext || null,
|
|
2070
|
+
history: null
|
|
2071
|
+
};
|
|
2072
|
+
const generatedLayout = await callPlannerLLM(plannerInput2);
|
|
2073
|
+
setLayout(generatedLayout);
|
|
2074
|
+
} catch (err) {
|
|
2075
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2076
|
+
} finally {
|
|
2077
|
+
setLoading(false);
|
|
2078
|
+
}
|
|
2079
|
+
}, [schema, goal, userContext]);
|
|
2080
|
+
const handleEvent = React.useCallback(
|
|
2081
|
+
async (event) => {
|
|
2082
|
+
if (!layout) {
|
|
2083
|
+
setError(new Error("Cannot handle event - no layout exists"));
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
setLoading(true);
|
|
2087
|
+
setError(null);
|
|
2088
|
+
try {
|
|
2089
|
+
const updatedLayout = await processEvent(
|
|
2090
|
+
event,
|
|
2091
|
+
router,
|
|
2092
|
+
schema,
|
|
2093
|
+
layout,
|
|
2094
|
+
dataContext,
|
|
2095
|
+
goal,
|
|
2096
|
+
userContext
|
|
2097
|
+
);
|
|
2098
|
+
setLayout(updatedLayout);
|
|
2099
|
+
} catch (err) {
|
|
2100
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
2101
|
+
} finally {
|
|
2102
|
+
setLoading(false);
|
|
2103
|
+
}
|
|
2104
|
+
},
|
|
2105
|
+
[layout, router, schema, dataContext, goal, userContext]
|
|
2106
|
+
);
|
|
2107
|
+
return {
|
|
2108
|
+
layout,
|
|
2109
|
+
loading,
|
|
2110
|
+
error,
|
|
2111
|
+
handleEvent,
|
|
2112
|
+
generateInitialLayout
|
|
2113
|
+
};
|
|
1447
2114
|
}
|
|
1448
2115
|
|
|
1449
2116
|
exports.ActionRouter = ActionRouter;
|
|
@@ -1462,5 +2129,6 @@ exports.systemEvents = systemEvents;
|
|
|
1462
2129
|
exports.uiEvent = uiEvent;
|
|
1463
2130
|
exports.uiEventType = uiEventType;
|
|
1464
2131
|
exports.uiSpecNode = uiSpecNode;
|
|
2132
|
+
exports.usePlanner = usePlanner;
|
|
1465
2133
|
//# sourceMappingURL=out.js.map
|
|
1466
2134
|
//# sourceMappingURL=index.js.map
|