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