autoui-react 0.1.0 → 0.1.1-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 +52 -6
- package/dist/index.css +10 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +453 -121
- package/dist/index.d.ts +453 -121
- package/dist/index.js +3163 -794
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3123 -786
- package/dist/index.mjs.map +1 -1
- package/package.json +49 -9
package/dist/index.js
CHANGED
|
@@ -1,26 +1,142 @@
|
|
|
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');
|
|
8
|
+
var anthropic = require('@ai-sdk/anthropic');
|
|
9
|
+
var ai = require('ai');
|
|
10
|
+
var zod = require('zod');
|
|
11
|
+
var React = require('react');
|
|
12
|
+
var DialogPrimitive = require('@radix-ui/react-dialog');
|
|
13
|
+
var lucideReact = require('lucide-react');
|
|
14
|
+
var SelectPrimitive = require('@radix-ui/react-select');
|
|
15
|
+
var CheckboxPrimitive = require('@radix-ui/react-checkbox');
|
|
16
|
+
var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
|
|
17
|
+
var TabsPrimitive = require('@radix-ui/react-tabs');
|
|
18
|
+
var LabelPrimitive = require('@radix-ui/react-label');
|
|
19
|
+
|
|
20
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
+
|
|
22
|
+
function _interopNamespace(e) {
|
|
23
|
+
if (e && e.__esModule) return e;
|
|
24
|
+
var n = Object.create(null);
|
|
25
|
+
if (e) {
|
|
26
|
+
Object.keys(e).forEach(function (k) {
|
|
27
|
+
if (k !== 'default') {
|
|
28
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
29
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () { return e[k]; }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
n.default = e;
|
|
37
|
+
return Object.freeze(n);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
41
|
+
var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
|
|
42
|
+
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
43
|
+
var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
|
|
44
|
+
var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
|
|
45
|
+
var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
|
|
46
|
+
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
47
|
+
|
|
48
|
+
var __defProp = Object.defineProperty;
|
|
49
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
50
|
+
var __esm = (fn, res) => function __init() {
|
|
51
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
52
|
+
};
|
|
53
|
+
var __export = (target, all) => {
|
|
54
|
+
for (var name in all)
|
|
55
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
56
|
+
};
|
|
57
|
+
function cn(...inputs) {
|
|
58
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
59
|
+
}
|
|
60
|
+
var init_utils = __esm({
|
|
61
|
+
"src/lib/utils.ts"() {
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// components/ui/button.tsx
|
|
66
|
+
var button_exports = {};
|
|
67
|
+
__export(button_exports, {
|
|
68
|
+
Button: () => Button2,
|
|
69
|
+
buttonVariants: () => buttonVariants
|
|
70
|
+
});
|
|
71
|
+
function Button2({
|
|
72
|
+
className,
|
|
73
|
+
variant,
|
|
74
|
+
size,
|
|
75
|
+
asChild = false,
|
|
76
|
+
...props
|
|
77
|
+
}) {
|
|
78
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
Comp,
|
|
81
|
+
{
|
|
82
|
+
"data-slot": "button",
|
|
83
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
84
|
+
...props
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
var buttonVariants;
|
|
89
|
+
var init_button = __esm({
|
|
90
|
+
"components/ui/button.tsx"() {
|
|
91
|
+
init_utils();
|
|
92
|
+
buttonVariants = classVarianceAuthority.cva(
|
|
93
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
94
|
+
{
|
|
95
|
+
variants: {
|
|
96
|
+
variant: {
|
|
97
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
98
|
+
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
99
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
100
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
101
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
102
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
103
|
+
},
|
|
104
|
+
size: {
|
|
105
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
106
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
107
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
108
|
+
icon: "size-9"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
defaultVariants: {
|
|
112
|
+
variant: "default",
|
|
113
|
+
size: "default"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
6
119
|
|
|
7
120
|
// src/core/reducer.ts
|
|
8
121
|
function cloneNode(node) {
|
|
9
122
|
return {
|
|
10
123
|
...node,
|
|
11
|
-
props: node.props ? { ...node.props } :
|
|
12
|
-
bindings: node.bindings ? { ...node.bindings } :
|
|
13
|
-
events: node.events ? { ...node.events } :
|
|
14
|
-
children: node.children
|
|
124
|
+
props: node.props ? { ...node.props } : null,
|
|
125
|
+
bindings: node.bindings ? { ...node.bindings } : null,
|
|
126
|
+
events: node.events ? { ...node.events } : null,
|
|
127
|
+
children: node.children ? node.children.map((child) => cloneNode(child)) : null
|
|
15
128
|
};
|
|
16
129
|
}
|
|
17
130
|
function findNodeById(tree, nodeId) {
|
|
18
|
-
if (!tree)
|
|
19
|
-
|
|
131
|
+
if (!tree)
|
|
132
|
+
return void 0;
|
|
133
|
+
if (tree.id === nodeId)
|
|
134
|
+
return tree;
|
|
20
135
|
if (tree.children) {
|
|
21
136
|
for (const child of tree.children) {
|
|
22
137
|
const found = findNodeById(child, nodeId);
|
|
23
|
-
if (found)
|
|
138
|
+
if (found)
|
|
139
|
+
return found;
|
|
24
140
|
}
|
|
25
141
|
}
|
|
26
142
|
return void 0;
|
|
@@ -35,13 +151,15 @@ function updateNodeById(tree, nodeId, updater) {
|
|
|
35
151
|
if (node.children) {
|
|
36
152
|
for (const child of node.children) {
|
|
37
153
|
const path2 = findPath(child, id, newPath);
|
|
38
|
-
if (path2)
|
|
154
|
+
if (path2)
|
|
155
|
+
return path2;
|
|
39
156
|
}
|
|
40
157
|
}
|
|
41
158
|
return null;
|
|
42
159
|
}
|
|
43
160
|
const path = findPath(result, nodeId);
|
|
44
|
-
if (!path)
|
|
161
|
+
if (!path)
|
|
162
|
+
return result;
|
|
45
163
|
const nodeToUpdate = path[path.length - 1];
|
|
46
164
|
const updatedNode = updater(nodeToUpdate);
|
|
47
165
|
if (path.length === 1) {
|
|
@@ -50,18 +168,14 @@ function updateNodeById(tree, nodeId, updater) {
|
|
|
50
168
|
const parent = path[path.length - 2];
|
|
51
169
|
const updatedParent = {
|
|
52
170
|
...parent,
|
|
53
|
-
children: parent.children
|
|
171
|
+
children: parent.children ? parent.children.map(
|
|
54
172
|
(child) => child.id === nodeId ? updatedNode : child
|
|
55
|
-
)
|
|
173
|
+
) : null
|
|
56
174
|
};
|
|
57
175
|
if (path.length === 2) {
|
|
58
176
|
return updatedParent;
|
|
59
177
|
}
|
|
60
|
-
return updateNodeById(
|
|
61
|
-
result,
|
|
62
|
-
parent.id,
|
|
63
|
-
() => updatedParent
|
|
64
|
-
);
|
|
178
|
+
return updateNodeById(result, parent.id, () => updatedParent);
|
|
65
179
|
}
|
|
66
180
|
function replaceNodeById(tree, nodeId, newNode) {
|
|
67
181
|
return updateNodeById(tree, nodeId, () => newNode);
|
|
@@ -88,7 +202,8 @@ function removeNodeById(tree, nodeId) {
|
|
|
88
202
|
}
|
|
89
203
|
for (const child of node.children) {
|
|
90
204
|
const parent2 = findParent(child, id);
|
|
91
|
-
if (parent2)
|
|
205
|
+
if (parent2)
|
|
206
|
+
return parent2;
|
|
92
207
|
}
|
|
93
208
|
}
|
|
94
209
|
return null;
|
|
@@ -98,15 +213,20 @@ function removeNodeById(tree, nodeId) {
|
|
|
98
213
|
throw new Error("Cannot remove root node");
|
|
99
214
|
}
|
|
100
215
|
const parent = findParent(result, nodeId);
|
|
101
|
-
if (!parent)
|
|
216
|
+
if (!parent)
|
|
217
|
+
return result;
|
|
102
218
|
return updateNodeById(result, parent.id, (node) => ({
|
|
103
219
|
...node,
|
|
104
|
-
children: node.children
|
|
220
|
+
children: node.children ? node.children.filter((child) => child.id !== nodeId) : null
|
|
105
221
|
}));
|
|
106
222
|
}
|
|
107
223
|
function uiReducer(state, action) {
|
|
108
224
|
switch (action.type) {
|
|
109
225
|
case "UI_EVENT": {
|
|
226
|
+
if (action.event.type === "INIT" && state.history.some((e) => e.type === "INIT")) {
|
|
227
|
+
console.log("[AutoUI uiReducer] Ignoring duplicate INIT event");
|
|
228
|
+
return state;
|
|
229
|
+
}
|
|
110
230
|
return {
|
|
111
231
|
...state,
|
|
112
232
|
loading: true,
|
|
@@ -118,7 +238,7 @@ function uiReducer(state, action) {
|
|
|
118
238
|
...state,
|
|
119
239
|
layout: action.node,
|
|
120
240
|
loading: false,
|
|
121
|
-
error:
|
|
241
|
+
error: null
|
|
122
242
|
};
|
|
123
243
|
}
|
|
124
244
|
case "PARTIAL_UPDATE": {
|
|
@@ -127,7 +247,7 @@ function uiReducer(state, action) {
|
|
|
127
247
|
...state,
|
|
128
248
|
layout: action.node,
|
|
129
249
|
loading: false,
|
|
130
|
-
error:
|
|
250
|
+
error: null
|
|
131
251
|
};
|
|
132
252
|
}
|
|
133
253
|
if (action.nodeId === "root" || action.nodeId === state.layout.id) {
|
|
@@ -135,19 +255,23 @@ function uiReducer(state, action) {
|
|
|
135
255
|
...state,
|
|
136
256
|
layout: action.node,
|
|
137
257
|
loading: false,
|
|
138
|
-
error:
|
|
258
|
+
error: null
|
|
139
259
|
};
|
|
140
260
|
}
|
|
141
261
|
return {
|
|
142
262
|
...state,
|
|
143
263
|
layout: replaceNodeById(state.layout, action.nodeId, action.node),
|
|
144
264
|
loading: false,
|
|
145
|
-
error:
|
|
265
|
+
error: null
|
|
146
266
|
};
|
|
147
267
|
}
|
|
148
268
|
case "ADD_NODE": {
|
|
149
269
|
if (!state.layout) {
|
|
150
|
-
return
|
|
270
|
+
return {
|
|
271
|
+
...state,
|
|
272
|
+
error: "Cannot add node: Layout is empty.",
|
|
273
|
+
loading: false
|
|
274
|
+
};
|
|
151
275
|
}
|
|
152
276
|
return {
|
|
153
277
|
...state,
|
|
@@ -155,21 +279,41 @@ function uiReducer(state, action) {
|
|
|
155
279
|
state.layout,
|
|
156
280
|
action.parentId,
|
|
157
281
|
action.node,
|
|
158
|
-
action.index
|
|
282
|
+
action.index === null ? void 0 : action.index
|
|
159
283
|
),
|
|
160
284
|
loading: false,
|
|
161
|
-
error:
|
|
285
|
+
error: null
|
|
162
286
|
};
|
|
163
287
|
}
|
|
164
288
|
case "REMOVE_NODE": {
|
|
165
289
|
if (!state.layout) {
|
|
166
|
-
return
|
|
290
|
+
return {
|
|
291
|
+
...state,
|
|
292
|
+
error: "Cannot remove node: Layout is empty.",
|
|
293
|
+
loading: false
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
try {
|
|
297
|
+
return {
|
|
298
|
+
...state,
|
|
299
|
+
layout: removeNodeById(state.layout, action.nodeId),
|
|
300
|
+
loading: false,
|
|
301
|
+
error: null
|
|
302
|
+
};
|
|
303
|
+
} catch (e) {
|
|
304
|
+
const errorMessage = e instanceof Error ? e.message : "Failed to remove node.";
|
|
305
|
+
return {
|
|
306
|
+
...state,
|
|
307
|
+
error: errorMessage,
|
|
308
|
+
loading: false
|
|
309
|
+
};
|
|
167
310
|
}
|
|
311
|
+
}
|
|
312
|
+
case "ERROR": {
|
|
168
313
|
return {
|
|
169
314
|
...state,
|
|
170
|
-
|
|
171
|
-
loading: false
|
|
172
|
-
error: void 0
|
|
315
|
+
error: action.message,
|
|
316
|
+
loading: false
|
|
173
317
|
};
|
|
174
318
|
}
|
|
175
319
|
case "LOADING": {
|
|
@@ -178,11 +322,10 @@ function uiReducer(state, action) {
|
|
|
178
322
|
loading: action.isLoading
|
|
179
323
|
};
|
|
180
324
|
}
|
|
181
|
-
case "
|
|
325
|
+
case "SET_DATA_CONTEXT": {
|
|
182
326
|
return {
|
|
183
327
|
...state,
|
|
184
|
-
|
|
185
|
-
loading: false
|
|
328
|
+
dataContext: action.payload
|
|
186
329
|
};
|
|
187
330
|
}
|
|
188
331
|
default:
|
|
@@ -190,246 +333,33 @@ function uiReducer(state, action) {
|
|
|
190
333
|
}
|
|
191
334
|
}
|
|
192
335
|
var initialState = {
|
|
336
|
+
layout: null,
|
|
193
337
|
loading: true,
|
|
194
|
-
|
|
338
|
+
error: null,
|
|
339
|
+
history: [],
|
|
340
|
+
dataContext: {}
|
|
195
341
|
};
|
|
196
342
|
|
|
197
|
-
// src/
|
|
343
|
+
// src/schema/action-types.ts
|
|
198
344
|
var ActionType = /* @__PURE__ */ ((ActionType2) => {
|
|
199
345
|
ActionType2["FULL_REFRESH"] = "FULL_REFRESH";
|
|
200
346
|
ActionType2["UPDATE_NODE"] = "UPDATE_NODE";
|
|
347
|
+
ActionType2["UPDATE_DATA"] = "UPDATE_DATA";
|
|
348
|
+
ActionType2["ADD_ITEM"] = "ADD_ITEM";
|
|
349
|
+
ActionType2["DELETE_ITEM"] = "DELETE_ITEM";
|
|
201
350
|
ActionType2["ADD_DROPDOWN"] = "ADD_DROPDOWN";
|
|
202
351
|
ActionType2["SHOW_DETAIL"] = "SHOW_DETAIL";
|
|
203
352
|
ActionType2["HIDE_DETAIL"] = "HIDE_DETAIL";
|
|
353
|
+
ActionType2["HIDE_DIALOG"] = "HIDE_DIALOG";
|
|
354
|
+
ActionType2["SAVE_TASK_CHANGES"] = "SAVE_TASK_CHANGES";
|
|
204
355
|
ActionType2["TOGGLE_STATE"] = "TOGGLE_STATE";
|
|
205
356
|
ActionType2["UPDATE_FORM"] = "UPDATE_FORM";
|
|
206
357
|
ActionType2["NAVIGATE"] = "NAVIGATE";
|
|
358
|
+
ActionType2["OPEN_DIALOG"] = "OPEN_DIALOG";
|
|
359
|
+
ActionType2["CLOSE_DIALOG"] = "CLOSE_DIALOG";
|
|
360
|
+
ActionType2["UPDATE_CONTEXT"] = "UPDATE_CONTEXT";
|
|
207
361
|
return ActionType2;
|
|
208
362
|
})(ActionType || {});
|
|
209
|
-
var ActionRouter = class {
|
|
210
|
-
constructor() {
|
|
211
|
-
this.routes = {};
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Register a new action route
|
|
215
|
-
* @param eventType - UI event type to route
|
|
216
|
-
* @param config - Route configuration
|
|
217
|
-
*/
|
|
218
|
-
registerRoute(eventType, config) {
|
|
219
|
-
if (!this.routes[eventType]) {
|
|
220
|
-
this.routes[eventType] = [];
|
|
221
|
-
}
|
|
222
|
-
this.routes[eventType].push(config);
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Find the appropriate route for an event
|
|
226
|
-
* @param event - UI event
|
|
227
|
-
* @param layout - Current UI layout
|
|
228
|
-
* @param dataContext - Current data context
|
|
229
|
-
* @returns Route resolution or null if no match
|
|
230
|
-
*/
|
|
231
|
-
resolveRoute(event, schema, layout, dataContext, goal, userContext) {
|
|
232
|
-
const routes = this.routes[event.type] || [];
|
|
233
|
-
if (routes.length === 0) {
|
|
234
|
-
return {
|
|
235
|
-
actionType: "FULL_REFRESH" /* FULL_REFRESH */,
|
|
236
|
-
targetNodeId: layout?.id || "root",
|
|
237
|
-
plannerInput: {
|
|
238
|
-
schema,
|
|
239
|
-
goal,
|
|
240
|
-
history: [event],
|
|
241
|
-
userContext
|
|
242
|
-
},
|
|
243
|
-
prompt: `Generate a new UI for the goal: "${goal}". The user just triggered: ${event.type} on node ${event.nodeId}`
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
|
|
247
|
-
const nodeConfig = sourceNode?.events?.[event.type];
|
|
248
|
-
let matchingRoute;
|
|
249
|
-
if (nodeConfig) {
|
|
250
|
-
matchingRoute = routes.find(
|
|
251
|
-
(route) => route.actionType.toString() === nodeConfig.action
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
if (!matchingRoute) {
|
|
255
|
-
matchingRoute = routes[0];
|
|
256
|
-
}
|
|
257
|
-
const targetNodeId = nodeConfig?.target || matchingRoute.targetNodeId || event.nodeId;
|
|
258
|
-
const additionalContext = {};
|
|
259
|
-
if (matchingRoute.contextKeys) {
|
|
260
|
-
matchingRoute.contextKeys.forEach((key) => {
|
|
261
|
-
additionalContext[key] = dataContext[key];
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
if (sourceNode) {
|
|
265
|
-
additionalContext.sourceNode = sourceNode;
|
|
266
|
-
}
|
|
267
|
-
if (layout) {
|
|
268
|
-
const targetNode = findNodeById(layout, targetNodeId);
|
|
269
|
-
if (targetNode) {
|
|
270
|
-
additionalContext.targetNode = targetNode;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (event.payload) {
|
|
274
|
-
additionalContext.eventPayload = event.payload;
|
|
275
|
-
}
|
|
276
|
-
if (nodeConfig?.payload) {
|
|
277
|
-
Object.entries(nodeConfig.payload).forEach(([key, value]) => {
|
|
278
|
-
additionalContext[key] = value;
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
const plannerInput2 = {
|
|
282
|
-
schema,
|
|
283
|
-
goal,
|
|
284
|
-
history: [event],
|
|
285
|
-
userContext: {
|
|
286
|
-
...userContext,
|
|
287
|
-
...additionalContext
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
const prompt = this.processTemplate(
|
|
291
|
-
matchingRoute.promptTemplate,
|
|
292
|
-
{
|
|
293
|
-
goal,
|
|
294
|
-
eventType: event.type,
|
|
295
|
-
nodeId: event.nodeId,
|
|
296
|
-
targetNodeId,
|
|
297
|
-
actionType: matchingRoute.actionType,
|
|
298
|
-
...additionalContext
|
|
299
|
-
}
|
|
300
|
-
);
|
|
301
|
-
return {
|
|
302
|
-
actionType: matchingRoute.actionType,
|
|
303
|
-
targetNodeId,
|
|
304
|
-
plannerInput: plannerInput2,
|
|
305
|
-
prompt
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Process a prompt template with variables
|
|
310
|
-
* @param template - Template string with ${var} placeholders
|
|
311
|
-
* @param values - Values to substitute
|
|
312
|
-
* @returns Processed string
|
|
313
|
-
*/
|
|
314
|
-
processTemplate(template, values) {
|
|
315
|
-
return template.replace(/\${(\w+)}/g, (_, key) => {
|
|
316
|
-
return values[key] !== void 0 ? String(values[key]) : `\${${key}}`;
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
function createDefaultRouter() {
|
|
321
|
-
const router = new ActionRouter();
|
|
322
|
-
router.registerRoute("CLICK", {
|
|
323
|
-
actionType: "FULL_REFRESH" /* FULL_REFRESH */,
|
|
324
|
-
targetNodeId: "root",
|
|
325
|
-
promptTemplate: 'Generate a new UI for the goal: "${goal}". The user just clicked on node ${nodeId}'
|
|
326
|
-
});
|
|
327
|
-
router.registerRoute("CLICK", {
|
|
328
|
-
actionType: "SHOW_DETAIL" /* SHOW_DETAIL */,
|
|
329
|
-
targetNodeId: "${targetNodeId}",
|
|
330
|
-
promptTemplate: "Update the UI to show details for the selected item. Current node: ${nodeId}, Target node: ${targetNodeId}",
|
|
331
|
-
contextKeys: ["selected"]
|
|
332
|
-
});
|
|
333
|
-
router.registerRoute("CLICK", {
|
|
334
|
-
actionType: "NAVIGATE" /* NAVIGATE */,
|
|
335
|
-
targetNodeId: "root",
|
|
336
|
-
promptTemplate: "Navigate to a new view based on the user clicking ${nodeId}. Current goal: ${goal}"
|
|
337
|
-
});
|
|
338
|
-
router.registerRoute("CLICK", {
|
|
339
|
-
actionType: "ADD_DROPDOWN" /* ADD_DROPDOWN */,
|
|
340
|
-
targetNodeId: "${targetNodeId}",
|
|
341
|
-
promptTemplate: "Add a dropdown menu to node ${targetNodeId} with options relevant to the clicked element ${nodeId}"
|
|
342
|
-
});
|
|
343
|
-
router.registerRoute("CLICK", {
|
|
344
|
-
actionType: "TOGGLE_STATE" /* TOGGLE_STATE */,
|
|
345
|
-
targetNodeId: "${nodeId}",
|
|
346
|
-
promptTemplate: "Toggle the state of node ${nodeId} (e.g., expanded/collapsed, selected/unselected)"
|
|
347
|
-
});
|
|
348
|
-
router.registerRoute("CHANGE", {
|
|
349
|
-
actionType: "UPDATE_FORM" /* UPDATE_FORM */,
|
|
350
|
-
targetNodeId: "${targetNodeId}",
|
|
351
|
-
promptTemplate: "Update the form based on the user changing the value of ${nodeId}"
|
|
352
|
-
});
|
|
353
|
-
return router;
|
|
354
|
-
}
|
|
355
|
-
var uiEventType = zod.z.enum([
|
|
356
|
-
"CLICK",
|
|
357
|
-
"CHANGE",
|
|
358
|
-
"SUBMIT",
|
|
359
|
-
"MOUSEOVER",
|
|
360
|
-
"MOUSEOUT",
|
|
361
|
-
"FOCUS",
|
|
362
|
-
"BLUR"
|
|
363
|
-
]);
|
|
364
|
-
var uiEvent = zod.z.object({
|
|
365
|
-
type: uiEventType,
|
|
366
|
-
nodeId: zod.z.string(),
|
|
367
|
-
timestamp: zod.z.number().optional(),
|
|
368
|
-
payload: zod.z.record(zod.z.any()).optional()
|
|
369
|
-
});
|
|
370
|
-
zod.z.enum([
|
|
371
|
-
"AI_RESPONSE",
|
|
372
|
-
"ERROR"
|
|
373
|
-
]);
|
|
374
|
-
var uiSpecNode = zod.z.lazy(() => zod.z.object({
|
|
375
|
-
id: zod.z.string(),
|
|
376
|
-
type: zod.z.string(),
|
|
377
|
-
// e.g., "ListView", "Button", "TextField"
|
|
378
|
-
props: zod.z.record(zod.z.any()).optional(),
|
|
379
|
-
bindings: zod.z.record(zod.z.any()).optional(),
|
|
380
|
-
// Data bindings
|
|
381
|
-
events: zod.z.record(zod.z.string(), zod.z.object({
|
|
382
|
-
action: zod.z.string(),
|
|
383
|
-
target: zod.z.string().optional(),
|
|
384
|
-
payload: zod.z.record(zod.z.any()).optional()
|
|
385
|
-
})).optional(),
|
|
386
|
-
children: zod.z.array(uiSpecNode).optional()
|
|
387
|
-
}));
|
|
388
|
-
zod.z.discriminatedUnion("type", [
|
|
389
|
-
zod.z.object({
|
|
390
|
-
type: zod.z.literal("UI_EVENT"),
|
|
391
|
-
event: uiEvent
|
|
392
|
-
}),
|
|
393
|
-
zod.z.object({
|
|
394
|
-
type: zod.z.literal("AI_RESPONSE"),
|
|
395
|
-
node: uiSpecNode
|
|
396
|
-
}),
|
|
397
|
-
zod.z.object({
|
|
398
|
-
type: zod.z.literal("PARTIAL_UPDATE"),
|
|
399
|
-
nodeId: zod.z.string(),
|
|
400
|
-
node: uiSpecNode
|
|
401
|
-
}),
|
|
402
|
-
zod.z.object({
|
|
403
|
-
type: zod.z.literal("ADD_NODE"),
|
|
404
|
-
parentId: zod.z.string(),
|
|
405
|
-
node: uiSpecNode,
|
|
406
|
-
index: zod.z.number().optional()
|
|
407
|
-
}),
|
|
408
|
-
zod.z.object({
|
|
409
|
-
type: zod.z.literal("REMOVE_NODE"),
|
|
410
|
-
nodeId: zod.z.string()
|
|
411
|
-
}),
|
|
412
|
-
zod.z.object({
|
|
413
|
-
type: zod.z.literal("ERROR"),
|
|
414
|
-
message: zod.z.string()
|
|
415
|
-
}),
|
|
416
|
-
zod.z.object({
|
|
417
|
-
type: zod.z.literal("LOADING"),
|
|
418
|
-
isLoading: zod.z.boolean()
|
|
419
|
-
})
|
|
420
|
-
]);
|
|
421
|
-
zod.z.object({
|
|
422
|
-
layout: uiSpecNode.optional(),
|
|
423
|
-
loading: zod.z.boolean(),
|
|
424
|
-
history: zod.z.array(uiEvent),
|
|
425
|
-
error: zod.z.string().optional()
|
|
426
|
-
});
|
|
427
|
-
zod.z.object({
|
|
428
|
-
schema: zod.z.record(zod.z.unknown()),
|
|
429
|
-
goal: zod.z.string(),
|
|
430
|
-
history: zod.z.array(uiEvent).optional(),
|
|
431
|
-
userContext: zod.z.record(zod.z.unknown()).optional()
|
|
432
|
-
});
|
|
433
363
|
|
|
434
364
|
// src/core/system-events.ts
|
|
435
365
|
var SystemEventType = /* @__PURE__ */ ((SystemEventType2) => {
|
|
@@ -454,7 +384,7 @@ var SystemEventManager = class {
|
|
|
454
384
|
}
|
|
455
385
|
/**
|
|
456
386
|
* Register a listener for a specific system event type
|
|
457
|
-
*
|
|
387
|
+
*
|
|
458
388
|
* @param eventType - The system event type to listen for
|
|
459
389
|
* @param listener - The listener function
|
|
460
390
|
* @returns Function to unregister the listener
|
|
@@ -474,7 +404,7 @@ var SystemEventManager = class {
|
|
|
474
404
|
}
|
|
475
405
|
/**
|
|
476
406
|
* Emit a system event to all registered listeners
|
|
477
|
-
*
|
|
407
|
+
*
|
|
478
408
|
* @param event - The system event to emit
|
|
479
409
|
*/
|
|
480
410
|
async emit(event) {
|
|
@@ -492,259 +422,1711 @@ function createSystemEvent(type, data) {
|
|
|
492
422
|
...data
|
|
493
423
|
};
|
|
494
424
|
}
|
|
425
|
+
var componentType = zod.z.enum([
|
|
426
|
+
// Layout components
|
|
427
|
+
"Container",
|
|
428
|
+
"Card",
|
|
429
|
+
"Header",
|
|
430
|
+
// Input components
|
|
431
|
+
"Button",
|
|
432
|
+
"Input",
|
|
433
|
+
"Select",
|
|
434
|
+
"Textarea",
|
|
435
|
+
"Checkbox",
|
|
436
|
+
"RadioGroup",
|
|
437
|
+
// Data display components
|
|
438
|
+
"ListView",
|
|
439
|
+
"Detail",
|
|
440
|
+
"Tabs",
|
|
441
|
+
"Dialog",
|
|
442
|
+
"Badge",
|
|
443
|
+
// Typography
|
|
444
|
+
"Heading",
|
|
445
|
+
"Text"
|
|
446
|
+
]);
|
|
495
447
|
|
|
496
|
-
// src/
|
|
497
|
-
(
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
448
|
+
// src/schema/openai-ui-spec.ts
|
|
449
|
+
var actionTypeEnum = zod.z.enum([
|
|
450
|
+
"FULL_REFRESH",
|
|
451
|
+
"UPDATE_NODE",
|
|
452
|
+
"UPDATE_DATA",
|
|
453
|
+
"ADD_DROPDOWN",
|
|
454
|
+
"SHOW_DETAIL",
|
|
455
|
+
"HIDE_DETAIL",
|
|
456
|
+
"HIDE_DIALOG",
|
|
457
|
+
"SAVE_TASK_CHANGES",
|
|
458
|
+
"TOGGLE_STATE",
|
|
459
|
+
"UPDATE_FORM",
|
|
460
|
+
"NAVIGATE",
|
|
461
|
+
"OPEN_DIALOG",
|
|
462
|
+
"CLOSE_DIALOG",
|
|
463
|
+
"UPDATE_CONTEXT"
|
|
464
|
+
]);
|
|
465
|
+
var openAISimplifiedValue = zod.z.string().nullable();
|
|
466
|
+
var openAIRecordSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
467
|
+
var openAIEventPayloadSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
|
|
468
|
+
var openAIBaseNode = zod.z.object({
|
|
469
|
+
id: zod.z.string().describe("Unique identifier for the UI node."),
|
|
470
|
+
node_type: componentType.describe(
|
|
471
|
+
"The type of UI component (e.g., Container, Text, Button, ListView)."
|
|
472
|
+
),
|
|
473
|
+
props: openAIRecordSimplifiedNullable.describe(
|
|
474
|
+
'Component-specific properties (attributes). Values should be strings or null. E.g., for Header use { "title": "My Title" }; for Text use { "text": "My Text" }; for Button use { "label": "My Button Label" }.'
|
|
475
|
+
),
|
|
476
|
+
bindings: openAIRecordSimplifiedNullable.describe(
|
|
477
|
+
'Data bindings map context paths to component props. Values are paths (e.g., "user.name") or templates (e.g., "{{item.title}}")...'
|
|
478
|
+
),
|
|
479
|
+
events: zod.z.record(
|
|
480
|
+
zod.z.string(),
|
|
481
|
+
zod.z.object({
|
|
482
|
+
action: actionTypeEnum.describe(
|
|
483
|
+
'Action identifier (e.g., "UPDATE_DATA", "ADD_ITEM", "DELETE_ITEM", "VIEW_DETAIL", "HIDE_DETAIL"). Defines what operation to perform when the event occurs.'
|
|
484
|
+
),
|
|
485
|
+
target: zod.z.string().describe("Target identifier."),
|
|
486
|
+
payload: openAIEventPayloadSimplifiedNullable.describe(
|
|
487
|
+
"Static payload to merge with the event's runtime payload."
|
|
488
|
+
)
|
|
489
|
+
})
|
|
490
|
+
).nullable().describe(
|
|
491
|
+
'Defines event handlers mapped from UIEventType (e.g., "CLICK", "CHANGE") to an action configuration.'
|
|
492
|
+
),
|
|
493
|
+
children: zod.z.null()
|
|
494
|
+
});
|
|
495
|
+
var openAINodeL4 = openAIBaseNode;
|
|
496
|
+
var openAINodeL3 = openAIBaseNode.extend({
|
|
497
|
+
children: zod.z.array(openAINodeL4).nullable()
|
|
498
|
+
});
|
|
499
|
+
var openAINodeL2 = openAIBaseNode.extend({
|
|
500
|
+
children: zod.z.array(openAINodeL3).nullable()
|
|
501
|
+
});
|
|
502
|
+
var openAIUISpec = openAIBaseNode.extend({
|
|
503
|
+
children: zod.z.array(openAINodeL2).nullable()
|
|
501
504
|
});
|
|
502
505
|
|
|
503
506
|
// src/core/planner.ts
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
Schema: ${JSON.stringify(tableSchema)}`;
|
|
509
|
-
}).join("\n\n");
|
|
510
|
-
const recentEvents = history?.slice(-5).map(
|
|
511
|
-
(event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
|
|
512
|
-
).join("\n") || "No recent events";
|
|
513
|
-
const userContextSection = userContext ? `
|
|
514
|
-
|
|
515
|
-
User Context:
|
|
516
|
-
${JSON.stringify(userContext)}` : "";
|
|
517
|
-
return `
|
|
518
|
-
You are an expert UI generator.
|
|
519
|
-
Create a user interface that achieves the following goal: "${goal}"
|
|
520
|
-
|
|
521
|
-
Available data schema:
|
|
522
|
-
${schemaInfo}
|
|
523
|
-
|
|
524
|
-
Recent user interactions:
|
|
525
|
-
${recentEvents}${userContextSection}
|
|
526
|
-
|
|
527
|
-
Generate a complete UI specification in JSON format that matches the following TypeScript type:
|
|
528
|
-
type UISpecNode = {
|
|
529
|
-
id: string;
|
|
530
|
-
type: string;
|
|
531
|
-
props?: Record<string, any>;
|
|
532
|
-
bindings?: Record<string, any>;
|
|
533
|
-
events?: Record<string, { action: string; target?: string; payload?: Record<string, any>; }>;
|
|
534
|
-
children?: UISpecNode[];
|
|
507
|
+
var getAnthropicClient = (apiKey) => {
|
|
508
|
+
return anthropic.createAnthropic({
|
|
509
|
+
apiKey
|
|
510
|
+
});
|
|
535
511
|
};
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
6. Provide event handlers for user interactions
|
|
544
|
-
|
|
545
|
-
Respond ONLY with the JSON UI specification and no other text.
|
|
546
|
-
`;
|
|
547
|
-
}
|
|
548
|
-
function mockPlanner(input, targetNodeId, customPrompt) {
|
|
549
|
-
const mockNode = {
|
|
550
|
-
id: targetNodeId || "root",
|
|
551
|
-
type: "Container",
|
|
552
|
-
props: { title: "Mock UI" },
|
|
512
|
+
function mockPlanner(_input) {
|
|
513
|
+
return {
|
|
514
|
+
id: "task-dashboard",
|
|
515
|
+
node_type: "Container",
|
|
516
|
+
props: { className: "p-4 space-y-4" },
|
|
517
|
+
bindings: null,
|
|
518
|
+
events: null,
|
|
553
519
|
children: [
|
|
554
520
|
{
|
|
555
|
-
id: "
|
|
556
|
-
|
|
557
|
-
props: {
|
|
521
|
+
id: "header",
|
|
522
|
+
node_type: "Container",
|
|
523
|
+
props: { className: "flex justify-between items-center mb-4" },
|
|
524
|
+
bindings: null,
|
|
525
|
+
events: null,
|
|
526
|
+
children: [
|
|
527
|
+
{
|
|
528
|
+
id: "title",
|
|
529
|
+
node_type: "Text",
|
|
530
|
+
props: { text: "Task Dashboard", className: "text-2xl font-bold" },
|
|
531
|
+
bindings: null,
|
|
532
|
+
events: null,
|
|
533
|
+
children: null
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
id: "add-task-button",
|
|
537
|
+
node_type: "Button",
|
|
538
|
+
props: { label: "Add Task", variant: "default" },
|
|
539
|
+
bindings: null,
|
|
540
|
+
events: {
|
|
541
|
+
CLICK: { action: "SHOW_DETAIL", target: "new-task-form" }
|
|
542
|
+
},
|
|
543
|
+
children: null
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
id: "main-content",
|
|
549
|
+
node_type: "Container",
|
|
550
|
+
props: { className: "flex gap-4" },
|
|
551
|
+
bindings: null,
|
|
552
|
+
events: null,
|
|
553
|
+
children: [
|
|
554
|
+
{
|
|
555
|
+
id: "tasks-container",
|
|
556
|
+
node_type: "Container",
|
|
557
|
+
props: { className: "flex-1" },
|
|
558
|
+
bindings: null,
|
|
559
|
+
events: null,
|
|
560
|
+
children: [
|
|
561
|
+
{
|
|
562
|
+
id: "task-list",
|
|
563
|
+
node_type: "ListView",
|
|
564
|
+
props: { className: "space-y-2" },
|
|
565
|
+
bindings: { data: "tasks.data" },
|
|
566
|
+
events: null,
|
|
567
|
+
children: [
|
|
568
|
+
{
|
|
569
|
+
id: "task-item-{{index}}",
|
|
570
|
+
node_type: "Card",
|
|
571
|
+
props: { className: "p-3 border rounded" },
|
|
572
|
+
bindings: null,
|
|
573
|
+
events: null,
|
|
574
|
+
children: [
|
|
575
|
+
{
|
|
576
|
+
id: "task-title-{{index}}",
|
|
577
|
+
node_type: "Text",
|
|
578
|
+
props: { className: "font-medium" },
|
|
579
|
+
bindings: { text: "item.title" },
|
|
580
|
+
events: null,
|
|
581
|
+
children: null
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
id: "task-status-{{index}}",
|
|
585
|
+
node_type: "Badge",
|
|
586
|
+
props: {},
|
|
587
|
+
bindings: { text: "item.status" },
|
|
588
|
+
events: null,
|
|
589
|
+
children: null
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
id: "view-details-button-{{index}}",
|
|
593
|
+
node_type: "Button",
|
|
594
|
+
props: { label: "View Details", variant: "outline", size: "sm" },
|
|
595
|
+
bindings: null,
|
|
596
|
+
events: {
|
|
597
|
+
CLICK: { action: "SHOW_DETAIL", target: "task-detail" }
|
|
598
|
+
},
|
|
599
|
+
children: null
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
}
|
|
603
|
+
]
|
|
604
|
+
}
|
|
605
|
+
]
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
id: "task-detail",
|
|
609
|
+
node_type: "Container",
|
|
610
|
+
props: { className: "w-1/3 border-l pl-4", visible: false },
|
|
611
|
+
bindings: null,
|
|
612
|
+
events: null,
|
|
613
|
+
children: [
|
|
614
|
+
{
|
|
615
|
+
id: "detail-title",
|
|
616
|
+
node_type: "Text",
|
|
617
|
+
props: { text: "Task Details", className: "text-lg font-bold mb-2" },
|
|
618
|
+
bindings: null,
|
|
619
|
+
events: null,
|
|
620
|
+
children: null
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
id: "detail-content",
|
|
624
|
+
node_type: "Text",
|
|
625
|
+
props: {},
|
|
626
|
+
bindings: { text: "tasks.selected.description" },
|
|
627
|
+
events: null,
|
|
628
|
+
children: null
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
id: "close-detail-button",
|
|
632
|
+
node_type: "Button",
|
|
633
|
+
props: { label: "Close", variant: "ghost" },
|
|
634
|
+
bindings: null,
|
|
635
|
+
events: {
|
|
636
|
+
CLICK: { action: "HIDE_DETAIL", target: "task-detail" }
|
|
637
|
+
},
|
|
638
|
+
children: null
|
|
639
|
+
}
|
|
640
|
+
]
|
|
641
|
+
}
|
|
642
|
+
]
|
|
558
643
|
}
|
|
559
644
|
]
|
|
560
645
|
};
|
|
561
|
-
return mockNode;
|
|
562
646
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
647
|
+
async function callPlannerLLM(input, apiKey, useMock) {
|
|
648
|
+
await systemEvents.emit(
|
|
649
|
+
createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
|
|
650
|
+
);
|
|
651
|
+
if (useMock || typeof window !== "undefined" && window.__USE_MOCK_PLANNER) {
|
|
652
|
+
console.log("[Mock Planner] Using mock planner for testing");
|
|
653
|
+
const mockLayout = mockPlanner();
|
|
654
|
+
await systemEvents.emit(
|
|
655
|
+
createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
|
|
656
|
+
layout: mockLayout,
|
|
657
|
+
executionTimeMs: 0
|
|
658
|
+
})
|
|
659
|
+
);
|
|
660
|
+
return mockLayout;
|
|
572
661
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
);
|
|
597
|
-
if (route) {
|
|
598
|
-
console.log("Resolved route:", route);
|
|
599
|
-
systemEvents.emit(
|
|
600
|
-
createSystemEvent("PLAN_START" /* PLAN_START */, {
|
|
601
|
-
plannerInput: route.plannerInput
|
|
602
|
-
})
|
|
603
|
-
);
|
|
604
|
-
if (mockMode) {
|
|
605
|
-
const node = mockPlanner(route.plannerInput, route.targetNodeId, route.prompt);
|
|
606
|
-
switch (route.actionType) {
|
|
607
|
-
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
608
|
-
dispatch({ type: "AI_RESPONSE", node });
|
|
609
|
-
break;
|
|
610
|
-
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
611
|
-
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
612
|
-
case "HIDE_DETAIL" /* HIDE_DETAIL */:
|
|
613
|
-
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
614
|
-
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
615
|
-
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
616
|
-
case "NAVIGATE" /* NAVIGATE */:
|
|
617
|
-
dispatch({
|
|
618
|
-
type: "PARTIAL_UPDATE",
|
|
619
|
-
nodeId: route.targetNodeId,
|
|
620
|
-
node
|
|
621
|
-
});
|
|
622
|
-
break;
|
|
623
|
-
}
|
|
624
|
-
} else {
|
|
625
|
-
const prompt = route.prompt;
|
|
626
|
-
systemEvents.emit(
|
|
627
|
-
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
628
|
-
);
|
|
629
|
-
append({
|
|
630
|
-
content: prompt,
|
|
631
|
-
role: "user"
|
|
632
|
-
});
|
|
633
|
-
sessionStorage.setItem("currentRoute", JSON.stringify({
|
|
634
|
-
actionType: route.actionType,
|
|
635
|
-
targetNodeId: route.targetNodeId
|
|
636
|
-
}));
|
|
662
|
+
if (!apiKey) {
|
|
663
|
+
console.warn(
|
|
664
|
+
`Anthropic API key was not provided to callPlannerLLM. Returning a placeholder UI.`
|
|
665
|
+
);
|
|
666
|
+
return {
|
|
667
|
+
id: "root-no-api-key",
|
|
668
|
+
node_type: "Container",
|
|
669
|
+
props: {
|
|
670
|
+
className: "p-4 flex flex-col items-center justify-center h-full"
|
|
671
|
+
},
|
|
672
|
+
bindings: null,
|
|
673
|
+
events: null,
|
|
674
|
+
children: [
|
|
675
|
+
{
|
|
676
|
+
id: "no-api-key-message",
|
|
677
|
+
node_type: "Text",
|
|
678
|
+
props: {
|
|
679
|
+
text: "Anthropic API Key is required to generate the UI. Please provide one in your environment configuration.",
|
|
680
|
+
className: "text-red-500 text-center"
|
|
681
|
+
},
|
|
682
|
+
bindings: null,
|
|
683
|
+
events: null,
|
|
684
|
+
children: null
|
|
637
685
|
}
|
|
638
|
-
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
const input = {
|
|
642
|
-
schema,
|
|
643
|
-
goal,
|
|
644
|
-
history: [...state.history, event],
|
|
645
|
-
userContext
|
|
686
|
+
]
|
|
646
687
|
};
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
688
|
+
}
|
|
689
|
+
const startTime = Date.now();
|
|
690
|
+
const templateValuesForPrompt = input.userContext ? { ...input.userContext } : void 0;
|
|
691
|
+
const promptTemplateFromInput = typeof input.userContext?.promptTemplate === "string" ? input.userContext.promptTemplate : void 0;
|
|
692
|
+
const prompt = buildPrompt(
|
|
693
|
+
input,
|
|
694
|
+
promptTemplateFromInput,
|
|
695
|
+
// Use template from input.userContext
|
|
696
|
+
templateValuesForPrompt
|
|
697
|
+
// Use values from input.userContext
|
|
698
|
+
);
|
|
699
|
+
await systemEvents.emit(
|
|
700
|
+
createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
|
|
701
|
+
);
|
|
702
|
+
try {
|
|
703
|
+
let uiSpec;
|
|
704
|
+
if (typeof window !== "undefined") {
|
|
705
|
+
const response = await fetch("/api/generate-ui", {
|
|
706
|
+
method: "POST",
|
|
707
|
+
headers: {
|
|
708
|
+
"Content-Type": "application/json"
|
|
709
|
+
},
|
|
710
|
+
body: JSON.stringify({ prompt, apiKey })
|
|
711
|
+
});
|
|
712
|
+
if (!response.ok) {
|
|
713
|
+
const errorData = await response.json();
|
|
714
|
+
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
|
|
715
|
+
}
|
|
716
|
+
const data = await response.json();
|
|
717
|
+
uiSpec = data.uiSpec;
|
|
650
718
|
} else {
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
719
|
+
const { object } = await ai.generateObject({
|
|
720
|
+
model: getAnthropicClient(apiKey)("claude-haiku-4-5"),
|
|
721
|
+
schema: openAIUISpec,
|
|
722
|
+
mode: "tool",
|
|
723
|
+
messages: [{ role: "user", content: prompt }],
|
|
724
|
+
temperature: 0.2,
|
|
725
|
+
maxTokens: 4e3
|
|
655
726
|
});
|
|
727
|
+
uiSpec = object;
|
|
728
|
+
}
|
|
729
|
+
await systemEvents.emit(
|
|
730
|
+
createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
|
|
731
|
+
layout: uiSpec,
|
|
732
|
+
executionTimeMs: Date.now() - startTime
|
|
733
|
+
})
|
|
734
|
+
);
|
|
735
|
+
return uiSpec;
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error("Error calling LLM planner:", error);
|
|
738
|
+
await systemEvents.emit(
|
|
739
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
740
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
741
|
+
})
|
|
742
|
+
);
|
|
743
|
+
throw error;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// src/core/bindings.ts
|
|
748
|
+
function getValueByPath(context, path) {
|
|
749
|
+
const parts = path.split(".");
|
|
750
|
+
let current = context;
|
|
751
|
+
for (const part of parts) {
|
|
752
|
+
if (current === null || current === void 0) {
|
|
753
|
+
return void 0;
|
|
754
|
+
}
|
|
755
|
+
if (typeof current !== "object") {
|
|
756
|
+
return void 0;
|
|
757
|
+
}
|
|
758
|
+
current = current[part];
|
|
759
|
+
}
|
|
760
|
+
return current;
|
|
761
|
+
}
|
|
762
|
+
function setValueByPath(context, path, value) {
|
|
763
|
+
if (!path) {
|
|
764
|
+
return context;
|
|
765
|
+
}
|
|
766
|
+
const result = { ...context };
|
|
767
|
+
const parts = path.split(".");
|
|
768
|
+
let current = result;
|
|
769
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
770
|
+
const part = parts[i];
|
|
771
|
+
if (typeof current !== "object" || current === null) {
|
|
772
|
+
console.warn(
|
|
773
|
+
`setValueByPath: Cannot traverse path "${path}". Parent segment "${parts[i - 1] || "(root)"}" is not an object.`
|
|
774
|
+
);
|
|
775
|
+
return context;
|
|
776
|
+
}
|
|
777
|
+
const currentAsObject = current;
|
|
778
|
+
const nextPartValue = currentAsObject[part];
|
|
779
|
+
if (nextPartValue === void 0 || nextPartValue === null) {
|
|
780
|
+
currentAsObject[part] = {};
|
|
781
|
+
} else if (typeof nextPartValue !== "object") {
|
|
782
|
+
console.warn(
|
|
783
|
+
`setValueByPath: Cannot create nested path "${path}". Segment "${part}" is not an object.`
|
|
784
|
+
);
|
|
785
|
+
return context;
|
|
786
|
+
} else {
|
|
787
|
+
currentAsObject[part] = { ...nextPartValue };
|
|
788
|
+
}
|
|
789
|
+
current = currentAsObject[part];
|
|
790
|
+
}
|
|
791
|
+
const lastPart = parts[parts.length - 1];
|
|
792
|
+
if (typeof current === "object" && current !== null) {
|
|
793
|
+
current[lastPart] = value;
|
|
794
|
+
} else {
|
|
795
|
+
console.warn(
|
|
796
|
+
`setValueByPath: Could not set value for path "${path}". Final segment parent is not an object.`
|
|
797
|
+
);
|
|
798
|
+
return context;
|
|
799
|
+
}
|
|
800
|
+
return result;
|
|
801
|
+
}
|
|
802
|
+
function processBinding(binding, context, itemData) {
|
|
803
|
+
if (typeof binding === "string") {
|
|
804
|
+
const exactMatchArr = binding.match(/^{{(.*)}}$/);
|
|
805
|
+
const pathInsideExact = exactMatchArr ? exactMatchArr[1].trim() : null;
|
|
806
|
+
if (pathInsideExact !== null && !pathInsideExact.includes("{{") && !pathInsideExact.includes("}}")) {
|
|
807
|
+
const pathToResolve = pathInsideExact;
|
|
808
|
+
let resolvedValue = void 0;
|
|
809
|
+
if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
|
|
810
|
+
if (pathToResolve.startsWith("item.")) {
|
|
811
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
|
|
812
|
+
} else {
|
|
813
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
|
|
814
|
+
}
|
|
815
|
+
} else if (itemData && pathToResolve in itemData) {
|
|
816
|
+
resolvedValue = getValueByPath(itemData, pathToResolve);
|
|
817
|
+
}
|
|
818
|
+
if (resolvedValue === void 0) {
|
|
819
|
+
resolvedValue = getValueByPath(context, pathToResolve);
|
|
820
|
+
}
|
|
821
|
+
return resolvedValue;
|
|
822
|
+
} else if (binding.includes("{{") && binding.includes("}}")) {
|
|
823
|
+
const resolvedString = binding.replaceAll(
|
|
824
|
+
/{{(.*?)}}/g,
|
|
825
|
+
// Non-greedy match inside braces
|
|
826
|
+
(match, path) => {
|
|
827
|
+
const trimmedPath = path.trim();
|
|
828
|
+
let resolvedValue = void 0;
|
|
829
|
+
if ((trimmedPath.startsWith("item.") || trimmedPath.startsWith("row.")) && itemData) {
|
|
830
|
+
if (trimmedPath.startsWith("item.")) {
|
|
831
|
+
resolvedValue = getValueByPath(
|
|
832
|
+
itemData,
|
|
833
|
+
trimmedPath.substring(5)
|
|
834
|
+
);
|
|
835
|
+
} else {
|
|
836
|
+
resolvedValue = getValueByPath(
|
|
837
|
+
itemData,
|
|
838
|
+
trimmedPath.substring(4)
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
} else if (itemData && trimmedPath in itemData) {
|
|
842
|
+
resolvedValue = getValueByPath(itemData, trimmedPath);
|
|
843
|
+
}
|
|
844
|
+
if (resolvedValue === void 0) {
|
|
845
|
+
resolvedValue = getValueByPath(context, trimmedPath);
|
|
846
|
+
}
|
|
847
|
+
return resolvedValue === null || resolvedValue === void 0 ? "" : String(resolvedValue);
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
return resolvedString;
|
|
851
|
+
} else {
|
|
852
|
+
const pathToResolve = binding;
|
|
853
|
+
let resolvedValue = void 0;
|
|
854
|
+
if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
|
|
855
|
+
if (pathToResolve.startsWith("item.")) {
|
|
856
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
|
|
857
|
+
} else {
|
|
858
|
+
resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
|
|
859
|
+
}
|
|
860
|
+
if (resolvedValue !== void 0) {
|
|
861
|
+
return resolvedValue;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
if (itemData && !pathToResolve.includes(".") && pathToResolve in itemData) {
|
|
865
|
+
resolvedValue = getValueByPath(itemData, pathToResolve);
|
|
866
|
+
}
|
|
867
|
+
if (resolvedValue === void 0) {
|
|
868
|
+
resolvedValue = getValueByPath(context, pathToResolve);
|
|
869
|
+
}
|
|
870
|
+
return resolvedValue;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (Array.isArray(binding)) {
|
|
874
|
+
return binding.map((item) => processBinding(item, context, itemData));
|
|
875
|
+
}
|
|
876
|
+
if (binding !== null && typeof binding === "object") {
|
|
877
|
+
const result = {};
|
|
878
|
+
for (const [key, value] of Object.entries(binding)) {
|
|
879
|
+
result[key] = processBinding(value, context, itemData);
|
|
880
|
+
}
|
|
881
|
+
return result;
|
|
882
|
+
}
|
|
883
|
+
return binding;
|
|
884
|
+
}
|
|
885
|
+
async function resolveBindings(node, context, itemData) {
|
|
886
|
+
if (node.id === "task-detail") {
|
|
887
|
+
console.log(
|
|
888
|
+
`[resolveBindings task-detail SPECIFIC CHECK] Context for task-detail:`,
|
|
889
|
+
JSON.stringify(context)
|
|
890
|
+
);
|
|
891
|
+
console.log(
|
|
892
|
+
`[resolveBindings task-detail SPECIFIC CHECK] context.isTaskDetailDialogVisible = ${context.isTaskDetailDialogVisible}`
|
|
893
|
+
);
|
|
894
|
+
console.log(
|
|
895
|
+
`[resolveBindings task-detail SPECIFIC CHECK] context.selectedTask = ${JSON.stringify(
|
|
896
|
+
context.selectedTask
|
|
897
|
+
)}`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
console.log(
|
|
901
|
+
`[resolveBindings ENTRY] Node ID: ${node.id}, Type: ${node.node_type}, Has itemData: ${!!itemData}, Context keys: ${Object.keys(
|
|
902
|
+
context || {}
|
|
903
|
+
// Ensure context is not null before Object.keys
|
|
904
|
+
).join(", ")}`
|
|
905
|
+
);
|
|
906
|
+
const result = {
|
|
907
|
+
...node,
|
|
908
|
+
props: node.props ? JSON.parse(JSON.stringify(node.props)) : null,
|
|
909
|
+
events: node.events ? JSON.parse(JSON.stringify(node.events)) : null,
|
|
910
|
+
bindings: node.bindings ? JSON.parse(JSON.stringify(node.bindings)) : null,
|
|
911
|
+
children: null
|
|
912
|
+
// Initialize children to null
|
|
913
|
+
};
|
|
914
|
+
let mergedProps = node.props ? { ...JSON.parse(JSON.stringify(node.props)) } : null;
|
|
915
|
+
const PROP_KEYS_TO_RESOLVE = /* @__PURE__ */ new Set([
|
|
916
|
+
"text",
|
|
917
|
+
"label",
|
|
918
|
+
"title",
|
|
919
|
+
"placeholder",
|
|
920
|
+
"value"
|
|
921
|
+
]);
|
|
922
|
+
if (node.props) {
|
|
923
|
+
for (const [key, value] of Object.entries(node.props)) {
|
|
924
|
+
if (!mergedProps)
|
|
925
|
+
mergedProps = {};
|
|
926
|
+
if (PROP_KEYS_TO_RESOLVE.has(key)) {
|
|
927
|
+
const resolvedPropValue = processBinding(
|
|
928
|
+
value,
|
|
929
|
+
context,
|
|
930
|
+
itemData ?? void 0
|
|
931
|
+
);
|
|
932
|
+
if (resolvedPropValue !== void 0) {
|
|
933
|
+
mergedProps[key] = resolvedPropValue;
|
|
934
|
+
} else {
|
|
935
|
+
if (typeof value === "string" && (value.includes("{{") || value.includes("."))) {
|
|
936
|
+
mergedProps[key] = "";
|
|
937
|
+
} else {
|
|
938
|
+
mergedProps[key] = value;
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
} else if (value !== void 0) {
|
|
942
|
+
mergedProps[key] = value;
|
|
943
|
+
} else {
|
|
944
|
+
delete mergedProps[key];
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
result.props = mergedProps;
|
|
949
|
+
if (node.bindings) {
|
|
950
|
+
for (const [key, bindingValue] of Object.entries(node.bindings)) {
|
|
951
|
+
const resolvedValue = processBinding(
|
|
952
|
+
bindingValue,
|
|
953
|
+
context,
|
|
954
|
+
itemData ?? void 0
|
|
955
|
+
);
|
|
956
|
+
if (node.id === "task-detail") {
|
|
957
|
+
console.log(
|
|
958
|
+
`[resolveBindings - ${node.id}] Binding for '${key}': '${String(
|
|
959
|
+
bindingValue
|
|
960
|
+
)}' -> Resolved:`,
|
|
961
|
+
resolvedValue
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
if (resolvedValue !== void 0) {
|
|
965
|
+
if (!result.props)
|
|
966
|
+
result.props = {};
|
|
967
|
+
result.props[key] = resolvedValue;
|
|
968
|
+
if (node.id === "task-detail") {
|
|
969
|
+
console.log(
|
|
970
|
+
`[resolveBindings - ${node.id}] Set result.props.${key} =`,
|
|
971
|
+
resolvedValue
|
|
972
|
+
);
|
|
973
|
+
}
|
|
974
|
+
} else {
|
|
975
|
+
if (node.id === "task-detail") {
|
|
976
|
+
console.log(
|
|
977
|
+
`[resolveBindings - ${node.id}] Binding for '${key}' ('${String(
|
|
978
|
+
bindingValue
|
|
979
|
+
)}') resolved to undefined. Not setting prop.`
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
if (node.events) {
|
|
986
|
+
const processedEvents = {};
|
|
987
|
+
for (const eventType in node.events) {
|
|
988
|
+
const eventConfig = node.events[eventType];
|
|
989
|
+
processedEvents[eventType] = {
|
|
990
|
+
...eventConfig,
|
|
991
|
+
payload: eventConfig.payload ? processBinding(
|
|
992
|
+
eventConfig.payload,
|
|
993
|
+
context,
|
|
994
|
+
itemData ?? void 0
|
|
995
|
+
) : null
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
result.events = processedEvents;
|
|
999
|
+
} else {
|
|
1000
|
+
result.events = null;
|
|
1001
|
+
}
|
|
1002
|
+
const dataBindingValue = result.props?.data ?? result.props?.items;
|
|
1003
|
+
if ((node.node_type === "ListView" || node.node_type === "Table") && Array.isArray(dataBindingValue) && node.children && node.children.length > 0) {
|
|
1004
|
+
const templateChild = node.children[0];
|
|
1005
|
+
const looksLikeTemplate = node.children.length === 1 && (templateChild.id.includes("{{") || templateChild.id.includes("-template"));
|
|
1006
|
+
const isAlreadyExpanded = !looksLikeTemplate && node.children.length > 1;
|
|
1007
|
+
if (isAlreadyExpanded) {
|
|
1008
|
+
result.children = await Promise.all(
|
|
1009
|
+
node.children.map(
|
|
1010
|
+
(existingChild) => resolveBindings(existingChild, context, itemData)
|
|
1011
|
+
)
|
|
1012
|
+
// itemData is tricky here, should it be from parent or is it irrelevant?
|
|
1013
|
+
// For now, assume itemData is not applicable for re-resolving top-level list items this way.
|
|
1014
|
+
);
|
|
1015
|
+
} else {
|
|
1016
|
+
const mappedChildren = await Promise.all(
|
|
1017
|
+
dataBindingValue.map(async (currentItemData, index) => {
|
|
1018
|
+
try {
|
|
1019
|
+
if (typeof currentItemData !== "object" || currentItemData === null) {
|
|
1020
|
+
console.warn(
|
|
1021
|
+
`List item at index ${index} for node ${node.id} is not an object:`,
|
|
1022
|
+
currentItemData
|
|
1023
|
+
);
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
const currentItemAsRecord = currentItemData;
|
|
1027
|
+
const itemId = currentItemAsRecord.id;
|
|
1028
|
+
const instanceId = `${templateChild.id}-${itemId || index}`;
|
|
1029
|
+
const childNodeInstance = JSON.parse(
|
|
1030
|
+
JSON.stringify(templateChild)
|
|
1031
|
+
);
|
|
1032
|
+
childNodeInstance.id = instanceId;
|
|
1033
|
+
makeChildIdsUniqueInInstance(
|
|
1034
|
+
childNodeInstance,
|
|
1035
|
+
instanceId,
|
|
1036
|
+
templateChild.id
|
|
1037
|
+
);
|
|
1038
|
+
const resolvedChild = await resolveBindings(
|
|
1039
|
+
childNodeInstance,
|
|
1040
|
+
context,
|
|
1041
|
+
currentItemAsRecord
|
|
1042
|
+
);
|
|
1043
|
+
if (resolvedChild && !resolvedChild.props)
|
|
1044
|
+
resolvedChild.props = {};
|
|
1045
|
+
if (resolvedChild && resolvedChild.props) {
|
|
1046
|
+
resolvedChild.props.key = itemId || `${node.id}-item-${index}`;
|
|
1047
|
+
}
|
|
1048
|
+
return resolvedChild;
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
console.error(
|
|
1051
|
+
`[resolveBindings Error] Error processing item at index ${index} for node ${node.id}:`,
|
|
1052
|
+
error,
|
|
1053
|
+
"Item Data:",
|
|
1054
|
+
currentItemData
|
|
1055
|
+
);
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
})
|
|
1059
|
+
);
|
|
1060
|
+
result.children = mappedChildren.filter(
|
|
1061
|
+
(child) => child !== null
|
|
1062
|
+
);
|
|
1063
|
+
}
|
|
1064
|
+
} else if (node.children && node.children.length > 0) {
|
|
1065
|
+
result.children = await Promise.all(
|
|
1066
|
+
node.children.map((child) => resolveBindings(child, context, itemData))
|
|
1067
|
+
);
|
|
1068
|
+
} else {
|
|
1069
|
+
result.children = [];
|
|
1070
|
+
}
|
|
1071
|
+
return result;
|
|
1072
|
+
}
|
|
1073
|
+
function executeAction(action, target, payload, context = {}) {
|
|
1074
|
+
let newContext = { ...context };
|
|
1075
|
+
switch (action) {
|
|
1076
|
+
case "SHOW_DETAIL" /* SHOW_DETAIL */: {
|
|
1077
|
+
const taskId = payload?.taskId;
|
|
1078
|
+
const dialogNodeId = target;
|
|
1079
|
+
if (taskId && dialogNodeId) {
|
|
1080
|
+
const tasksData = getValueByPath(context, "tasks.data");
|
|
1081
|
+
if (Array.isArray(tasksData)) {
|
|
1082
|
+
const foundTask = tasksData.find(
|
|
1083
|
+
(t) => t.id === taskId
|
|
1084
|
+
);
|
|
1085
|
+
if (foundTask) {
|
|
1086
|
+
newContext = setValueByPath(newContext, "selectedTask", foundTask);
|
|
1087
|
+
} else {
|
|
1088
|
+
console.warn(
|
|
1089
|
+
`[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: Task with id "${taskId}" not found in tasks.data.`
|
|
1090
|
+
);
|
|
1091
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1092
|
+
}
|
|
1093
|
+
} else {
|
|
1094
|
+
console.warn(
|
|
1095
|
+
`[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: context.tasks.data is not an array or not found.`
|
|
1096
|
+
);
|
|
1097
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1098
|
+
}
|
|
1099
|
+
} else {
|
|
1100
|
+
console.warn(
|
|
1101
|
+
`[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: payload.taskId or target (dialogNodeId) was missing. Dialog will be shown without a selected task.`
|
|
1102
|
+
);
|
|
1103
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1104
|
+
}
|
|
1105
|
+
newContext = setValueByPath(
|
|
1106
|
+
newContext,
|
|
1107
|
+
// Use the potentially modified newContext (with selectedTask set or cleared)
|
|
1108
|
+
"isTaskDetailDialogVisible",
|
|
1109
|
+
true
|
|
1110
|
+
);
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
case "HIDE_DIALOG" /* HIDE_DIALOG */: {
|
|
1114
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1115
|
+
newContext = setValueByPath(
|
|
1116
|
+
newContext,
|
|
1117
|
+
"isTaskDetailDialogVisible",
|
|
1118
|
+
false
|
|
1119
|
+
);
|
|
1120
|
+
break;
|
|
1121
|
+
}
|
|
1122
|
+
case "HIDE_DETAIL" /* HIDE_DETAIL */: {
|
|
1123
|
+
newContext = setValueByPath(newContext, "selectedItemForDetail", null);
|
|
1124
|
+
newContext = setValueByPath(newContext, "isDetailViewOpen", false);
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
case "OPEN_DIALOG" /* OPEN_DIALOG */: {
|
|
1128
|
+
const dialogId = target || payload?.dialogId;
|
|
1129
|
+
if (dialogId === "taskDetailDialogNodeId" || !dialogId) {
|
|
1130
|
+
newContext = setValueByPath(
|
|
1131
|
+
newContext,
|
|
1132
|
+
"isTaskDetailDialogVisible",
|
|
1133
|
+
true
|
|
1134
|
+
);
|
|
1135
|
+
} else {
|
|
1136
|
+
console.warn(
|
|
1137
|
+
`[executeAction] ${"OPEN_DIALOG" /* OPEN_DIALOG */}: Unhandled dialogId: ${dialogId}.`
|
|
1138
|
+
);
|
|
1139
|
+
}
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
case "CLOSE_DIALOG" /* CLOSE_DIALOG */: {
|
|
1143
|
+
const dialogId = target || payload?.dialogId;
|
|
1144
|
+
if (dialogId === "taskDetailDialogNodeId" || !dialogId) {
|
|
1145
|
+
newContext = setValueByPath(
|
|
1146
|
+
newContext,
|
|
1147
|
+
"isTaskDetailDialogVisible",
|
|
1148
|
+
false
|
|
1149
|
+
);
|
|
1150
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1151
|
+
} else {
|
|
1152
|
+
console.warn(
|
|
1153
|
+
`[executeAction] ${"CLOSE_DIALOG" /* CLOSE_DIALOG */}: Unhandled dialogId: ${dialogId}.`
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
case "UPDATE_DATA" /* UPDATE_DATA */: {
|
|
1159
|
+
let updatedContext = context;
|
|
1160
|
+
if (target && payload && "value" in payload) {
|
|
1161
|
+
updatedContext = setValueByPath(context, target, payload.value);
|
|
1162
|
+
} else {
|
|
1163
|
+
console.warn(
|
|
1164
|
+
`[executeAction] ${"UPDATE_DATA" /* UPDATE_DATA */} requires targetPath (data path) and payload with 'value' property.`
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
newContext = updatedContext;
|
|
1168
|
+
break;
|
|
1169
|
+
}
|
|
1170
|
+
case "ADD_ITEM" /* ADD_ITEM */: {
|
|
1171
|
+
if (!target) {
|
|
1172
|
+
console.warn(`[executeAction] ADD_ITEM requires target path.`);
|
|
1173
|
+
break;
|
|
1174
|
+
}
|
|
1175
|
+
if (!payload?.item) {
|
|
1176
|
+
console.warn(
|
|
1177
|
+
`[executeAction] ADD_ITEM requires payload with item property.`
|
|
1178
|
+
);
|
|
1179
|
+
break;
|
|
1180
|
+
}
|
|
1181
|
+
const list = getValueByPath(newContext, target);
|
|
1182
|
+
if (!Array.isArray(list)) {
|
|
1183
|
+
console.warn(
|
|
1184
|
+
`[executeAction] ADD_ITEM failed: target path "${target}" does not resolve to an array.`
|
|
1185
|
+
);
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
const newItem = payload.item;
|
|
1189
|
+
const position = payload.position;
|
|
1190
|
+
let newList;
|
|
1191
|
+
if (position === "start") {
|
|
1192
|
+
newList = [newItem, ...list];
|
|
1193
|
+
} else {
|
|
1194
|
+
newList = [...list, newItem];
|
|
1195
|
+
}
|
|
1196
|
+
newContext = setValueByPath(newContext, target, newList);
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
case "DELETE_ITEM" /* DELETE_ITEM */: {
|
|
1200
|
+
if (!target) {
|
|
1201
|
+
console.warn(`[executeAction] DELETE_ITEM requires target path.`);
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
const itemId = payload?.id;
|
|
1205
|
+
if (itemId === void 0 || itemId === null) {
|
|
1206
|
+
console.warn(
|
|
1207
|
+
`[executeAction] DELETE_ITEM requires payload with id property.`
|
|
1208
|
+
);
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
const list = getValueByPath(newContext, target);
|
|
1212
|
+
if (!Array.isArray(list)) {
|
|
1213
|
+
console.warn(
|
|
1214
|
+
`[executeAction] DELETE_ITEM failed: target path "${target}" does not resolve to an array.`
|
|
1215
|
+
);
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
const newList = list.filter(
|
|
1219
|
+
(item) => item?.id !== itemId
|
|
1220
|
+
);
|
|
1221
|
+
if (newList.length !== list.length) {
|
|
1222
|
+
newContext = setValueByPath(newContext, target, newList);
|
|
1223
|
+
} else {
|
|
1224
|
+
console.warn(
|
|
1225
|
+
`[executeAction] DELETE_ITEM: Item with id "${itemId}" not found in list at path "${target}".`
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
break;
|
|
1229
|
+
}
|
|
1230
|
+
case "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */: {
|
|
1231
|
+
if (!target) {
|
|
1232
|
+
console.warn(
|
|
1233
|
+
"[executeAction] SAVE_TASK_CHANGES requires target (task ID)."
|
|
1234
|
+
);
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
const taskIdToSave = target;
|
|
1238
|
+
const currentTasks = getValueByPath(newContext, "tasks.data");
|
|
1239
|
+
const selectedTaskData = getValueByPath(newContext, "selectedTask");
|
|
1240
|
+
if (currentTasks && selectedTaskData && selectedTaskData.id === taskIdToSave) {
|
|
1241
|
+
const updatedTasks = currentTasks.map(
|
|
1242
|
+
(task) => task.id === taskIdToSave ? { ...task, ...selectedTaskData, ...payload } : task
|
|
1243
|
+
// Merge selectedTaskData and any direct payload changes
|
|
1244
|
+
);
|
|
1245
|
+
newContext = setValueByPath(newContext, "tasks.data", updatedTasks);
|
|
1246
|
+
newContext = setValueByPath(newContext, "selectedTask", null);
|
|
1247
|
+
newContext = setValueByPath(
|
|
1248
|
+
newContext,
|
|
1249
|
+
"isTaskDetailDialogVisible",
|
|
1250
|
+
false
|
|
1251
|
+
);
|
|
1252
|
+
} else {
|
|
1253
|
+
console.warn(
|
|
1254
|
+
"[executeAction] SAVE_TASK_CHANGES: Could not save. Task list, selected task, or ID mismatch.",
|
|
1255
|
+
{
|
|
1256
|
+
taskIdToSave,
|
|
1257
|
+
selectedTaskDataId: selectedTaskData?.id,
|
|
1258
|
+
currentTasksExists: !!currentTasks
|
|
1259
|
+
}
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
default:
|
|
1265
|
+
console.warn(`[executeAction] Unhandled action type: ${action}`);
|
|
1266
|
+
}
|
|
1267
|
+
return newContext;
|
|
1268
|
+
}
|
|
1269
|
+
function makeChildIdsUniqueInInstance(parentNode, baseInstanceId, originalTemplateRootId) {
|
|
1270
|
+
if (parentNode.children) {
|
|
1271
|
+
parentNode.children = parentNode.children.map((child) => {
|
|
1272
|
+
let originalChildId = child.id;
|
|
1273
|
+
if (child.id.startsWith(originalTemplateRootId + "-")) {
|
|
1274
|
+
const parts = child.id.split("-");
|
|
1275
|
+
if (parts.length > 1) {
|
|
1276
|
+
originalChildId = parts[parts.length - 1];
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
const newChildId = `${baseInstanceId}-${originalChildId}`;
|
|
1280
|
+
const newChild = {
|
|
1281
|
+
...JSON.parse(JSON.stringify(child)),
|
|
1282
|
+
// Deep clone child
|
|
1283
|
+
id: newChildId
|
|
1284
|
+
};
|
|
1285
|
+
makeChildIdsUniqueInInstance(
|
|
1286
|
+
newChild,
|
|
1287
|
+
baseInstanceId,
|
|
1288
|
+
originalTemplateRootId
|
|
1289
|
+
);
|
|
1290
|
+
return newChild;
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/core/action-router.ts
|
|
1296
|
+
var UI_GUIDANCE_BASE = `
|
|
1297
|
+
UI Guidance:
|
|
1298
|
+
1. Create a focused interface that directly addresses the goal
|
|
1299
|
+
2. Use appropriate UI patterns (lists, forms, details, etc.)
|
|
1300
|
+
3. Include navigation between related views when needed
|
|
1301
|
+
4. Keep the interface simple and intuitive
|
|
1302
|
+
5. Bind to schema data where appropriate
|
|
1303
|
+
6. **CRITICAL for Buttons:** All \`Button\` nodes **MUST** include a \`label\` property in their \`props\` (e.g., \`{ "props": { "label": "Click Me" } }\`).
|
|
1304
|
+
7. Provide event handlers for user interactions - make sure to always include both action and target properties`;
|
|
1305
|
+
var LIST_BINDING_GUIDANCE = `8. **CRITICAL:** For \`ListView\` or \`Table\` nodes, the \`data\` binding key **MUST** point to the *exact path* of the data *array* within the context.`;
|
|
1306
|
+
var LIST_BINDING_EXAMPLE = `Example: If the context has \`{ tasks: { data: [...] } }\`, the binding **MUST** be \`{ "bindings": { "data": "tasks.data" } }\`. If the context has \`{ userList: [...] }\`, the binding **MUST** be \`{ "bindings": { "data": "userList" } }\`. **NEVER** bind to the parent object containing the array (e.g., DO NOT USE \`{ "bindings": { "data": "tasks" } }\`).`;
|
|
1307
|
+
var COMMON_UI_GUIDANCE = UI_GUIDANCE_BASE + "\n" + // Add a newline separator
|
|
1308
|
+
LIST_BINDING_GUIDANCE + // Add the specific list binding rule
|
|
1309
|
+
"\n" + // Add a newline separator
|
|
1310
|
+
LIST_BINDING_EXAMPLE;
|
|
1311
|
+
function processTemplate(template, values) {
|
|
1312
|
+
return template.replace(/\${(.*?)}/g, (match, key) => {
|
|
1313
|
+
const trimmedKey = key.trim();
|
|
1314
|
+
return trimmedKey in values ? String(values[trimmedKey]) : match;
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
function buildPrompt(input, promptTemplate, templateValues) {
|
|
1318
|
+
const { schema, goal, history, userContext } = input;
|
|
1319
|
+
const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
|
|
1320
|
+
const schemaString = typeof tableSchema === "object" && tableSchema !== null ? JSON.stringify(tableSchema) : String(tableSchema);
|
|
1321
|
+
return `Table: ${tableName}
|
|
1322
|
+
Schema: ${schemaString}`;
|
|
1323
|
+
}).join("\n\n");
|
|
1324
|
+
const recentEvents = history && history.length > 0 ? history.slice(-5).map(
|
|
1325
|
+
(event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
|
|
1326
|
+
).join("\n") : "No recent events";
|
|
1327
|
+
const userContextSection = userContext ? `
|
|
1328
|
+
|
|
1329
|
+
User Context:
|
|
1330
|
+
${JSON.stringify(userContext)}` : "";
|
|
1331
|
+
if (promptTemplate && templateValues) {
|
|
1332
|
+
const fullTemplateValues = {
|
|
1333
|
+
...templateValues,
|
|
1334
|
+
schemaInfo,
|
|
1335
|
+
recentEvents,
|
|
1336
|
+
userContextString: userContextSection.trim(),
|
|
1337
|
+
// Use trimmed version
|
|
1338
|
+
commonUIGuidance: COMMON_UI_GUIDANCE,
|
|
1339
|
+
goal
|
|
1340
|
+
// Ensure goal is always available to templates
|
|
1341
|
+
};
|
|
1342
|
+
return processTemplate(promptTemplate, fullTemplateValues);
|
|
1343
|
+
}
|
|
1344
|
+
const interactionDescription = history && history.length > 0 ? `The user's last action was: ${history[history.length - 1].type} on node ${history[history.length - 1].nodeId}` : "The user initiated the session for the goal";
|
|
1345
|
+
return `
|
|
1346
|
+
You are an expert UI generator.
|
|
1347
|
+
Create a user interface that achieves the following goal: "${goal}".
|
|
1348
|
+
${interactionDescription}.
|
|
1349
|
+
|
|
1350
|
+
Available data schema:
|
|
1351
|
+
${schemaInfo}
|
|
1352
|
+
|
|
1353
|
+
Recent user interactions:
|
|
1354
|
+
${recentEvents}${userContextSection}
|
|
1355
|
+
|
|
1356
|
+
Generate a complete UI specification in JSON format that matches the following TypeScript type:
|
|
1357
|
+
type UISpecNode = { id: string; node_type: string; props?: Record<string, unknown>; bindings?: Record<string, unknown>; events?: Record<string, { action: string; target: string; payload?: Record<string, unknown>; }>; children?: UISpecNode[]; };
|
|
1358
|
+
${COMMON_UI_GUIDANCE}
|
|
1359
|
+
|
|
1360
|
+
Respond ONLY with the JSON UI specification and no other text.
|
|
1361
|
+
`;
|
|
1362
|
+
}
|
|
1363
|
+
var ActionRouter = class {
|
|
1364
|
+
constructor(apiKey, planningConfig) {
|
|
1365
|
+
this.apiKey = apiKey;
|
|
1366
|
+
this.planningConfig = planningConfig || {
|
|
1367
|
+
prefetchDepth: 1,
|
|
1368
|
+
temperature: 0.5,
|
|
1369
|
+
streaming: false
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Find the appropriate route for an event
|
|
1374
|
+
* @param event - UI event
|
|
1375
|
+
* @param layout - Current UI layout
|
|
1376
|
+
* @param dataContext - Current data context
|
|
1377
|
+
* @returns Route resolution or null if no match
|
|
1378
|
+
*/
|
|
1379
|
+
async resolveRoute(event, schema, layout, dataContext, goal, apiKey, userContext) {
|
|
1380
|
+
console.log(
|
|
1381
|
+
`[ActionRouter Debug] resolveRoute called for event type: ${event.type}, nodeId: ${event.nodeId}`
|
|
1382
|
+
);
|
|
1383
|
+
const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
|
|
1384
|
+
const nodeConfig = sourceNode?.events?.[event.type];
|
|
1385
|
+
let actionType;
|
|
1386
|
+
let determinedTargetNodeId;
|
|
1387
|
+
let determinedNodeConfigPayload = null;
|
|
1388
|
+
if (nodeConfig?.action) {
|
|
1389
|
+
actionType = nodeConfig.action;
|
|
1390
|
+
determinedTargetNodeId = nodeConfig.target || sourceNode?.id || "root";
|
|
1391
|
+
determinedNodeConfigPayload = nodeConfig.payload ? { ...nodeConfig.payload } : null;
|
|
1392
|
+
} else {
|
|
1393
|
+
switch (event.type) {
|
|
1394
|
+
case "INIT":
|
|
1395
|
+
actionType = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1396
|
+
determinedTargetNodeId = "root";
|
|
1397
|
+
break;
|
|
1398
|
+
case "CLICK":
|
|
1399
|
+
actionType = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1400
|
+
determinedTargetNodeId = "root";
|
|
1401
|
+
break;
|
|
1402
|
+
case "CHANGE":
|
|
1403
|
+
if (sourceNode && ["Input", "Select", "Textarea", "Checkbox", "RadioGroup"].includes(sourceNode.node_type)) {
|
|
1404
|
+
actionType = "UPDATE_DATA" /* UPDATE_DATA */;
|
|
1405
|
+
determinedTargetNodeId = sourceNode.id;
|
|
1406
|
+
} else {
|
|
1407
|
+
actionType = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1408
|
+
determinedTargetNodeId = "root";
|
|
1409
|
+
}
|
|
1410
|
+
break;
|
|
1411
|
+
default:
|
|
1412
|
+
actionType = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1413
|
+
determinedTargetNodeId = "root";
|
|
1414
|
+
break;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
if (!Object.values(ActionType).includes(actionType)) {
|
|
1418
|
+
throw new Error(`Invalid action type: ${actionType}`);
|
|
1419
|
+
}
|
|
1420
|
+
const additionalContext = {};
|
|
1421
|
+
if (sourceNode) {
|
|
1422
|
+
additionalContext.sourceNode = sourceNode;
|
|
1423
|
+
}
|
|
1424
|
+
const targetNode = layout ? findNodeById(layout, determinedTargetNodeId) : void 0;
|
|
1425
|
+
if (targetNode) {
|
|
1426
|
+
additionalContext.targetNode = targetNode;
|
|
1427
|
+
}
|
|
1428
|
+
let finalEventPayload = event.payload && Object.keys(event.payload).length > 0 ? { ...event.payload } : null;
|
|
1429
|
+
if (determinedNodeConfigPayload) {
|
|
1430
|
+
finalEventPayload = { ...finalEventPayload || {}, ...determinedNodeConfigPayload };
|
|
1431
|
+
}
|
|
1432
|
+
if (finalEventPayload) {
|
|
1433
|
+
additionalContext.eventPayload = finalEventPayload;
|
|
1434
|
+
}
|
|
1435
|
+
const plannerInput2 = {
|
|
1436
|
+
schema,
|
|
1437
|
+
goal,
|
|
1438
|
+
history: [event],
|
|
1439
|
+
userContext: { ...userContext || {}, ...additionalContext }
|
|
1440
|
+
};
|
|
1441
|
+
if (actionType === "SHOW_DETAIL" /* SHOW_DETAIL */) {
|
|
1442
|
+
const itemData = dataContext.tasks?.data?.find(
|
|
1443
|
+
(task) => task.id === (event.payload?.itemId || event.payload?.taskId)
|
|
1444
|
+
);
|
|
1445
|
+
const updatedDataContext = {
|
|
1446
|
+
...dataContext,
|
|
1447
|
+
selectedTask: itemData,
|
|
1448
|
+
isTaskDetailDialogVisible: true
|
|
1449
|
+
};
|
|
1450
|
+
const layoutFromLLM = await callPlannerLLM(plannerInput2, apiKey);
|
|
1451
|
+
return {
|
|
1452
|
+
actionType: "UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
|
|
1453
|
+
targetNodeId: determinedTargetNodeId,
|
|
1454
|
+
updatedDataContext,
|
|
1455
|
+
updatedNode: layoutFromLLM
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
if (actionType === "HIDE_DIALOG" /* HIDE_DIALOG */) {
|
|
1459
|
+
if (!layout) {
|
|
1460
|
+
throw new Error("Layout cannot be null when handling HIDE_DIALOG");
|
|
1461
|
+
}
|
|
1462
|
+
return {
|
|
1463
|
+
actionType: "UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
|
|
1464
|
+
targetNodeId: determinedTargetNodeId,
|
|
1465
|
+
updatedDataContext: {
|
|
1466
|
+
...dataContext,
|
|
1467
|
+
selectedTask: null,
|
|
1468
|
+
isTaskDetailDialogVisible: false
|
|
1469
|
+
},
|
|
1470
|
+
updatedNode: layout
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
if (["UPDATE_DATA" /* UPDATE_DATA */, "ADD_ITEM" /* ADD_ITEM */, "DELETE_ITEM" /* DELETE_ITEM */, "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */].includes(actionType)) {
|
|
1474
|
+
if (!layout) {
|
|
1475
|
+
throw new Error("Layout cannot be null when handling data update actions");
|
|
1476
|
+
}
|
|
1477
|
+
const targetPathOrId = actionType === "UPDATE_DATA" /* UPDATE_DATA */ ? sourceNode?.bindings?.value || determinedTargetNodeId : determinedTargetNodeId;
|
|
1478
|
+
return {
|
|
1479
|
+
actionType,
|
|
1480
|
+
targetNodeId: determinedTargetNodeId,
|
|
1481
|
+
updatedNode: layout,
|
|
1482
|
+
updatedDataContext: executeAction(actionType, targetPathOrId, finalEventPayload || {}, dataContext)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
if ([
|
|
1486
|
+
"FULL_REFRESH" /* FULL_REFRESH */,
|
|
1487
|
+
"UPDATE_NODE" /* UPDATE_NODE */,
|
|
1488
|
+
"ADD_DROPDOWN" /* ADD_DROPDOWN */,
|
|
1489
|
+
"TOGGLE_STATE" /* TOGGLE_STATE */,
|
|
1490
|
+
"UPDATE_FORM" /* UPDATE_FORM */,
|
|
1491
|
+
"NAVIGATE" /* NAVIGATE */,
|
|
1492
|
+
"UPDATE_CONTEXT" /* UPDATE_CONTEXT */
|
|
1493
|
+
].includes(actionType)) {
|
|
1494
|
+
const layoutFromLLM = await callPlannerLLM(plannerInput2, apiKey);
|
|
1495
|
+
return {
|
|
1496
|
+
actionType,
|
|
1497
|
+
targetNodeId: determinedTargetNodeId,
|
|
1498
|
+
updatedNode: layoutFromLLM,
|
|
1499
|
+
updatedDataContext: dataContext
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
throw new Error(`Unhandled action type: ${actionType}`);
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
function useUIStateEngine({
|
|
1506
|
+
schema,
|
|
1507
|
+
goal,
|
|
1508
|
+
apiKey = "",
|
|
1509
|
+
userContext,
|
|
1510
|
+
planningConfig,
|
|
1511
|
+
dataContext = {},
|
|
1512
|
+
enablePartialUpdates = true
|
|
1513
|
+
}) {
|
|
1514
|
+
if (userContext === null) {
|
|
1515
|
+
console.warn(
|
|
1516
|
+
"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."
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
const [state, dispatch] = React.useReducer(uiReducer, initialState);
|
|
1520
|
+
const stateRef = React.useRef(state);
|
|
1521
|
+
const router = new ActionRouter(apiKey, planningConfig);
|
|
1522
|
+
React.useEffect(() => {
|
|
1523
|
+
stateRef.current = state;
|
|
1524
|
+
}, [state]);
|
|
1525
|
+
const handleEvent = React.useCallback(
|
|
1526
|
+
async (event, currentResolvedLayout, updatedDataContext) => {
|
|
1527
|
+
dispatch({ type: "UI_EVENT", event });
|
|
1528
|
+
dispatch({ type: "LOADING", isLoading: true });
|
|
1529
|
+
try {
|
|
1530
|
+
let resolvedNode;
|
|
1531
|
+
let actionTypeForDispatch = "FULL_REFRESH" /* FULL_REFRESH */;
|
|
1532
|
+
let targetNodeIdForDispatch = "root";
|
|
1533
|
+
const layoutForRouting = currentResolvedLayout || stateRef.current.layout;
|
|
1534
|
+
const contextForRouting = updatedDataContext || dataContext;
|
|
1535
|
+
console.log(
|
|
1536
|
+
"[state.ts handleEvent] About to call router.resolveRoute. enablePartialUpdates:",
|
|
1537
|
+
enablePartialUpdates
|
|
1538
|
+
);
|
|
1539
|
+
console.log(
|
|
1540
|
+
"[state.ts handleEvent] layoutForRouting ID (if exists):",
|
|
1541
|
+
layoutForRouting?.id
|
|
1542
|
+
);
|
|
1543
|
+
console.log(
|
|
1544
|
+
"[state.ts handleEvent] contextForRouting:",
|
|
1545
|
+
JSON.stringify(contextForRouting, null, 2)
|
|
1546
|
+
);
|
|
1547
|
+
const route = await router.resolveRoute(
|
|
1548
|
+
event,
|
|
1549
|
+
schema,
|
|
1550
|
+
layoutForRouting,
|
|
1551
|
+
contextForRouting,
|
|
1552
|
+
goal,
|
|
1553
|
+
apiKey,
|
|
1554
|
+
userContext
|
|
1555
|
+
);
|
|
1556
|
+
console.log(
|
|
1557
|
+
"[state.ts handleEvent] router.resolveRoute returned:",
|
|
1558
|
+
route
|
|
1559
|
+
);
|
|
1560
|
+
if (route) {
|
|
1561
|
+
console.log("Resolved route:", route);
|
|
1562
|
+
actionTypeForDispatch = route.actionType;
|
|
1563
|
+
targetNodeIdForDispatch = route.targetNodeId;
|
|
1564
|
+
if (route.updatedDataContext) {
|
|
1565
|
+
dispatch({
|
|
1566
|
+
type: "SET_DATA_CONTEXT",
|
|
1567
|
+
payload: route.updatedDataContext
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
if (!route.updatedNode) {
|
|
1571
|
+
throw new Error(
|
|
1572
|
+
"No updatedNode returned from router.resolveRoute. This should not happen."
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
resolvedNode = route.updatedNode;
|
|
1576
|
+
} else {
|
|
1577
|
+
throw new Error("No route returned from router.resolveRoute");
|
|
1578
|
+
}
|
|
1579
|
+
switch (actionTypeForDispatch) {
|
|
1580
|
+
case "UPDATE_NODE" /* UPDATE_NODE */:
|
|
1581
|
+
case "SHOW_DETAIL" /* SHOW_DETAIL */:
|
|
1582
|
+
case "TOGGLE_STATE" /* TOGGLE_STATE */:
|
|
1583
|
+
case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
|
|
1584
|
+
case "UPDATE_FORM" /* UPDATE_FORM */:
|
|
1585
|
+
case "NAVIGATE" /* NAVIGATE */:
|
|
1586
|
+
dispatch({
|
|
1587
|
+
type: "PARTIAL_UPDATE",
|
|
1588
|
+
nodeId: targetNodeIdForDispatch,
|
|
1589
|
+
node: resolvedNode
|
|
1590
|
+
});
|
|
1591
|
+
break;
|
|
1592
|
+
case "HIDE_DIALOG" /* HIDE_DIALOG */:
|
|
1593
|
+
dispatch({
|
|
1594
|
+
type: "PARTIAL_UPDATE",
|
|
1595
|
+
nodeId: targetNodeIdForDispatch,
|
|
1596
|
+
node: resolvedNode
|
|
1597
|
+
});
|
|
1598
|
+
break;
|
|
1599
|
+
case "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */:
|
|
1600
|
+
dispatch({ type: "AI_RESPONSE", node: resolvedNode });
|
|
1601
|
+
break;
|
|
1602
|
+
case "FULL_REFRESH" /* FULL_REFRESH */:
|
|
1603
|
+
default:
|
|
1604
|
+
dispatch({ type: "AI_RESPONSE", node: resolvedNode });
|
|
1605
|
+
break;
|
|
1606
|
+
}
|
|
1607
|
+
} catch (e) {
|
|
1608
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1609
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
1610
|
+
systemEvents.emit(
|
|
1611
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
1612
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
1613
|
+
})
|
|
1614
|
+
);
|
|
1615
|
+
} finally {
|
|
1616
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
1617
|
+
}
|
|
1618
|
+
},
|
|
1619
|
+
[
|
|
1620
|
+
goal,
|
|
1621
|
+
schema,
|
|
1622
|
+
userContext,
|
|
1623
|
+
dataContext,
|
|
1624
|
+
apiKey,
|
|
1625
|
+
enablePartialUpdates,
|
|
1626
|
+
dispatch
|
|
1627
|
+
]
|
|
1628
|
+
);
|
|
1629
|
+
React.useEffect(() => {
|
|
1630
|
+
const initialFetch = async () => {
|
|
1631
|
+
dispatch({ type: "LOADING", isLoading: true });
|
|
1632
|
+
try {
|
|
1633
|
+
const initEvent = {
|
|
1634
|
+
type: "INIT",
|
|
1635
|
+
nodeId: "system",
|
|
1636
|
+
timestamp: Date.now(),
|
|
1637
|
+
payload: null
|
|
1638
|
+
};
|
|
1639
|
+
const route = await router.resolveRoute(
|
|
1640
|
+
initEvent,
|
|
1641
|
+
schema,
|
|
1642
|
+
stateRef.current.layout,
|
|
1643
|
+
dataContext,
|
|
1644
|
+
goal,
|
|
1645
|
+
apiKey,
|
|
1646
|
+
userContext
|
|
1647
|
+
);
|
|
1648
|
+
if (!route) {
|
|
1649
|
+
console.error(
|
|
1650
|
+
"[UIStateEngine] Initial fetch: Failed to resolve route for INIT event."
|
|
1651
|
+
);
|
|
1652
|
+
throw new Error("Failed to initialize UI due to routing error.");
|
|
1653
|
+
}
|
|
1654
|
+
if (route.updatedDataContext) {
|
|
1655
|
+
dispatch({
|
|
1656
|
+
type: "SET_DATA_CONTEXT",
|
|
1657
|
+
payload: route.updatedDataContext
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
if (!route.updatedNode) {
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
"No updatedNode returned from router.resolveRoute on initial fetch. This should not happen."
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
const node = route.updatedNode;
|
|
1666
|
+
dispatch({ type: "AI_RESPONSE", node });
|
|
1667
|
+
} catch (e) {
|
|
1668
|
+
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
1669
|
+
dispatch({ type: "ERROR", message: errorMessage });
|
|
1670
|
+
systemEvents.emit(
|
|
1671
|
+
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
1672
|
+
error: e instanceof Error ? e : new Error(String(e))
|
|
1673
|
+
})
|
|
1674
|
+
);
|
|
1675
|
+
} finally {
|
|
1676
|
+
dispatch({ type: "LOADING", isLoading: false });
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
initialFetch();
|
|
1680
|
+
}, [goal, schema, dispatch]);
|
|
1681
|
+
return {
|
|
1682
|
+
state,
|
|
1683
|
+
dispatch,
|
|
1684
|
+
handleEvent
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// src/adapters/shadcn.tsx
|
|
1689
|
+
init_utils();
|
|
1690
|
+
|
|
1691
|
+
// components/ui/dialog.tsx
|
|
1692
|
+
init_utils();
|
|
1693
|
+
function Dialog({
|
|
1694
|
+
...props
|
|
1695
|
+
}) {
|
|
1696
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Root, { "data-slot": "dialog", ...props });
|
|
1697
|
+
}
|
|
1698
|
+
function DialogPortal({
|
|
1699
|
+
...props
|
|
1700
|
+
}) {
|
|
1701
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogPrimitive__namespace.Portal, { "data-slot": "dialog-portal", ...props });
|
|
1702
|
+
}
|
|
1703
|
+
function DialogOverlay({
|
|
1704
|
+
className,
|
|
1705
|
+
...props
|
|
1706
|
+
}) {
|
|
1707
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1708
|
+
DialogPrimitive__namespace.Overlay,
|
|
1709
|
+
{
|
|
1710
|
+
"data-slot": "dialog-overlay",
|
|
1711
|
+
className: cn(
|
|
1712
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
1713
|
+
className
|
|
1714
|
+
),
|
|
1715
|
+
...props
|
|
1716
|
+
}
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
function DialogContent({
|
|
1720
|
+
className,
|
|
1721
|
+
children,
|
|
1722
|
+
...props
|
|
1723
|
+
}) {
|
|
1724
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1727
|
+
DialogPrimitive__namespace.Content,
|
|
1728
|
+
{
|
|
1729
|
+
"data-slot": "dialog-content",
|
|
1730
|
+
className: cn(
|
|
1731
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
1732
|
+
className
|
|
1733
|
+
),
|
|
1734
|
+
...props,
|
|
1735
|
+
children: [
|
|
1736
|
+
children,
|
|
1737
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogPrimitive__namespace.Close, { className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", children: [
|
|
1738
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {}),
|
|
1739
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Close" })
|
|
1740
|
+
] })
|
|
1741
|
+
]
|
|
1742
|
+
}
|
|
1743
|
+
)
|
|
1744
|
+
] });
|
|
1745
|
+
}
|
|
1746
|
+
function DialogHeader({ className, ...props }) {
|
|
1747
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1748
|
+
"div",
|
|
1749
|
+
{
|
|
1750
|
+
"data-slot": "dialog-header",
|
|
1751
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
1752
|
+
...props
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
function DialogTitle({
|
|
1757
|
+
className,
|
|
1758
|
+
...props
|
|
1759
|
+
}) {
|
|
1760
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1761
|
+
DialogPrimitive__namespace.Title,
|
|
1762
|
+
{
|
|
1763
|
+
"data-slot": "dialog-title",
|
|
1764
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
1765
|
+
...props
|
|
1766
|
+
}
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
function DialogDescription({
|
|
1770
|
+
className,
|
|
1771
|
+
...props
|
|
1772
|
+
}) {
|
|
1773
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1774
|
+
DialogPrimitive__namespace.Description,
|
|
1775
|
+
{
|
|
1776
|
+
"data-slot": "dialog-description",
|
|
1777
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
1778
|
+
...props
|
|
656
1779
|
}
|
|
657
|
-
|
|
658
|
-
|
|
1780
|
+
);
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// components/ui/card.tsx
|
|
1784
|
+
init_utils();
|
|
1785
|
+
function Card({ className, ...props }) {
|
|
1786
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1787
|
+
"div",
|
|
659
1788
|
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
1789
|
+
"data-slot": "card",
|
|
1790
|
+
className: cn(
|
|
1791
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
1792
|
+
className
|
|
1793
|
+
),
|
|
1794
|
+
...props
|
|
1795
|
+
}
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
function CardContent({ className, ...props }) {
|
|
1799
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1800
|
+
"div",
|
|
1801
|
+
{
|
|
1802
|
+
"data-slot": "card-content",
|
|
1803
|
+
className: cn("px-6", className),
|
|
1804
|
+
...props
|
|
1805
|
+
}
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
// components/ui/input.tsx
|
|
1810
|
+
init_utils();
|
|
1811
|
+
function Input({ className, type, ...props }) {
|
|
1812
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1813
|
+
"input",
|
|
1814
|
+
{
|
|
1815
|
+
type,
|
|
1816
|
+
"data-slot": "input",
|
|
1817
|
+
className: cn(
|
|
1818
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1819
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
1820
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
1821
|
+
className
|
|
1822
|
+
),
|
|
1823
|
+
...props
|
|
1824
|
+
}
|
|
1825
|
+
);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// components/ui/textarea.tsx
|
|
1829
|
+
init_utils();
|
|
1830
|
+
function Textarea({ className, ...props }) {
|
|
1831
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1832
|
+
"textarea",
|
|
1833
|
+
{
|
|
1834
|
+
"data-slot": "textarea",
|
|
1835
|
+
className: cn(
|
|
1836
|
+
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
1837
|
+
className
|
|
1838
|
+
),
|
|
1839
|
+
...props
|
|
1840
|
+
}
|
|
1841
|
+
);
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
// components/ui/select.tsx
|
|
1845
|
+
init_utils();
|
|
1846
|
+
function Select({
|
|
1847
|
+
...props
|
|
1848
|
+
}) {
|
|
1849
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Root, { "data-slot": "select", ...props });
|
|
1850
|
+
}
|
|
1851
|
+
function SelectValue({
|
|
1852
|
+
...props
|
|
1853
|
+
}) {
|
|
1854
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Value, { "data-slot": "select-value", ...props });
|
|
1855
|
+
}
|
|
1856
|
+
function SelectTrigger({
|
|
1857
|
+
className,
|
|
1858
|
+
size = "default",
|
|
1859
|
+
children,
|
|
1860
|
+
...props
|
|
1861
|
+
}) {
|
|
1862
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1863
|
+
SelectPrimitive__namespace.Trigger,
|
|
1864
|
+
{
|
|
1865
|
+
"data-slot": "select-trigger",
|
|
1866
|
+
"data-size": size,
|
|
1867
|
+
className: cn(
|
|
1868
|
+
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1869
|
+
className
|
|
1870
|
+
),
|
|
1871
|
+
...props,
|
|
1872
|
+
children: [
|
|
1873
|
+
children,
|
|
1874
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4 opacity-50" }) })
|
|
1875
|
+
]
|
|
1876
|
+
}
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
function SelectContent({
|
|
1880
|
+
className,
|
|
1881
|
+
children,
|
|
1882
|
+
position = "popper",
|
|
1883
|
+
...props
|
|
1884
|
+
}) {
|
|
1885
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1886
|
+
SelectPrimitive__namespace.Content,
|
|
1887
|
+
{
|
|
1888
|
+
"data-slot": "select-content",
|
|
1889
|
+
className: cn(
|
|
1890
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
1891
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
1892
|
+
className
|
|
1893
|
+
),
|
|
1894
|
+
position,
|
|
1895
|
+
...props,
|
|
1896
|
+
children: [
|
|
1897
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
1898
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1899
|
+
SelectPrimitive__namespace.Viewport,
|
|
1900
|
+
{
|
|
1901
|
+
className: cn(
|
|
1902
|
+
"p-1",
|
|
1903
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
|
1904
|
+
),
|
|
1905
|
+
children
|
|
699
1906
|
}
|
|
700
|
-
|
|
701
|
-
|
|
1907
|
+
),
|
|
1908
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
1909
|
+
]
|
|
1910
|
+
}
|
|
1911
|
+
) });
|
|
1912
|
+
}
|
|
1913
|
+
function SelectItem({
|
|
1914
|
+
className,
|
|
1915
|
+
children,
|
|
1916
|
+
...props
|
|
1917
|
+
}) {
|
|
1918
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1919
|
+
SelectPrimitive__namespace.Item,
|
|
1920
|
+
{
|
|
1921
|
+
"data-slot": "select-item",
|
|
1922
|
+
className: cn(
|
|
1923
|
+
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
1924
|
+
className
|
|
1925
|
+
),
|
|
1926
|
+
...props,
|
|
1927
|
+
children: [
|
|
1928
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-4" }) }) }),
|
|
1929
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
1930
|
+
]
|
|
1931
|
+
}
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
function SelectScrollUpButton({
|
|
1935
|
+
className,
|
|
1936
|
+
...props
|
|
1937
|
+
}) {
|
|
1938
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1939
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
1940
|
+
{
|
|
1941
|
+
"data-slot": "select-scroll-up-button",
|
|
1942
|
+
className: cn(
|
|
1943
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1944
|
+
className
|
|
1945
|
+
),
|
|
1946
|
+
...props,
|
|
1947
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUpIcon, { className: "size-4" })
|
|
1948
|
+
}
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
function SelectScrollDownButton({
|
|
1952
|
+
className,
|
|
1953
|
+
...props
|
|
1954
|
+
}) {
|
|
1955
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1956
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
1957
|
+
{
|
|
1958
|
+
"data-slot": "select-scroll-down-button",
|
|
1959
|
+
className: cn(
|
|
1960
|
+
"flex cursor-default items-center justify-center py-1",
|
|
1961
|
+
className
|
|
1962
|
+
),
|
|
1963
|
+
...props,
|
|
1964
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4" })
|
|
1965
|
+
}
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
// components/ui/checkbox.tsx
|
|
1970
|
+
init_utils();
|
|
1971
|
+
function Checkbox({
|
|
1972
|
+
className,
|
|
1973
|
+
...props
|
|
1974
|
+
}) {
|
|
1975
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1976
|
+
CheckboxPrimitive__namespace.Root,
|
|
1977
|
+
{
|
|
1978
|
+
"data-slot": "checkbox",
|
|
1979
|
+
className: cn(
|
|
1980
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
1981
|
+
className
|
|
1982
|
+
),
|
|
1983
|
+
...props,
|
|
1984
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1985
|
+
CheckboxPrimitive__namespace.Indicator,
|
|
1986
|
+
{
|
|
1987
|
+
"data-slot": "checkbox-indicator",
|
|
1988
|
+
className: "flex items-center justify-center text-current transition-none",
|
|
1989
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" })
|
|
702
1990
|
}
|
|
703
|
-
|
|
704
|
-
createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
|
|
705
|
-
layout: validatedNode,
|
|
706
|
-
executionTimeMs: 0
|
|
707
|
-
// Not available here
|
|
708
|
-
})
|
|
709
|
-
);
|
|
710
|
-
} catch (parseError) {
|
|
711
|
-
console.error("Failed to parse LLM response:", parseError);
|
|
712
|
-
dispatch({
|
|
713
|
-
type: "ERROR",
|
|
714
|
-
message: "Failed to parse LLM response"
|
|
715
|
-
});
|
|
716
|
-
systemEvents.emit(
|
|
717
|
-
createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
|
|
718
|
-
error: parseError instanceof Error ? parseError : new Error("Parse error")
|
|
719
|
-
})
|
|
720
|
-
);
|
|
721
|
-
}
|
|
1991
|
+
)
|
|
722
1992
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
role: "user"
|
|
739
|
-
});
|
|
1993
|
+
);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// components/ui/radio-group.tsx
|
|
1997
|
+
init_utils();
|
|
1998
|
+
function RadioGroup({
|
|
1999
|
+
className,
|
|
2000
|
+
...props
|
|
2001
|
+
}) {
|
|
2002
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2003
|
+
RadioGroupPrimitive__namespace.Root,
|
|
2004
|
+
{
|
|
2005
|
+
"data-slot": "radio-group",
|
|
2006
|
+
className: cn("grid gap-3", className),
|
|
2007
|
+
...props
|
|
740
2008
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
function RadioGroupItem({
|
|
2012
|
+
className,
|
|
2013
|
+
...props
|
|
2014
|
+
}) {
|
|
2015
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2016
|
+
RadioGroupPrimitive__namespace.Item,
|
|
2017
|
+
{
|
|
2018
|
+
"data-slot": "radio-group-item",
|
|
2019
|
+
className: cn(
|
|
2020
|
+
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
2021
|
+
className
|
|
2022
|
+
),
|
|
2023
|
+
...props,
|
|
2024
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2025
|
+
RadioGroupPrimitive__namespace.Indicator,
|
|
2026
|
+
{
|
|
2027
|
+
"data-slot": "radio-group-indicator",
|
|
2028
|
+
className: "relative flex items-center justify-center",
|
|
2029
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleIcon, { className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" })
|
|
2030
|
+
}
|
|
2031
|
+
)
|
|
2032
|
+
}
|
|
2033
|
+
);
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// components/ui/tabs.tsx
|
|
2037
|
+
init_utils();
|
|
2038
|
+
function Tabs({
|
|
2039
|
+
className,
|
|
2040
|
+
...props
|
|
2041
|
+
}) {
|
|
2042
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2043
|
+
TabsPrimitive__namespace.Root,
|
|
2044
|
+
{
|
|
2045
|
+
"data-slot": "tabs",
|
|
2046
|
+
className: cn("flex flex-col gap-2", className),
|
|
2047
|
+
...props
|
|
2048
|
+
}
|
|
2049
|
+
);
|
|
2050
|
+
}
|
|
2051
|
+
function TabsList({
|
|
2052
|
+
className,
|
|
2053
|
+
...props
|
|
2054
|
+
}) {
|
|
2055
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2056
|
+
TabsPrimitive__namespace.List,
|
|
2057
|
+
{
|
|
2058
|
+
"data-slot": "tabs-list",
|
|
2059
|
+
className: cn(
|
|
2060
|
+
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
|
2061
|
+
className
|
|
2062
|
+
),
|
|
2063
|
+
...props
|
|
2064
|
+
}
|
|
2065
|
+
);
|
|
2066
|
+
}
|
|
2067
|
+
function TabsTrigger({
|
|
2068
|
+
className,
|
|
2069
|
+
...props
|
|
2070
|
+
}) {
|
|
2071
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2072
|
+
TabsPrimitive__namespace.Trigger,
|
|
2073
|
+
{
|
|
2074
|
+
"data-slot": "tabs-trigger",
|
|
2075
|
+
className: cn(
|
|
2076
|
+
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
2077
|
+
className
|
|
2078
|
+
),
|
|
2079
|
+
...props
|
|
2080
|
+
}
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
function TabsContent({
|
|
2084
|
+
className,
|
|
2085
|
+
...props
|
|
2086
|
+
}) {
|
|
2087
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2088
|
+
TabsPrimitive__namespace.Content,
|
|
2089
|
+
{
|
|
2090
|
+
"data-slot": "tabs-content",
|
|
2091
|
+
className: cn("flex-1 outline-none", className),
|
|
2092
|
+
...props
|
|
2093
|
+
}
|
|
2094
|
+
);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// components/ui/label.tsx
|
|
2098
|
+
init_utils();
|
|
2099
|
+
function Label2({
|
|
2100
|
+
className,
|
|
2101
|
+
...props
|
|
2102
|
+
}) {
|
|
2103
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2104
|
+
LabelPrimitive__namespace.Root,
|
|
2105
|
+
{
|
|
2106
|
+
"data-slot": "label",
|
|
2107
|
+
className: cn(
|
|
2108
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
2109
|
+
className
|
|
2110
|
+
),
|
|
2111
|
+
...props
|
|
2112
|
+
}
|
|
2113
|
+
);
|
|
747
2114
|
}
|
|
2115
|
+
var parseStyleString = (styleString) => {
|
|
2116
|
+
if (typeof styleString !== "string") {
|
|
2117
|
+
return typeof styleString === "object" ? styleString : {};
|
|
2118
|
+
}
|
|
2119
|
+
const style = {};
|
|
2120
|
+
styleString.split(";").forEach((declaration) => {
|
|
2121
|
+
const [property, value] = declaration.split(":");
|
|
2122
|
+
if (property && value) {
|
|
2123
|
+
const camelCasedProperty = property.trim().replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
2124
|
+
style[camelCasedProperty] = value.trim();
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
return style;
|
|
2128
|
+
};
|
|
2129
|
+
var isArrayOf = (guard) => (arr) => Array.isArray(arr) && arr.every(guard);
|
|
748
2130
|
var ShimmerBlock = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-8 bg-gray-200 animate-pulse rounded" });
|
|
749
2131
|
var ShimmerTable = ({ rows = 3 }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full space-y-2", children: [
|
|
750
2132
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-10 bg-gray-200 animate-pulse rounded" }),
|
|
@@ -758,117 +2140,846 @@ var ShimmerCard = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-f
|
|
|
758
2140
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-5/6 h-4 bg-gray-200 animate-pulse rounded" })
|
|
759
2141
|
] })
|
|
760
2142
|
] });
|
|
761
|
-
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `
|
|
762
|
-
var Header = ({
|
|
2143
|
+
var Container = (props) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: `autoui-mock-container ${props.className || ""}`, children: props.children });
|
|
2144
|
+
var Header = ({
|
|
2145
|
+
title,
|
|
2146
|
+
className
|
|
2147
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2148
|
+
"header",
|
|
2149
|
+
{
|
|
2150
|
+
className: `py-4 px-6 border-b border-gray-300 mb-4 bg-gray-50 dark:bg-gray-800 ${className || ""}`,
|
|
2151
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold text-gray-800 dark:text-white", children: title })
|
|
2152
|
+
}
|
|
2153
|
+
);
|
|
763
2154
|
var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
764
2155
|
"button",
|
|
765
2156
|
{
|
|
766
|
-
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"}`,
|
|
767
|
-
onClick,
|
|
2157
|
+
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"}`,
|
|
2158
|
+
onClick: () => onClick?.(),
|
|
768
2159
|
children
|
|
769
2160
|
}
|
|
770
2161
|
);
|
|
771
|
-
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: [
|
|
772
|
-
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: field.label }, field.key)) }) }),
|
|
773
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
774
|
-
"tr",
|
|
775
|
-
{
|
|
776
|
-
onClick: () => selectable && onSelect && onSelect(item),
|
|
777
|
-
className: selectable ? "cursor-pointer hover:bg-gray-50" : "",
|
|
778
|
-
children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-500", children: item[field.key] }, field.key))
|
|
779
|
-
},
|
|
780
|
-
index
|
|
781
|
-
)) })
|
|
782
|
-
] }) });
|
|
783
2162
|
var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
|
|
784
|
-
if (!visible)
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
2163
|
+
if (!visible) {
|
|
2164
|
+
console.log(
|
|
2165
|
+
`[Detail Component Internal] Detail component for node id: ${data?.id || title || "Unknown Detail"} is NOT RENDERING because visible is ${visible}.`
|
|
2166
|
+
);
|
|
2167
|
+
return null;
|
|
2168
|
+
}
|
|
2169
|
+
console.log(
|
|
2170
|
+
`[Detail Component Internal] Detail component for node id: ${data?.id || title || "Unknown Detail"} IS RENDERING because visible is ${visible}.`
|
|
2171
|
+
);
|
|
2172
|
+
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: [
|
|
2173
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-3", children: [
|
|
2174
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-800 dark:text-white", children: title }),
|
|
788
2175
|
onBack && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
|
|
789
2176
|
] }),
|
|
790
2177
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: fields.map((field) => {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
2178
|
+
try {
|
|
2179
|
+
if (field.type === "heading") {
|
|
2180
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2181
|
+
"h3",
|
|
2182
|
+
{
|
|
2183
|
+
className: "text-xl font-semibold text-gray-800 dark:text-white",
|
|
2184
|
+
children: data?.[field.key] ?? ""
|
|
2185
|
+
},
|
|
2186
|
+
field.key
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
if (field.type === "content") {
|
|
2190
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2191
|
+
"div",
|
|
2192
|
+
{
|
|
2193
|
+
className: "text-sm text-gray-700 dark:text-gray-300",
|
|
2194
|
+
children: data?.[field.key] ?? ""
|
|
2195
|
+
},
|
|
2196
|
+
field.key
|
|
2197
|
+
);
|
|
2198
|
+
}
|
|
2199
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2200
|
+
"div",
|
|
2201
|
+
{
|
|
2202
|
+
className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
|
|
2203
|
+
children: [
|
|
2204
|
+
field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
|
|
2205
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
|
|
2206
|
+
]
|
|
2207
|
+
},
|
|
2208
|
+
field.key
|
|
2209
|
+
);
|
|
2210
|
+
} catch (e) {
|
|
2211
|
+
console.error(
|
|
2212
|
+
`[Detail Component Internal] Error rendering field: ${field?.key}`,
|
|
2213
|
+
e,
|
|
2214
|
+
"Field data:",
|
|
2215
|
+
field,
|
|
2216
|
+
"Full data object:",
|
|
2217
|
+
data
|
|
2218
|
+
);
|
|
2219
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2220
|
+
"Error rendering field: ",
|
|
2221
|
+
field?.key
|
|
2222
|
+
] }, field?.key || "error-field");
|
|
796
2223
|
}
|
|
797
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
|
|
798
|
-
field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500", children: field.label }),
|
|
799
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: data?.[field.key] })
|
|
800
|
-
] }, field.key);
|
|
801
2224
|
}) })
|
|
802
2225
|
] });
|
|
803
2226
|
};
|
|
804
|
-
var
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
2227
|
+
var getSafeProp = (props, key, validator, defaultValue) => {
|
|
2228
|
+
if (props && typeof props === "object" && key in props) {
|
|
2229
|
+
const value = props[key];
|
|
2230
|
+
if (validator(value)) {
|
|
2231
|
+
return value;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
return defaultValue;
|
|
2235
|
+
};
|
|
2236
|
+
var isObject = (value) => typeof value === "object" && value !== null;
|
|
2237
|
+
var isString = (value) => typeof value === "string";
|
|
2238
|
+
var isBoolean = (value) => typeof value === "boolean";
|
|
2239
|
+
var isButtonVariant = (value) => isString(value) && ["default", "outline", "destructive"].includes(value);
|
|
2240
|
+
var getSafeBinding = (bindings, key, validator, defaultValue) => {
|
|
2241
|
+
if (bindings && typeof bindings === "object" && key in bindings) {
|
|
2242
|
+
const value = bindings[key];
|
|
2243
|
+
if (validator(value)) {
|
|
2244
|
+
return value;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
return defaultValue;
|
|
2248
|
+
};
|
|
2249
|
+
var isReactNode = (value) => {
|
|
2250
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null || typeof value === "undefined" || typeof value === "object" && value !== null && "$$typeof" in value;
|
|
2251
|
+
};
|
|
2252
|
+
var isRecordWithReactNodeValues = (value) => isObject(value) && Object.values(value).every(isReactNode);
|
|
2253
|
+
var isSelectOptionObject = (item) => isObject(item) && isString(item.value) && isString(item.label);
|
|
2254
|
+
var isTabObject = (item) => (
|
|
2255
|
+
// Allow content to be optional initially
|
|
2256
|
+
isObject(item) && isString(item.value) && isString(item.label) && (item.content === void 0 || isUISpecNode(item.content))
|
|
2257
|
+
);
|
|
2258
|
+
var isUISpecNode = (value) => {
|
|
2259
|
+
if (!isObject(value))
|
|
2260
|
+
return false;
|
|
2261
|
+
return isString(value.id) && isString(value.node_type);
|
|
2262
|
+
};
|
|
2263
|
+
var isDetailFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label) && (item.type === void 0 || isString(item.type));
|
|
2264
|
+
var createEventHandler = (node, eventName, uiEventType2, processEvent) => {
|
|
2265
|
+
const eventConfig = node.events?.[uiEventType2];
|
|
2266
|
+
if (!processEvent || !eventConfig)
|
|
2267
|
+
return void 0;
|
|
2268
|
+
return (eventPayload) => {
|
|
2269
|
+
const fullEvent = {
|
|
2270
|
+
type: uiEventType2,
|
|
2271
|
+
nodeId: node.id,
|
|
2272
|
+
timestamp: Date.now(),
|
|
2273
|
+
payload: {
|
|
2274
|
+
...eventConfig.payload || {},
|
|
2275
|
+
...eventPayload || {}
|
|
2276
|
+
}
|
|
2277
|
+
};
|
|
2278
|
+
processEvent(fullEvent);
|
|
813
2279
|
};
|
|
814
2280
|
};
|
|
815
2281
|
var adapterMap = {
|
|
816
|
-
Container: (node
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
2282
|
+
Container: (node, processEvent) => {
|
|
2283
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2284
|
+
const children = node.children?.map(
|
|
2285
|
+
(child) => (
|
|
2286
|
+
// Use React.cloneElement to add the key prop to the element returned by renderNode
|
|
2287
|
+
React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
|
|
2288
|
+
)
|
|
2289
|
+
);
|
|
2290
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2291
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2292
|
+
"div",
|
|
2293
|
+
{
|
|
2294
|
+
className: cn("autoui-container", className),
|
|
2295
|
+
style,
|
|
2296
|
+
...restProps,
|
|
2297
|
+
"data-id": node.id,
|
|
2298
|
+
children: [
|
|
2299
|
+
(() => {
|
|
2300
|
+
console.log(
|
|
2301
|
+
`[Adapter Debug] Rendering Container: id=${node.id}, props=`,
|
|
2302
|
+
node.props
|
|
2303
|
+
);
|
|
2304
|
+
return null;
|
|
2305
|
+
})(),
|
|
2306
|
+
children
|
|
2307
|
+
]
|
|
2308
|
+
},
|
|
2309
|
+
key
|
|
2310
|
+
);
|
|
2311
|
+
},
|
|
2312
|
+
Header: (node) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2313
|
+
Header,
|
|
820
2314
|
{
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
children: node.props?.label || "Button"
|
|
2315
|
+
title: getSafeProp(node.props, "title", isString, "Untitled"),
|
|
2316
|
+
className: getSafeProp(node.props, "className", isString, "")
|
|
824
2317
|
}
|
|
825
2318
|
),
|
|
826
|
-
|
|
827
|
-
|
|
2319
|
+
Button: (node, processEvent) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2320
|
+
Button,
|
|
828
2321
|
{
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
onSelect: createEventHandler(node, "onSelect")
|
|
2322
|
+
variant: getSafeProp(node.props, "variant", isButtonVariant, "default"),
|
|
2323
|
+
onClick: createEventHandler(node, "onClick", "CLICK", processEvent),
|
|
2324
|
+
children: getSafeProp(node.props, "label", isString, "Button")
|
|
833
2325
|
}
|
|
834
2326
|
),
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
2327
|
+
ListView: (node, processEvent) => {
|
|
2328
|
+
const {
|
|
2329
|
+
className,
|
|
2330
|
+
style: styleProp,
|
|
2331
|
+
key,
|
|
2332
|
+
// Exclude data prop (array) from being spread onto the DOM element
|
|
2333
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2334
|
+
data: _data,
|
|
2335
|
+
...restProps
|
|
2336
|
+
} = node.props || {};
|
|
2337
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2338
|
+
console.log(
|
|
2339
|
+
`[Adapter Debug] Rendering ListView: id=${node.id}, props=`,
|
|
2340
|
+
node.props
|
|
2341
|
+
);
|
|
2342
|
+
const children = node.children?.map(
|
|
2343
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
|
|
2344
|
+
);
|
|
2345
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2346
|
+
"div",
|
|
2347
|
+
{
|
|
2348
|
+
className: cn(
|
|
2349
|
+
"autoui-listview-container space-y-2",
|
|
2350
|
+
className
|
|
2351
|
+
),
|
|
2352
|
+
style,
|
|
2353
|
+
...restProps,
|
|
2354
|
+
"data-id": node.id,
|
|
2355
|
+
children
|
|
2356
|
+
},
|
|
2357
|
+
key
|
|
2358
|
+
);
|
|
2359
|
+
},
|
|
2360
|
+
Detail: (node, processEvent) => {
|
|
2361
|
+
const data = getSafeProp(
|
|
2362
|
+
node.props,
|
|
2363
|
+
"data",
|
|
2364
|
+
isRecordWithReactNodeValues,
|
|
2365
|
+
{}
|
|
2366
|
+
);
|
|
2367
|
+
const fields = getSafeProp(
|
|
2368
|
+
node.props,
|
|
2369
|
+
"fields",
|
|
2370
|
+
isArrayOf(isDetailFieldObject),
|
|
2371
|
+
[]
|
|
2372
|
+
);
|
|
2373
|
+
const title = getSafeProp(node.props, "title", isString, "");
|
|
2374
|
+
const visible = getSafeProp(node.props, "visible", isBoolean, true);
|
|
2375
|
+
console.log(
|
|
2376
|
+
`[Adapter Debug] Rendering Detail: id=${node.id}, props=`,
|
|
2377
|
+
JSON.stringify(node.props),
|
|
2378
|
+
`effective visible=${visible}`,
|
|
2379
|
+
`typeof fields=${typeof fields}`,
|
|
2380
|
+
`Array.isArray(fields)=${Array.isArray(fields)}`
|
|
2381
|
+
);
|
|
2382
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2383
|
+
Detail,
|
|
2384
|
+
{
|
|
2385
|
+
data,
|
|
2386
|
+
fields,
|
|
2387
|
+
title,
|
|
2388
|
+
visible,
|
|
2389
|
+
onBack: createEventHandler(node, "onBack", "CLICK", processEvent)
|
|
2390
|
+
}
|
|
2391
|
+
);
|
|
2392
|
+
},
|
|
2393
|
+
Card: (node, processEvent) => {
|
|
2394
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2395
|
+
const children = node.children?.map(
|
|
2396
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
|
|
2397
|
+
);
|
|
2398
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2399
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2400
|
+
Card,
|
|
2401
|
+
{
|
|
2402
|
+
className: cn("autoui-card", className),
|
|
2403
|
+
style,
|
|
2404
|
+
...restProps,
|
|
2405
|
+
"data-id": node.id,
|
|
2406
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "p-0", children: [
|
|
2407
|
+
" ",
|
|
2408
|
+
children
|
|
2409
|
+
] })
|
|
2410
|
+
},
|
|
2411
|
+
key
|
|
2412
|
+
);
|
|
2413
|
+
},
|
|
2414
|
+
Input: (node, processEvent) => {
|
|
2415
|
+
const name = getSafeProp(node.props, "name", isString, "inputName");
|
|
2416
|
+
const label = getSafeProp(node.props, "label", isString, "");
|
|
2417
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2418
|
+
const placeholder = getSafeProp(node.props, "placeholder", isString, "");
|
|
2419
|
+
const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
|
|
2420
|
+
const className = getSafeProp(node.props, "className", isString, "");
|
|
2421
|
+
const handleChange = (e) => {
|
|
2422
|
+
const handler = createEventHandler(
|
|
2423
|
+
node,
|
|
2424
|
+
"onChange",
|
|
2425
|
+
"CHANGE",
|
|
2426
|
+
processEvent
|
|
2427
|
+
);
|
|
2428
|
+
if (handler)
|
|
2429
|
+
handler({ value: e.target.value });
|
|
2430
|
+
};
|
|
2431
|
+
const handleFocus = () => {
|
|
2432
|
+
const handler = createEventHandler(
|
|
2433
|
+
node,
|
|
2434
|
+
"onFocus",
|
|
2435
|
+
"FOCUS",
|
|
2436
|
+
processEvent
|
|
2437
|
+
);
|
|
2438
|
+
if (handler)
|
|
2439
|
+
handler({});
|
|
2440
|
+
};
|
|
2441
|
+
const handleBlur = () => {
|
|
2442
|
+
const handler = createEventHandler(node, "onBlur", "BLUR", processEvent);
|
|
2443
|
+
if (handler)
|
|
2444
|
+
handler({});
|
|
2445
|
+
};
|
|
2446
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full max-w-sm items-center gap-1.5", children: [
|
|
2447
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
2448
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2449
|
+
Input,
|
|
2450
|
+
{
|
|
2451
|
+
id: name,
|
|
2452
|
+
name,
|
|
2453
|
+
placeholder,
|
|
2454
|
+
disabled,
|
|
2455
|
+
value,
|
|
2456
|
+
onChange: handleChange,
|
|
2457
|
+
onFocus: handleFocus,
|
|
2458
|
+
onBlur: handleBlur,
|
|
2459
|
+
className
|
|
2460
|
+
}
|
|
2461
|
+
)
|
|
2462
|
+
] });
|
|
2463
|
+
},
|
|
2464
|
+
Select: (node, processEvent) => {
|
|
2465
|
+
const name = getSafeProp(node.props, "name", isString, "selectName");
|
|
2466
|
+
const label = getSafeProp(node.props, "label", isString, "");
|
|
2467
|
+
const placeholder = getSafeProp(
|
|
2468
|
+
node.props,
|
|
2469
|
+
"placeholder",
|
|
2470
|
+
isString,
|
|
2471
|
+
"Select..."
|
|
2472
|
+
);
|
|
2473
|
+
const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
|
|
2474
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2475
|
+
const options = getSafeBinding(
|
|
2476
|
+
node.bindings,
|
|
2477
|
+
"options",
|
|
2478
|
+
isArrayOf(isSelectOptionObject),
|
|
2479
|
+
[]
|
|
2480
|
+
);
|
|
2481
|
+
const className = getSafeProp(node.props, "className", isString, "");
|
|
2482
|
+
const handleValueChange = (selectedValue) => {
|
|
2483
|
+
const handler = createEventHandler(
|
|
2484
|
+
node,
|
|
2485
|
+
"onValueChange",
|
|
2486
|
+
"CHANGE",
|
|
2487
|
+
processEvent
|
|
2488
|
+
);
|
|
2489
|
+
if (handler)
|
|
2490
|
+
handler({ value: selectedValue });
|
|
2491
|
+
};
|
|
2492
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2493
|
+
"div",
|
|
2494
|
+
{
|
|
2495
|
+
className: cn("grid w-full max-w-sm items-center gap-1.5", className),
|
|
2496
|
+
children: [
|
|
2497
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
2498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2499
|
+
Select,
|
|
2500
|
+
{
|
|
2501
|
+
name,
|
|
2502
|
+
value,
|
|
2503
|
+
onValueChange: handleValueChange,
|
|
2504
|
+
disabled,
|
|
2505
|
+
children: [
|
|
2506
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: name, children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder }) }),
|
|
2507
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
|
|
2508
|
+
]
|
|
2509
|
+
}
|
|
2510
|
+
)
|
|
2511
|
+
]
|
|
2512
|
+
}
|
|
2513
|
+
);
|
|
2514
|
+
},
|
|
2515
|
+
Textarea: (node, processEvent) => {
|
|
2516
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2517
|
+
const name = getSafeProp(propsWithoutKey, "name", isString, "textareaName");
|
|
2518
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2519
|
+
const placeholder = getSafeProp(
|
|
2520
|
+
propsWithoutKey,
|
|
2521
|
+
"placeholder",
|
|
2522
|
+
isString,
|
|
2523
|
+
""
|
|
2524
|
+
);
|
|
2525
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2526
|
+
const rows = getSafeProp(
|
|
2527
|
+
propsWithoutKey,
|
|
2528
|
+
"rows",
|
|
2529
|
+
(v) => typeof v === "number",
|
|
2530
|
+
3
|
|
2531
|
+
);
|
|
2532
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2533
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2534
|
+
const handleChange = (e) => {
|
|
2535
|
+
const handler = createEventHandler(
|
|
2536
|
+
node,
|
|
2537
|
+
"onChange",
|
|
2538
|
+
"CHANGE",
|
|
2539
|
+
processEvent
|
|
2540
|
+
);
|
|
2541
|
+
if (handler)
|
|
2542
|
+
handler({ value: e.target.value });
|
|
2543
|
+
};
|
|
2544
|
+
const handleFocus = () => {
|
|
2545
|
+
const handler = createEventHandler(
|
|
2546
|
+
node,
|
|
2547
|
+
"onFocus",
|
|
2548
|
+
"FOCUS",
|
|
2549
|
+
processEvent
|
|
2550
|
+
);
|
|
2551
|
+
if (handler)
|
|
2552
|
+
handler({});
|
|
2553
|
+
};
|
|
2554
|
+
const handleBlur = () => {
|
|
2555
|
+
const handler = createEventHandler(node, "onBlur", "BLUR", processEvent);
|
|
2556
|
+
if (handler)
|
|
2557
|
+
handler({});
|
|
2558
|
+
};
|
|
2559
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full gap-1.5", children: [
|
|
2560
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, children: label }),
|
|
2561
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2562
|
+
Textarea,
|
|
2563
|
+
{
|
|
2564
|
+
id: name,
|
|
2565
|
+
name,
|
|
2566
|
+
placeholder,
|
|
2567
|
+
disabled,
|
|
2568
|
+
rows,
|
|
2569
|
+
value,
|
|
2570
|
+
onChange: handleChange,
|
|
2571
|
+
onFocus: handleFocus,
|
|
2572
|
+
onBlur: handleBlur,
|
|
2573
|
+
className
|
|
2574
|
+
}
|
|
2575
|
+
)
|
|
2576
|
+
] }, key);
|
|
2577
|
+
},
|
|
2578
|
+
Checkbox: (node, processEvent) => {
|
|
2579
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2580
|
+
const name = getSafeProp(propsWithoutKey, "name", isString, "checkboxName");
|
|
2581
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2582
|
+
const checked = getSafeBinding(node.bindings, "checked", isBoolean, false);
|
|
2583
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2584
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2585
|
+
const handleCheckedChange = (isChecked) => {
|
|
2586
|
+
if (typeof isChecked === "boolean") {
|
|
2587
|
+
const handler = createEventHandler(
|
|
2588
|
+
node,
|
|
2589
|
+
"onCheckedChange",
|
|
2590
|
+
"CHANGE",
|
|
2591
|
+
processEvent
|
|
2592
|
+
);
|
|
2593
|
+
if (handler)
|
|
2594
|
+
handler({ checked: isChecked });
|
|
2595
|
+
}
|
|
2596
|
+
};
|
|
2597
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2598
|
+
"div",
|
|
2599
|
+
{
|
|
2600
|
+
className: cn("flex items-center space-x-2", className),
|
|
2601
|
+
children: [
|
|
2602
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2603
|
+
Checkbox,
|
|
2604
|
+
{
|
|
2605
|
+
id: name,
|
|
2606
|
+
name,
|
|
2607
|
+
checked,
|
|
2608
|
+
disabled,
|
|
2609
|
+
onCheckedChange: handleCheckedChange
|
|
2610
|
+
}
|
|
2611
|
+
),
|
|
2612
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: name, className: "cursor-pointer", children: label })
|
|
2613
|
+
]
|
|
2614
|
+
},
|
|
2615
|
+
key
|
|
2616
|
+
);
|
|
2617
|
+
},
|
|
2618
|
+
RadioGroup: (node, processEvent) => {
|
|
2619
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2620
|
+
const name = getSafeProp(
|
|
2621
|
+
propsWithoutKey,
|
|
2622
|
+
"name",
|
|
2623
|
+
isString,
|
|
2624
|
+
"radioGroupName"
|
|
2625
|
+
);
|
|
2626
|
+
const label = getSafeProp(propsWithoutKey, "label", isString, "");
|
|
2627
|
+
const value = getSafeBinding(node.bindings, "value", isString, "");
|
|
2628
|
+
const options = getSafeBinding(
|
|
2629
|
+
node.bindings,
|
|
2630
|
+
"options",
|
|
2631
|
+
isArrayOf(isSelectOptionObject),
|
|
2632
|
+
[]
|
|
2633
|
+
);
|
|
2634
|
+
const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
|
|
2635
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2636
|
+
const handleValueChange = (selectedValue) => {
|
|
2637
|
+
const handler = createEventHandler(
|
|
2638
|
+
node,
|
|
2639
|
+
"onValueChange",
|
|
2640
|
+
"CHANGE",
|
|
2641
|
+
processEvent
|
|
2642
|
+
);
|
|
2643
|
+
if (handler)
|
|
2644
|
+
handler({ value: selectedValue });
|
|
2645
|
+
};
|
|
2646
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2647
|
+
"div",
|
|
2648
|
+
{
|
|
2649
|
+
className: cn("grid gap-1.5", className),
|
|
2650
|
+
children: [
|
|
2651
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(Label2, { className: "mb-1", children: label }),
|
|
2652
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2653
|
+
RadioGroup,
|
|
2654
|
+
{
|
|
2655
|
+
name,
|
|
2656
|
+
value,
|
|
2657
|
+
onValueChange: handleValueChange,
|
|
2658
|
+
disabled,
|
|
2659
|
+
className: "flex flex-col space-y-1",
|
|
2660
|
+
children: options.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
2661
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2662
|
+
RadioGroupItem,
|
|
2663
|
+
{
|
|
2664
|
+
value: option.value,
|
|
2665
|
+
id: `${name}-${option.value}`
|
|
2666
|
+
}
|
|
2667
|
+
),
|
|
2668
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2669
|
+
Label2,
|
|
2670
|
+
{
|
|
2671
|
+
htmlFor: `${name}-${option.value}`,
|
|
2672
|
+
className: "cursor-pointer",
|
|
2673
|
+
children: option.label
|
|
2674
|
+
}
|
|
2675
|
+
)
|
|
2676
|
+
] }, option.value))
|
|
2677
|
+
}
|
|
2678
|
+
)
|
|
2679
|
+
]
|
|
2680
|
+
},
|
|
2681
|
+
key
|
|
2682
|
+
);
|
|
2683
|
+
},
|
|
2684
|
+
Tabs: (node, processEvent) => {
|
|
2685
|
+
const { key, ...propsWithoutKey } = node.props || {};
|
|
2686
|
+
const rawTabs = getSafeBinding(
|
|
2687
|
+
node.bindings,
|
|
2688
|
+
"tabs",
|
|
2689
|
+
isArrayOf(isTabObject),
|
|
2690
|
+
[]
|
|
2691
|
+
);
|
|
2692
|
+
const defaultValue = getSafeProp(
|
|
2693
|
+
propsWithoutKey,
|
|
2694
|
+
"defaultValue",
|
|
2695
|
+
isString,
|
|
2696
|
+
rawTabs[0]?.value || ""
|
|
2697
|
+
);
|
|
2698
|
+
const className = getSafeProp(propsWithoutKey, "className", isString, "");
|
|
2699
|
+
const handleValueChange = (value) => {
|
|
2700
|
+
const handler = createEventHandler(
|
|
2701
|
+
node,
|
|
2702
|
+
"onValueChange",
|
|
2703
|
+
"CHANGE",
|
|
2704
|
+
processEvent
|
|
2705
|
+
);
|
|
2706
|
+
if (handler)
|
|
2707
|
+
handler({ value });
|
|
2708
|
+
};
|
|
2709
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2710
|
+
Tabs,
|
|
2711
|
+
{
|
|
2712
|
+
defaultValue,
|
|
2713
|
+
onValueChange: handleValueChange,
|
|
2714
|
+
className: cn("autoui-tabs w-full", className),
|
|
2715
|
+
"data-id": node.id,
|
|
2716
|
+
children: [
|
|
2717
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabsList, { children: rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value)) }),
|
|
2718
|
+
rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: tab.value, children: tab.content ? renderNode(tab.content, processEvent) : null }, tab.value))
|
|
2719
|
+
]
|
|
2720
|
+
},
|
|
2721
|
+
key
|
|
2722
|
+
);
|
|
2723
|
+
},
|
|
2724
|
+
Dialog: (node, processEvent) => {
|
|
2725
|
+
const isOpen = getSafeBinding(
|
|
2726
|
+
node.bindings,
|
|
2727
|
+
"visible",
|
|
2728
|
+
isBoolean,
|
|
2729
|
+
// Check bindings.visible (planner output)
|
|
2730
|
+
getSafeProp(
|
|
2731
|
+
node.props,
|
|
2732
|
+
"open",
|
|
2733
|
+
isBoolean,
|
|
2734
|
+
// Then props.open (if binding resolution put it there)
|
|
2735
|
+
getSafeProp(node.props, "visible", isBoolean, false)
|
|
2736
|
+
// Then props.visible (if binding resolution put it there under 'visible')
|
|
2737
|
+
)
|
|
2738
|
+
);
|
|
2739
|
+
const {
|
|
2740
|
+
title,
|
|
2741
|
+
description,
|
|
2742
|
+
className,
|
|
2743
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2744
|
+
style: _styleProp,
|
|
2745
|
+
key,
|
|
2746
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2747
|
+
open: _openProp,
|
|
2748
|
+
// ADD 'visible' to destructuring to prevent it from going into restProps
|
|
2749
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2750
|
+
visible: _visibleProp,
|
|
2751
|
+
...restProps
|
|
2752
|
+
} = node.props || {};
|
|
2753
|
+
const children = node.children?.map(
|
|
2754
|
+
(child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
|
|
2755
|
+
);
|
|
2756
|
+
const handleOpenChange = (open) => {
|
|
2757
|
+
if (!open) {
|
|
2758
|
+
const handler = createEventHandler(
|
|
2759
|
+
node,
|
|
2760
|
+
"onClose",
|
|
2761
|
+
// Assumed event name in UISpec
|
|
2762
|
+
"CLICK",
|
|
2763
|
+
// Use CLICK as the event type for closing dialogs
|
|
2764
|
+
processEvent
|
|
2765
|
+
);
|
|
2766
|
+
if (handler) {
|
|
2767
|
+
handler({});
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
};
|
|
2771
|
+
console.log(
|
|
2772
|
+
`[Adapter Debug] Rendering Dialog: id=${node.id}, props=`,
|
|
2773
|
+
node.props,
|
|
2774
|
+
`isOpen=${isOpen}`
|
|
2775
|
+
);
|
|
2776
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2777
|
+
Dialog,
|
|
2778
|
+
{
|
|
2779
|
+
open: isOpen,
|
|
2780
|
+
onOpenChange: handleOpenChange,
|
|
2781
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2782
|
+
DialogContent,
|
|
2783
|
+
{
|
|
2784
|
+
className: cn("autoui-dialog-content", className),
|
|
2785
|
+
...restProps,
|
|
2786
|
+
"data-id": node.id,
|
|
2787
|
+
children: [
|
|
2788
|
+
(title || description) && /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
2789
|
+
title && /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: title }),
|
|
2790
|
+
description && /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: description })
|
|
2791
|
+
] }),
|
|
2792
|
+
children
|
|
2793
|
+
]
|
|
2794
|
+
}
|
|
2795
|
+
)
|
|
2796
|
+
},
|
|
2797
|
+
key
|
|
2798
|
+
);
|
|
2799
|
+
},
|
|
2800
|
+
Heading: (node) => {
|
|
2801
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2802
|
+
const text = getSafeProp(node.props, "text", isString, "Heading");
|
|
2803
|
+
let level = getSafeProp(
|
|
2804
|
+
node.props,
|
|
2805
|
+
"level",
|
|
2806
|
+
(v) => typeof v === "number" && v >= 1 && v <= 6,
|
|
2807
|
+
2
|
|
2808
|
+
);
|
|
2809
|
+
if (typeof level !== "number" || level < 1 || level > 6) {
|
|
2810
|
+
level = 2;
|
|
843
2811
|
}
|
|
844
|
-
|
|
2812
|
+
const Tag = `h${level}`;
|
|
2813
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2814
|
+
const headingStyles = {
|
|
2815
|
+
1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
|
|
2816
|
+
2: "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0",
|
|
2817
|
+
3: "scroll-m-20 text-2xl font-semibold tracking-tight",
|
|
2818
|
+
4: "scroll-m-20 text-xl font-semibold tracking-tight"
|
|
2819
|
+
// Add styles for h5, h6 if needed, using text-lg, text-base etc.
|
|
2820
|
+
}[level] || "text-lg font-semibold";
|
|
2821
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2822
|
+
Tag,
|
|
2823
|
+
{
|
|
2824
|
+
className: cn(headingStyles, className),
|
|
2825
|
+
style,
|
|
2826
|
+
...restProps,
|
|
2827
|
+
children: text
|
|
2828
|
+
},
|
|
2829
|
+
key
|
|
2830
|
+
);
|
|
2831
|
+
},
|
|
2832
|
+
Text: (node) => {
|
|
2833
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2834
|
+
const text = getSafeProp(node.props, "text", isString, "Some text");
|
|
2835
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2836
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2837
|
+
"p",
|
|
2838
|
+
{
|
|
2839
|
+
className: cn("leading-7", className),
|
|
2840
|
+
style,
|
|
2841
|
+
...restProps,
|
|
2842
|
+
children: text
|
|
2843
|
+
},
|
|
2844
|
+
key
|
|
2845
|
+
);
|
|
2846
|
+
},
|
|
2847
|
+
Badge: (node) => {
|
|
2848
|
+
const { className, style: styleProp, key, ...restProps } = node.props || {};
|
|
2849
|
+
const text = getSafeProp(node.props, "text", isString, "");
|
|
2850
|
+
const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
|
|
2851
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2852
|
+
"span",
|
|
2853
|
+
{
|
|
2854
|
+
className: cn(
|
|
2855
|
+
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium bg-gray-100 text-gray-800",
|
|
2856
|
+
className
|
|
2857
|
+
),
|
|
2858
|
+
style,
|
|
2859
|
+
...restProps,
|
|
2860
|
+
children: text
|
|
2861
|
+
},
|
|
2862
|
+
key
|
|
2863
|
+
);
|
|
2864
|
+
}
|
|
845
2865
|
};
|
|
846
|
-
function renderNode(node) {
|
|
847
|
-
const
|
|
848
|
-
if (
|
|
849
|
-
return
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
] });
|
|
2866
|
+
function renderNode(node, processEvent) {
|
|
2867
|
+
const mappedComponent = adapterMap[node.node_type];
|
|
2868
|
+
if (mappedComponent) {
|
|
2869
|
+
return mappedComponent(node, processEvent);
|
|
2870
|
+
}
|
|
2871
|
+
console.warn(`Unknown node type: ${node.node_type}`);
|
|
2872
|
+
return React__default.default.createElement(
|
|
2873
|
+
Container,
|
|
2874
|
+
{},
|
|
2875
|
+
`Unknown node type: ${node.node_type}`
|
|
2876
|
+
);
|
|
858
2877
|
}
|
|
859
|
-
|
|
2878
|
+
var renderedNodesCache = /* @__PURE__ */ new Map();
|
|
2879
|
+
var MAX_CACHE_SIZE = 10;
|
|
2880
|
+
var CACHE_TTL = 5e3;
|
|
2881
|
+
var clearRenderedNodeCacheEntry = (cacheKey) => {
|
|
2882
|
+
renderedNodesCache.delete(cacheKey);
|
|
2883
|
+
console.log(`[Renderer Cache] Cleared entry FOR REAL for key: ${cacheKey}`);
|
|
2884
|
+
};
|
|
2885
|
+
var getRendererCacheKey = (node) => {
|
|
2886
|
+
if (node.id === "task-detail") {
|
|
2887
|
+
const dataId = node.props?.data?.id;
|
|
2888
|
+
return `${node.id}:${node.props?.visible}:${dataId || "no-data-selected"}`;
|
|
2889
|
+
}
|
|
2890
|
+
let propsString = "null";
|
|
2891
|
+
try {
|
|
2892
|
+
propsString = JSON.stringify(node.props);
|
|
2893
|
+
} catch (_e) {
|
|
2894
|
+
console.warn(
|
|
2895
|
+
`[Renderer Cache Key] Error stringifying node.props for ID ${node.id}, using 'null' for props part of key.`
|
|
2896
|
+
);
|
|
2897
|
+
}
|
|
2898
|
+
let bindingsString = "null";
|
|
2899
|
+
try {
|
|
2900
|
+
bindingsString = JSON.stringify(node.bindings);
|
|
2901
|
+
} catch (_e) {
|
|
2902
|
+
console.warn(
|
|
2903
|
+
`[Renderer Cache Key] Error stringifying node.bindings for ID ${node.id}, using 'null' for bindings part of key.`
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
return `${node.id}:${propsString}:${bindingsString}`;
|
|
2907
|
+
};
|
|
2908
|
+
async function renderNode2(node, adapter = "shadcn", processEvent) {
|
|
860
2909
|
const startTime = Date.now();
|
|
2910
|
+
const cacheKey = getRendererCacheKey(node);
|
|
2911
|
+
const cachedItem = renderedNodesCache.get(cacheKey);
|
|
2912
|
+
if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
|
|
2913
|
+
return cachedItem.element;
|
|
2914
|
+
}
|
|
2915
|
+
if (node.id === "task-detail") {
|
|
2916
|
+
let safeNodeString = "Error stringifying node for log";
|
|
2917
|
+
let propsToLog = "{}";
|
|
2918
|
+
try {
|
|
2919
|
+
const clonedProps = node.props ? { ...node.props } : {};
|
|
2920
|
+
if (clonedProps && clonedProps.data) {
|
|
2921
|
+
clonedProps.data = `Data present (type: ${typeof clonedProps.data}, logging suppressed)`;
|
|
2922
|
+
}
|
|
2923
|
+
if (clonedProps && Array.isArray(clonedProps.fields)) {
|
|
2924
|
+
clonedProps.fields = `Fields array (length: ${clonedProps.fields.length}, logging suppressed)`;
|
|
2925
|
+
} else if (clonedProps && clonedProps.fields) {
|
|
2926
|
+
clonedProps.fields = `Fields present (type: ${typeof clonedProps.fields}, logging suppressed)`;
|
|
2927
|
+
}
|
|
2928
|
+
propsToLog = JSON.stringify(clonedProps);
|
|
2929
|
+
const nodeToLog = {
|
|
2930
|
+
id: node.id,
|
|
2931
|
+
node_type: node.node_type,
|
|
2932
|
+
props: "See props above",
|
|
2933
|
+
bindings: node.bindings,
|
|
2934
|
+
events: node.events,
|
|
2935
|
+
children: node.children ? `Children array (length: ${node.children.length}, logging suppressed)` : null
|
|
2936
|
+
};
|
|
2937
|
+
safeNodeString = JSON.stringify(nodeToLog);
|
|
2938
|
+
} catch (e) {
|
|
2939
|
+
if (e instanceof Error) {
|
|
2940
|
+
safeNodeString = `Error stringifying node for log: ${e.message}`;
|
|
2941
|
+
} else {
|
|
2942
|
+
safeNodeString = `Error stringifying node for log: Unknown error`;
|
|
2943
|
+
}
|
|
2944
|
+
if (node.props === void 0)
|
|
2945
|
+
propsToLog = "undefined";
|
|
2946
|
+
else if (node.props === null)
|
|
2947
|
+
propsToLog = "null";
|
|
2948
|
+
}
|
|
2949
|
+
console.log(
|
|
2950
|
+
`[Renderer renderNode BEFORE RENDER_START event] About to call adapter for task-detail. ID: ${node.id}, Visible: ${node.props?.visible}, Props (safe): ${propsToLog}, Bindings: ${JSON.stringify(
|
|
2951
|
+
node.bindings
|
|
2952
|
+
)}, Node (safe): ${safeNodeString}`
|
|
2953
|
+
);
|
|
2954
|
+
}
|
|
861
2955
|
await systemEvents.emit(
|
|
862
2956
|
createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
|
|
863
2957
|
);
|
|
864
2958
|
let result;
|
|
865
2959
|
switch (adapter) {
|
|
866
2960
|
case "shadcn":
|
|
867
|
-
result = renderNode(node);
|
|
2961
|
+
result = renderNode(node, processEvent);
|
|
868
2962
|
break;
|
|
869
2963
|
default:
|
|
870
2964
|
console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
|
|
871
|
-
result = renderNode(node);
|
|
2965
|
+
result = renderNode(node, processEvent);
|
|
2966
|
+
}
|
|
2967
|
+
if (node.id === "task-detail") {
|
|
2968
|
+
let elementType = void 0;
|
|
2969
|
+
if (result && typeof result.type === "function") {
|
|
2970
|
+
elementType = result.type.name;
|
|
2971
|
+
} else if (result && typeof result.type === "string") {
|
|
2972
|
+
elementType = result.type;
|
|
2973
|
+
} else if (result && typeof result.type === "object" && result.type !== null) {
|
|
2974
|
+
const componentType2 = result.type;
|
|
2975
|
+
if (typeof componentType2.displayName === "string") {
|
|
2976
|
+
elementType = componentType2.displayName;
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
console.log(
|
|
2980
|
+
`[Renderer renderNode] Adapter for task-detail returned element. Element type:`,
|
|
2981
|
+
elementType
|
|
2982
|
+
);
|
|
872
2983
|
}
|
|
873
2984
|
await systemEvents.emit(
|
|
874
2985
|
createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
|
|
@@ -876,13 +2987,23 @@ async function renderNode2(node, adapter = "shadcn") {
|
|
|
876
2987
|
renderTimeMs: Date.now() - startTime
|
|
877
2988
|
})
|
|
878
2989
|
);
|
|
2990
|
+
renderedNodesCache.set(cacheKey, {
|
|
2991
|
+
element: result,
|
|
2992
|
+
timestamp: startTime
|
|
2993
|
+
});
|
|
2994
|
+
if (renderedNodesCache.size > MAX_CACHE_SIZE) {
|
|
2995
|
+
const oldestEntry = renderedNodesCache.entries().next().value;
|
|
2996
|
+
if (oldestEntry) {
|
|
2997
|
+
renderedNodesCache.delete(oldestEntry[0]);
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
879
3000
|
return result;
|
|
880
3001
|
}
|
|
881
3002
|
function renderShimmer(node, adapter = "shadcn") {
|
|
882
3003
|
if (!node) {
|
|
883
3004
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
|
|
884
3005
|
}
|
|
885
|
-
switch (node.
|
|
3006
|
+
switch (node.node_type) {
|
|
886
3007
|
case "ListView":
|
|
887
3008
|
return /* @__PURE__ */ jsxRuntime.jsx(ShimmerTable, { rows: 3 });
|
|
888
3009
|
case "Detail":
|
|
@@ -894,113 +3015,6 @@ function renderShimmer(node, adapter = "shadcn") {
|
|
|
894
3015
|
}
|
|
895
3016
|
}
|
|
896
3017
|
|
|
897
|
-
// src/core/bindings.ts
|
|
898
|
-
function getValueByPath(context, path) {
|
|
899
|
-
const parts = path.split(".");
|
|
900
|
-
let current = context;
|
|
901
|
-
for (const part of parts) {
|
|
902
|
-
if (current === null || current === void 0) {
|
|
903
|
-
return void 0;
|
|
904
|
-
}
|
|
905
|
-
if (typeof current !== "object") {
|
|
906
|
-
return void 0;
|
|
907
|
-
}
|
|
908
|
-
current = current[part];
|
|
909
|
-
}
|
|
910
|
-
return current;
|
|
911
|
-
}
|
|
912
|
-
function setValueByPath(context, path, value) {
|
|
913
|
-
const result = { ...context };
|
|
914
|
-
const parts = path.split(".");
|
|
915
|
-
let current = result;
|
|
916
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
917
|
-
const part = parts[i];
|
|
918
|
-
if (!(part in current) || current[part] === null || current[part] === void 0) {
|
|
919
|
-
current[part] = {};
|
|
920
|
-
}
|
|
921
|
-
current = current[part];
|
|
922
|
-
if (typeof current !== "object") {
|
|
923
|
-
current = {};
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
const lastPart = parts[parts.length - 1];
|
|
927
|
-
current[lastPart] = value;
|
|
928
|
-
return result;
|
|
929
|
-
}
|
|
930
|
-
function processBinding(binding, context) {
|
|
931
|
-
if (typeof binding === "string") {
|
|
932
|
-
return getValueByPath(context, binding);
|
|
933
|
-
}
|
|
934
|
-
if (Array.isArray(binding)) {
|
|
935
|
-
return binding.map((item) => processBinding(item, context));
|
|
936
|
-
}
|
|
937
|
-
if (binding !== null && typeof binding === "object") {
|
|
938
|
-
const result = {};
|
|
939
|
-
for (const [key, value] of Object.entries(binding)) {
|
|
940
|
-
result[key] = processBinding(value, context);
|
|
941
|
-
}
|
|
942
|
-
return result;
|
|
943
|
-
}
|
|
944
|
-
return binding;
|
|
945
|
-
}
|
|
946
|
-
async function resolveBindings(node, context) {
|
|
947
|
-
await systemEvents.emit(
|
|
948
|
-
createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, { layout: node })
|
|
949
|
-
);
|
|
950
|
-
const result = {
|
|
951
|
-
...node,
|
|
952
|
-
props: node.props ? { ...node.props } : void 0,
|
|
953
|
-
events: node.events ? { ...node.events } : void 0
|
|
954
|
-
};
|
|
955
|
-
if (node.bindings) {
|
|
956
|
-
for (const [key, binding] of Object.entries(node.bindings)) {
|
|
957
|
-
const value = processBinding(binding, context);
|
|
958
|
-
if (value !== void 0) {
|
|
959
|
-
if (!result.props) {
|
|
960
|
-
result.props = {};
|
|
961
|
-
}
|
|
962
|
-
result.props[key] = value;
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
if (node.children) {
|
|
967
|
-
result.children = await Promise.all(node.children.map((child) => resolveBindings(child, context)));
|
|
968
|
-
}
|
|
969
|
-
await systemEvents.emit(
|
|
970
|
-
createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
|
|
971
|
-
originalLayout: node,
|
|
972
|
-
resolvedLayout: result
|
|
973
|
-
})
|
|
974
|
-
);
|
|
975
|
-
return result;
|
|
976
|
-
}
|
|
977
|
-
function executeAction(action, targetId, payload, context = {}, layoutTree) {
|
|
978
|
-
let newContext = { ...context };
|
|
979
|
-
switch (action) {
|
|
980
|
-
case "VIEW_DETAIL": {
|
|
981
|
-
if (payload?.item) {
|
|
982
|
-
newContext = setValueByPath(newContext, "selected", payload.item);
|
|
983
|
-
}
|
|
984
|
-
break;
|
|
985
|
-
}
|
|
986
|
-
case "HIDE_DETAIL": {
|
|
987
|
-
newContext = setValueByPath(newContext, "selected", null);
|
|
988
|
-
break;
|
|
989
|
-
}
|
|
990
|
-
case "SET_VALUE": {
|
|
991
|
-
if (payload?.path && "value" in payload) {
|
|
992
|
-
const path = String(payload.path);
|
|
993
|
-
newContext = setValueByPath(newContext, path, payload.value);
|
|
994
|
-
}
|
|
995
|
-
break;
|
|
996
|
-
}
|
|
997
|
-
// Add more actions as needed
|
|
998
|
-
default:
|
|
999
|
-
console.warn(`Unknown action: ${action}`);
|
|
1000
|
-
}
|
|
1001
|
-
return newContext;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
3018
|
// src/core/events.ts
|
|
1005
3019
|
var EventManager = class {
|
|
1006
3020
|
constructor() {
|
|
@@ -1008,7 +3022,7 @@ var EventManager = class {
|
|
|
1008
3022
|
}
|
|
1009
3023
|
/**
|
|
1010
3024
|
* Register a hook for specific event types
|
|
1011
|
-
*
|
|
3025
|
+
*
|
|
1012
3026
|
* @param eventTypes - Event types to register for, or 'all' for all events
|
|
1013
3027
|
* @param hook - Hook function to execute
|
|
1014
3028
|
* @returns Unregister function
|
|
@@ -1041,7 +3055,7 @@ var EventManager = class {
|
|
|
1041
3055
|
}
|
|
1042
3056
|
/**
|
|
1043
3057
|
* Process an event through all registered hooks
|
|
1044
|
-
*
|
|
3058
|
+
*
|
|
1045
3059
|
* @param event - The UI event to process
|
|
1046
3060
|
* @returns Whether the default action should proceed
|
|
1047
3061
|
*/
|
|
@@ -1062,13 +3076,15 @@ var EventManager = class {
|
|
|
1062
3076
|
if (this.hooks.all) {
|
|
1063
3077
|
for (const hook of this.hooks.all) {
|
|
1064
3078
|
await hook(context);
|
|
1065
|
-
if (propagationStopped)
|
|
3079
|
+
if (propagationStopped)
|
|
3080
|
+
break;
|
|
1066
3081
|
}
|
|
1067
3082
|
}
|
|
1068
3083
|
if (!propagationStopped && this.hooks[event.type]) {
|
|
1069
3084
|
for (const hook of this.hooks[event.type] || []) {
|
|
1070
3085
|
await hook(context);
|
|
1071
|
-
if (propagationStopped)
|
|
3086
|
+
if (propagationStopped)
|
|
3087
|
+
break;
|
|
1072
3088
|
}
|
|
1073
3089
|
}
|
|
1074
3090
|
return !defaultPrevented;
|
|
@@ -1085,6 +3101,45 @@ function createEventHook(eventTypes, hook, options) {
|
|
|
1085
3101
|
}
|
|
1086
3102
|
};
|
|
1087
3103
|
}
|
|
3104
|
+
|
|
3105
|
+
// src/core/component-detection.ts
|
|
3106
|
+
function areShadcnComponentsAvailable() {
|
|
3107
|
+
try {
|
|
3108
|
+
init_button();
|
|
3109
|
+
return true;
|
|
3110
|
+
} catch (error) {
|
|
3111
|
+
return false;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
function getMissingComponentsMessage() {
|
|
3115
|
+
return `Missing required shadcn components. Please run:
|
|
3116
|
+
> npm run setup-shadcn`;
|
|
3117
|
+
}
|
|
3118
|
+
function correctListBindingsRecursive(node, dataContext) {
|
|
3119
|
+
const correctedNode = JSON.parse(JSON.stringify(node));
|
|
3120
|
+
if ((correctedNode.node_type === "ListView" || correctedNode.node_type === "Table") && correctedNode.bindings?.data) {
|
|
3121
|
+
const bindingPath = correctedNode.bindings.data;
|
|
3122
|
+
if (typeof bindingPath === "string") {
|
|
3123
|
+
const pathSegments = bindingPath.split(".");
|
|
3124
|
+
const mainKey = pathSegments[0];
|
|
3125
|
+
if (pathSegments.length === 1) {
|
|
3126
|
+
const potentialDataContextEntry = dataContext[mainKey];
|
|
3127
|
+
if (potentialDataContextEntry && typeof potentialDataContextEntry === "object" && potentialDataContextEntry !== null && "data" in potentialDataContextEntry && Array.isArray(potentialDataContextEntry.data)) {
|
|
3128
|
+
correctedNode.bindings.data = `${mainKey}.data`;
|
|
3129
|
+
console.log(
|
|
3130
|
+
`[AutoUI Debug] Corrected list binding for node '${correctedNode.id}': from '${mainKey}' to '${mainKey}.data'`
|
|
3131
|
+
);
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
if (correctedNode.children) {
|
|
3137
|
+
correctedNode.children = correctedNode.children.map(
|
|
3138
|
+
(child) => correctListBindingsRecursive(child, dataContext)
|
|
3139
|
+
);
|
|
3140
|
+
}
|
|
3141
|
+
return correctedNode;
|
|
3142
|
+
}
|
|
1088
3143
|
var AutoUI = ({
|
|
1089
3144
|
schema,
|
|
1090
3145
|
goal,
|
|
@@ -1094,24 +3149,42 @@ var AutoUI = ({
|
|
|
1094
3149
|
eventHooks,
|
|
1095
3150
|
systemEventHooks,
|
|
1096
3151
|
debugMode = false,
|
|
1097
|
-
mockMode = true,
|
|
1098
|
-
databaseConfig,
|
|
1099
3152
|
planningConfig,
|
|
1100
3153
|
integration = {},
|
|
1101
|
-
|
|
1102
|
-
|
|
3154
|
+
enablePartialUpdates = true,
|
|
3155
|
+
apiKey
|
|
1103
3156
|
}) => {
|
|
1104
|
-
const
|
|
1105
|
-
const
|
|
1106
|
-
const
|
|
1107
|
-
const
|
|
1108
|
-
|
|
3157
|
+
const stableGoal = React__default.default.useMemo(() => goal, [goal]);
|
|
3158
|
+
const schemaStringified = React__default.default.useMemo(() => JSON.stringify(schema), [schema]);
|
|
3159
|
+
const stableSchemaProp = React__default.default.useMemo(() => schema, [schemaStringified]);
|
|
3160
|
+
const [schemaAdapterInstance] = React.useState(null);
|
|
3161
|
+
const [dataContext, setDataContext] = React.useState({});
|
|
3162
|
+
const [componentsAvailable, setComponentsAvailable] = React.useState(true);
|
|
3163
|
+
const [uiStatus, setUiStatus] = React.useState("initializing");
|
|
3164
|
+
const [currentResolvedLayoutForRender, setCurrentResolvedLayoutForRender] = React.useState(null);
|
|
3165
|
+
const [isResolvingBindings, setIsResolvingBindings] = React.useState(false);
|
|
3166
|
+
const [renderedNode, setRenderedNode] = React.useState(
|
|
3167
|
+
null
|
|
3168
|
+
);
|
|
3169
|
+
const currentResolvedLayoutRef = React.useRef(null);
|
|
3170
|
+
const effectiveSchema = stableSchemaProp;
|
|
3171
|
+
const scopedGoal = stableGoal;
|
|
3172
|
+
React.useEffect(() => {
|
|
3173
|
+
if (componentAdapter === "shadcn") {
|
|
3174
|
+
setComponentsAvailable(areShadcnComponentsAvailable());
|
|
3175
|
+
}
|
|
3176
|
+
}, [componentAdapter]);
|
|
3177
|
+
React.useEffect(() => {
|
|
1109
3178
|
const unregisters = [];
|
|
1110
3179
|
if (systemEventHooks) {
|
|
1111
3180
|
Object.entries(systemEventHooks).forEach(([eventType, hooks]) => {
|
|
1112
|
-
if (!hooks)
|
|
3181
|
+
if (!hooks)
|
|
3182
|
+
return;
|
|
1113
3183
|
hooks.forEach((hook) => {
|
|
1114
|
-
const unregister = systemEvents.on(
|
|
3184
|
+
const unregister = systemEvents.on(
|
|
3185
|
+
eventType,
|
|
3186
|
+
hook
|
|
3187
|
+
);
|
|
1115
3188
|
unregisters.push(unregister);
|
|
1116
3189
|
});
|
|
1117
3190
|
});
|
|
@@ -1120,7 +3193,9 @@ var AutoUI = ({
|
|
|
1120
3193
|
const debugHook = (event) => {
|
|
1121
3194
|
console.debug(`[AutoUI Debug] System Event:`, event);
|
|
1122
3195
|
};
|
|
1123
|
-
Object.values(SystemEventType).
|
|
3196
|
+
Object.values(SystemEventType).filter(
|
|
3197
|
+
(eventType) => eventType !== "RENDER_START" /* RENDER_START */ && eventType !== "BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */
|
|
3198
|
+
).forEach((eventType) => {
|
|
1124
3199
|
const unregister = systemEvents.on(eventType, debugHook);
|
|
1125
3200
|
unregisters.push(unregister);
|
|
1126
3201
|
});
|
|
@@ -1129,7 +3204,7 @@ var AutoUI = ({
|
|
|
1129
3204
|
unregisters.forEach((unregister) => unregister());
|
|
1130
3205
|
};
|
|
1131
3206
|
}, [systemEventHooks, debugMode]);
|
|
1132
|
-
|
|
3207
|
+
React.useEffect(() => {
|
|
1133
3208
|
const initializeDataContext = async () => {
|
|
1134
3209
|
let initialData = {};
|
|
1135
3210
|
if (schemaAdapterInstance) {
|
|
@@ -1138,7 +3213,6 @@ var AutoUI = ({
|
|
|
1138
3213
|
Object.entries(effectiveSchema).forEach(([key, tableSchema]) => {
|
|
1139
3214
|
initialData[key] = {
|
|
1140
3215
|
schema: tableSchema,
|
|
1141
|
-
// For development, add sample data if available
|
|
1142
3216
|
data: tableSchema?.sampleData || [],
|
|
1143
3217
|
selected: null
|
|
1144
3218
|
};
|
|
@@ -1155,118 +3229,387 @@ var AutoUI = ({
|
|
|
1155
3229
|
schema: effectiveSchema,
|
|
1156
3230
|
goal: scopedGoal,
|
|
1157
3231
|
userContext,
|
|
1158
|
-
mockMode,
|
|
1159
3232
|
planningConfig,
|
|
1160
|
-
router: void 0,
|
|
1161
3233
|
dataContext,
|
|
1162
|
-
|
|
3234
|
+
// Pass the local dataContext here, engine will use it if its own is empty initially
|
|
3235
|
+
enablePartialUpdates,
|
|
3236
|
+
apiKey
|
|
1163
3237
|
});
|
|
1164
|
-
const eventManagerRef =
|
|
1165
|
-
|
|
1166
|
-
if (!eventHooks)
|
|
3238
|
+
const eventManagerRef = React.useRef(new EventManager());
|
|
3239
|
+
React.useEffect(() => {
|
|
3240
|
+
if (!eventHooks)
|
|
3241
|
+
return;
|
|
1167
3242
|
const unregisters = [];
|
|
1168
3243
|
if (eventHooks.all) {
|
|
1169
|
-
const unregister = eventManagerRef.current.register(
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
3244
|
+
const unregister = eventManagerRef.current.register(
|
|
3245
|
+
"all",
|
|
3246
|
+
async (ctx) => {
|
|
3247
|
+
for (const hook of eventHooks.all || []) {
|
|
3248
|
+
await hook(ctx);
|
|
3249
|
+
if (ctx.isPropagationStopped())
|
|
3250
|
+
break;
|
|
3251
|
+
}
|
|
1173
3252
|
}
|
|
1174
|
-
|
|
3253
|
+
);
|
|
1175
3254
|
unregisters.push(unregister);
|
|
1176
3255
|
}
|
|
1177
3256
|
Object.entries(eventHooks).forEach(([type, hooks]) => {
|
|
1178
|
-
if (type === "all" || !hooks)
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
3257
|
+
if (type === "all" || !hooks)
|
|
3258
|
+
return;
|
|
3259
|
+
const unregister = eventManagerRef.current.register(
|
|
3260
|
+
[type],
|
|
3261
|
+
async (ctx) => {
|
|
3262
|
+
for (const hook of hooks) {
|
|
3263
|
+
await hook(ctx);
|
|
3264
|
+
if (ctx.isPropagationStopped())
|
|
3265
|
+
break;
|
|
3266
|
+
}
|
|
1183
3267
|
}
|
|
1184
|
-
|
|
3268
|
+
);
|
|
1185
3269
|
unregisters.push(unregister);
|
|
1186
3270
|
});
|
|
1187
3271
|
return () => {
|
|
1188
3272
|
unregisters.forEach((unregister) => unregister());
|
|
1189
3273
|
};
|
|
1190
3274
|
}, [eventHooks]);
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
3275
|
+
React.useEffect(() => {
|
|
3276
|
+
currentResolvedLayoutRef.current = currentResolvedLayoutForRender;
|
|
3277
|
+
}, [currentResolvedLayoutForRender]);
|
|
3278
|
+
const processEvent = React.useCallback(
|
|
3279
|
+
async (event) => {
|
|
3280
|
+
setUiStatus("event_processing");
|
|
3281
|
+
const layoutAtEventTime = currentResolvedLayoutRef.current;
|
|
3282
|
+
const shouldProceed = await eventManagerRef.current.processEvent(event);
|
|
3283
|
+
if (onEvent)
|
|
3284
|
+
onEvent(event);
|
|
3285
|
+
if (!shouldProceed) {
|
|
3286
|
+
console.info(
|
|
3287
|
+
"[AutoUI.processEvent] Event processing stopped by local hooks",
|
|
3288
|
+
event
|
|
3289
|
+
);
|
|
3290
|
+
setUiStatus("idle");
|
|
3291
|
+
return;
|
|
3292
|
+
}
|
|
3293
|
+
if (event.type === "CLICK" && layoutAtEventTime && event.nodeId.includes("view-details-button")) {
|
|
3294
|
+
const mainContent = layoutAtEventTime.children?.find(
|
|
3295
|
+
(c) => c.id === "main-content"
|
|
3296
|
+
);
|
|
3297
|
+
const taskList = mainContent?.children?.find((c) => c.id === "tasks-container")?.children?.find((c) => c.id === "task-list");
|
|
3298
|
+
const eventSourceListItemCard = taskList?.children?.find(
|
|
3299
|
+
(item) => item.children?.some((btn) => btn.id === event.nodeId)
|
|
3300
|
+
);
|
|
3301
|
+
const buttonNode = eventSourceListItemCard?.children?.find(
|
|
3302
|
+
(btn) => btn.id === event.nodeId
|
|
3303
|
+
);
|
|
3304
|
+
if (buttonNode?.events?.CLICK?.action === "SHOW_DETAIL" /* SHOW_DETAIL */ && buttonNode?.events?.CLICK?.target === "task-detail") {
|
|
3305
|
+
const cacheKeyToClear = `task-detail:false:no-data-selected`;
|
|
3306
|
+
clearRenderedNodeCacheEntry(cacheKeyToClear);
|
|
3307
|
+
console.log(
|
|
3308
|
+
`[AutoUI.processEvent] Attempted to clear cache for task-detail using simplified key: ${cacheKeyToClear}`
|
|
3309
|
+
);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
console.log(
|
|
3313
|
+
"[AutoUI.processEvent] Forwarding event to engine:",
|
|
3314
|
+
JSON.stringify(event, null, 2)
|
|
3315
|
+
);
|
|
3316
|
+
console.log(
|
|
3317
|
+
"[AutoUI.processEvent] Passing currentLayout ID (at event time):",
|
|
3318
|
+
layoutAtEventTime?.id
|
|
3319
|
+
);
|
|
3320
|
+
console.log(
|
|
3321
|
+
"[AutoUI.processEvent] Passing dataContext keys to engine:",
|
|
3322
|
+
Object.keys(dataContext)
|
|
3323
|
+
);
|
|
3324
|
+
handleEvent(event, layoutAtEventTime, dataContext);
|
|
3325
|
+
},
|
|
3326
|
+
[
|
|
3327
|
+
dataContext,
|
|
3328
|
+
handleEvent,
|
|
3329
|
+
onEvent,
|
|
3330
|
+
eventManagerRef
|
|
3331
|
+
// currentResolvedLayoutForRender removed - using ref instead to prevent infinite loops
|
|
3332
|
+
// setUiStatus is implicitly available
|
|
3333
|
+
]
|
|
3334
|
+
);
|
|
3335
|
+
React.useEffect(() => {
|
|
3336
|
+
if (state.dataContext && Object.keys(state.dataContext).length > 0) {
|
|
3337
|
+
if (JSON.stringify(dataContext) !== JSON.stringify(state.dataContext)) {
|
|
3338
|
+
console.log(
|
|
3339
|
+
"[AutoUI] Syncing local dataContext from engine state. New keys:",
|
|
3340
|
+
Object.keys(state.dataContext),
|
|
3341
|
+
"Old keys:",
|
|
3342
|
+
Object.keys(dataContext)
|
|
3343
|
+
);
|
|
3344
|
+
setDataContext(state.dataContext);
|
|
3345
|
+
}
|
|
1199
3346
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
3347
|
+
}, [state.dataContext, dataContext]);
|
|
3348
|
+
React.useEffect(() => {
|
|
3349
|
+
const resolveAndSetLayout = async () => {
|
|
3350
|
+
const hasMeaningfulDataContext = dataContext && Object.keys(dataContext).some(
|
|
3351
|
+
(key) => key !== "user" || Object.keys(dataContext["user"]).length > 0
|
|
3352
|
+
);
|
|
3353
|
+
if (state.layout && hasMeaningfulDataContext) {
|
|
3354
|
+
console.log(
|
|
3355
|
+
`[AutoUI resolveAndSetLayout] Calling core resolveBindings for layout ID: ${state.layout.id}. DataContext keys: ${Object.keys(dataContext).join(", ")}`
|
|
3356
|
+
);
|
|
3357
|
+
setIsResolvingBindings(true);
|
|
3358
|
+
setUiStatus("resolving_bindings");
|
|
3359
|
+
try {
|
|
3360
|
+
const correctedLayout = correctListBindingsRecursive(
|
|
3361
|
+
state.layout,
|
|
3362
|
+
dataContext
|
|
3363
|
+
);
|
|
3364
|
+
systemEvents.emit(
|
|
3365
|
+
createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
|
|
3366
|
+
layout: correctedLayout
|
|
3367
|
+
// Log corrected layout before resolving
|
|
3368
|
+
})
|
|
3369
|
+
);
|
|
3370
|
+
const resolved = await resolveBindings(correctedLayout, dataContext);
|
|
3371
|
+
setCurrentResolvedLayoutForRender(resolved);
|
|
3372
|
+
systemEvents.emit(
|
|
3373
|
+
createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
|
|
3374
|
+
originalLayout: correctedLayout,
|
|
3375
|
+
// Log corrected layout
|
|
3376
|
+
resolvedLayout: resolved
|
|
3377
|
+
})
|
|
3378
|
+
);
|
|
3379
|
+
} catch (err) {
|
|
3380
|
+
console.error("Error resolving bindings:", err);
|
|
3381
|
+
setUiStatus("error");
|
|
3382
|
+
setCurrentResolvedLayoutForRender(null);
|
|
3383
|
+
} finally {
|
|
3384
|
+
setIsResolvingBindings(false);
|
|
1207
3385
|
}
|
|
3386
|
+
} else {
|
|
3387
|
+
if (!state.layout)
|
|
3388
|
+
console.log(
|
|
3389
|
+
"[AutoUI] Skipping resolveBindings: state.layout is null/undefined"
|
|
3390
|
+
);
|
|
3391
|
+
if (!hasMeaningfulDataContext)
|
|
3392
|
+
console.log(
|
|
3393
|
+
"[AutoUI] Skipping resolveBindings: dataContext is not meaningfully populated yet"
|
|
3394
|
+
);
|
|
3395
|
+
setCurrentResolvedLayoutForRender(null);
|
|
3396
|
+
if (!state.loading && uiStatus !== "initializing" && !isResolvingBindings)
|
|
3397
|
+
setUiStatus("idle");
|
|
1208
3398
|
}
|
|
1209
|
-
return void 0;
|
|
1210
3399
|
};
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
3400
|
+
resolveAndSetLayout();
|
|
3401
|
+
}, [state.layout, dataContext]);
|
|
3402
|
+
React.useEffect(() => {
|
|
3403
|
+
const renderLayout = async () => {
|
|
3404
|
+
if (currentResolvedLayoutForRender && !isResolvingBindings) {
|
|
3405
|
+
setUiStatus("rendering");
|
|
3406
|
+
console.log(
|
|
3407
|
+
"[AutoUI Debug] Rendering with currentResolvedLayoutForRender:",
|
|
3408
|
+
JSON.stringify(currentResolvedLayoutForRender, null, 2)
|
|
3409
|
+
);
|
|
3410
|
+
try {
|
|
3411
|
+
systemEvents.emit(
|
|
3412
|
+
createSystemEvent("RENDER_START" /* RENDER_START */, {
|
|
3413
|
+
layout: currentResolvedLayoutForRender
|
|
3414
|
+
})
|
|
3415
|
+
);
|
|
3416
|
+
const rendered = await renderNode2(
|
|
3417
|
+
currentResolvedLayoutForRender,
|
|
3418
|
+
componentAdapter,
|
|
3419
|
+
processEvent
|
|
3420
|
+
);
|
|
3421
|
+
setRenderedNode(rendered);
|
|
3422
|
+
systemEvents.emit(
|
|
3423
|
+
createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
|
|
3424
|
+
layout: currentResolvedLayoutForRender,
|
|
3425
|
+
renderTimeMs: 0
|
|
3426
|
+
// Placeholder, actual timing would be more complex
|
|
3427
|
+
})
|
|
3428
|
+
);
|
|
3429
|
+
setUiStatus("idle");
|
|
3430
|
+
} catch (err) {
|
|
3431
|
+
console.error("Error rendering node:", err);
|
|
3432
|
+
setUiStatus("error");
|
|
3433
|
+
}
|
|
3434
|
+
} else if (!currentResolvedLayoutForRender && !state.loading && !isResolvingBindings && uiStatus !== "initializing") {
|
|
3435
|
+
setRenderedNode(null);
|
|
3436
|
+
setUiStatus("idle");
|
|
3437
|
+
}
|
|
3438
|
+
};
|
|
3439
|
+
renderLayout();
|
|
3440
|
+
}, [
|
|
3441
|
+
currentResolvedLayoutForRender,
|
|
3442
|
+
componentAdapter,
|
|
3443
|
+
processEvent,
|
|
3444
|
+
isResolvingBindings,
|
|
3445
|
+
state.loading
|
|
3446
|
+
]);
|
|
3447
|
+
React.useEffect(() => {
|
|
3448
|
+
if (uiStatus !== "error") {
|
|
3449
|
+
setUiStatus("initializing");
|
|
1216
3450
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
3451
|
+
}, []);
|
|
3452
|
+
React.useEffect(() => {
|
|
3453
|
+
if (uiStatus === "initializing") {
|
|
3454
|
+
if (state.loading) ; else if (state.layout && !isResolvingBindings) ; else if (!state.layout && !state.loading && !isResolvingBindings) {
|
|
3455
|
+
if (state.error) {
|
|
3456
|
+
setUiStatus("error");
|
|
3457
|
+
} else {
|
|
3458
|
+
setUiStatus("idle");
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
1222
3461
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1241
|
-
|
|
3462
|
+
}, [state.loading, state.layout, state.error, uiStatus, isResolvingBindings]);
|
|
3463
|
+
React.useEffect(() => {
|
|
3464
|
+
if (!debugMode)
|
|
3465
|
+
return;
|
|
3466
|
+
const pipelineState = {
|
|
3467
|
+
uiStatus,
|
|
3468
|
+
hasLayout: !!state.layout,
|
|
3469
|
+
layoutId: state.layout?.id || null,
|
|
3470
|
+
hasResolvedLayout: !!currentResolvedLayoutForRender,
|
|
3471
|
+
resolvedLayoutId: currentResolvedLayoutForRender?.id || null,
|
|
3472
|
+
hasRenderedNode: !!renderedNode,
|
|
3473
|
+
isResolvingBindings,
|
|
3474
|
+
isLoading: state.loading,
|
|
3475
|
+
hasError: !!state.error,
|
|
3476
|
+
error: state.error || null,
|
|
3477
|
+
dataContextKeys: Object.keys(dataContext),
|
|
3478
|
+
timestamp: Date.now()
|
|
3479
|
+
};
|
|
3480
|
+
if (typeof window !== "undefined") {
|
|
3481
|
+
window.__autoui_pipeline_state = pipelineState;
|
|
3482
|
+
window.__autoui_pipeline_history = window.__autoui_pipeline_history || [];
|
|
3483
|
+
window.__autoui_pipeline_history.push(pipelineState);
|
|
3484
|
+
console.log("[PIPELINE_STATE]", JSON.stringify(pipelineState));
|
|
1242
3485
|
}
|
|
1243
|
-
}, [
|
|
3486
|
+
}, [
|
|
3487
|
+
debugMode,
|
|
3488
|
+
uiStatus,
|
|
3489
|
+
state.layout,
|
|
3490
|
+
state.loading,
|
|
3491
|
+
state.error,
|
|
3492
|
+
currentResolvedLayoutForRender,
|
|
3493
|
+
renderedNode,
|
|
3494
|
+
isResolvingBindings,
|
|
3495
|
+
dataContext
|
|
3496
|
+
]);
|
|
3497
|
+
if (!componentsAvailable) {
|
|
3498
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 text-red-700 rounded", children: [
|
|
3499
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Component Library Not Found" }),
|
|
3500
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-line", children: getMissingComponentsMessage() })
|
|
3501
|
+
] });
|
|
3502
|
+
}
|
|
3503
|
+
const showShimmer = (uiStatus === "initializing" || state.loading || isResolvingBindings) && !state.error;
|
|
1244
3504
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1245
3505
|
"div",
|
|
1246
3506
|
{
|
|
1247
3507
|
className: `autoui-root ${integration.className || ""}`,
|
|
1248
3508
|
id: integration.id,
|
|
1249
|
-
"data-mode": integration.mode,
|
|
1250
|
-
"data-scope": scope?.type || "full",
|
|
1251
3509
|
children: [
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
] }) })
|
|
1258
|
-
) : (
|
|
1259
|
-
// Render the resolved layout
|
|
1260
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderNode2(resolvedLayout, componentAdapter) })
|
|
1261
|
-
),
|
|
3510
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
3511
|
+
"Current Status: ",
|
|
3512
|
+
uiStatus
|
|
3513
|
+
] }),
|
|
3514
|
+
uiStatus === "idle" && !isResolvingBindings && renderedNode && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderedNode }),
|
|
1262
3515
|
state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error", children: [
|
|
1263
|
-
|
|
1264
|
-
|
|
3516
|
+
"Error: ",
|
|
3517
|
+
state.error
|
|
3518
|
+
] }),
|
|
3519
|
+
showShimmer && state.layout && // Show shimmer if we have a layout to base it on
|
|
3520
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-loading", children: renderShimmer(state.layout, componentAdapter) }),
|
|
3521
|
+
showShimmer && !state.layout && // Fallback shimmer if no layout yet
|
|
3522
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-loading p-4", children: [
|
|
3523
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-20 bg-gray-200 animate-pulse rounded mb-4" }),
|
|
3524
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-40 bg-gray-200 animate-pulse rounded" })
|
|
1265
3525
|
] })
|
|
1266
3526
|
]
|
|
1267
3527
|
}
|
|
1268
3528
|
);
|
|
1269
3529
|
};
|
|
3530
|
+
var uiEventType = zod.z.enum([
|
|
3531
|
+
"INIT",
|
|
3532
|
+
"CLICK",
|
|
3533
|
+
"CHANGE",
|
|
3534
|
+
"SUBMIT",
|
|
3535
|
+
"MOUSEOVER",
|
|
3536
|
+
"MOUSEOUT",
|
|
3537
|
+
"FOCUS",
|
|
3538
|
+
"BLUR"
|
|
3539
|
+
]);
|
|
3540
|
+
var uiEvent = zod.z.object({
|
|
3541
|
+
type: uiEventType,
|
|
3542
|
+
nodeId: zod.z.string(),
|
|
3543
|
+
timestamp: zod.z.number().nullable(),
|
|
3544
|
+
payload: zod.z.record(zod.z.unknown()).nullable()
|
|
3545
|
+
});
|
|
3546
|
+
zod.z.enum(["AI_RESPONSE", "ERROR"]);
|
|
3547
|
+
var runtimeRecord = zod.z.record(zod.z.unknown()).nullable();
|
|
3548
|
+
var uiSpecNode = zod.z.object({
|
|
3549
|
+
id: zod.z.string(),
|
|
3550
|
+
node_type: zod.z.string(),
|
|
3551
|
+
props: runtimeRecord,
|
|
3552
|
+
bindings: runtimeRecord,
|
|
3553
|
+
events: zod.z.record(
|
|
3554
|
+
zod.z.string(),
|
|
3555
|
+
zod.z.object({
|
|
3556
|
+
action: zod.z.string(),
|
|
3557
|
+
target: zod.z.string(),
|
|
3558
|
+
payload: runtimeRecord
|
|
3559
|
+
})
|
|
3560
|
+
).nullable(),
|
|
3561
|
+
children: zod.z.lazy(() => zod.z.array(uiSpecNode)).nullable()
|
|
3562
|
+
});
|
|
3563
|
+
zod.z.discriminatedUnion("type", [
|
|
3564
|
+
zod.z.object({
|
|
3565
|
+
type: zod.z.literal("UI_EVENT"),
|
|
3566
|
+
event: uiEvent
|
|
3567
|
+
}),
|
|
3568
|
+
zod.z.object({
|
|
3569
|
+
type: zod.z.literal("AI_RESPONSE"),
|
|
3570
|
+
node: uiSpecNode
|
|
3571
|
+
}),
|
|
3572
|
+
zod.z.object({
|
|
3573
|
+
type: zod.z.literal("PARTIAL_UPDATE"),
|
|
3574
|
+
nodeId: zod.z.string(),
|
|
3575
|
+
node: uiSpecNode
|
|
3576
|
+
}),
|
|
3577
|
+
zod.z.object({
|
|
3578
|
+
type: zod.z.literal("ADD_NODE"),
|
|
3579
|
+
parentId: zod.z.string(),
|
|
3580
|
+
node: uiSpecNode,
|
|
3581
|
+
index: zod.z.number().nullable()
|
|
3582
|
+
}),
|
|
3583
|
+
zod.z.object({
|
|
3584
|
+
type: zod.z.literal("REMOVE_NODE"),
|
|
3585
|
+
nodeId: zod.z.string()
|
|
3586
|
+
}),
|
|
3587
|
+
zod.z.object({
|
|
3588
|
+
type: zod.z.literal("ERROR"),
|
|
3589
|
+
message: zod.z.string()
|
|
3590
|
+
}),
|
|
3591
|
+
zod.z.object({
|
|
3592
|
+
type: zod.z.literal("LOADING"),
|
|
3593
|
+
isLoading: zod.z.boolean()
|
|
3594
|
+
}),
|
|
3595
|
+
zod.z.object({
|
|
3596
|
+
type: zod.z.literal("SET_DATA_CONTEXT"),
|
|
3597
|
+
payload: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
3598
|
+
})
|
|
3599
|
+
]);
|
|
3600
|
+
zod.z.object({
|
|
3601
|
+
layout: uiSpecNode.nullable(),
|
|
3602
|
+
loading: zod.z.boolean(),
|
|
3603
|
+
history: zod.z.array(uiEvent),
|
|
3604
|
+
error: zod.z.string().nullable(),
|
|
3605
|
+
dataContext: zod.z.record(zod.z.string(), zod.z.unknown())
|
|
3606
|
+
});
|
|
3607
|
+
zod.z.object({
|
|
3608
|
+
schema: zod.z.record(zod.z.unknown()),
|
|
3609
|
+
goal: zod.z.string(),
|
|
3610
|
+
history: zod.z.array(uiEvent).nullable(),
|
|
3611
|
+
userContext: zod.z.record(zod.z.unknown()).nullable().optional()
|
|
3612
|
+
});
|
|
1270
3613
|
|
|
1271
3614
|
// src/adapters/schema/drizzle.ts
|
|
1272
3615
|
var DrizzleAdapter = class {
|
|
@@ -1314,26 +3657,26 @@ var DrizzleAdapter = class {
|
|
|
1314
3657
|
*/
|
|
1315
3658
|
mapDataType(drizzleType) {
|
|
1316
3659
|
const typeMap = {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
3660
|
+
serial: "integer",
|
|
3661
|
+
integer: "integer",
|
|
3662
|
+
int: "integer",
|
|
3663
|
+
bigint: "integer",
|
|
3664
|
+
text: "string",
|
|
3665
|
+
varchar: "string",
|
|
3666
|
+
char: "string",
|
|
3667
|
+
boolean: "boolean",
|
|
3668
|
+
bool: "boolean",
|
|
3669
|
+
timestamp: "datetime",
|
|
3670
|
+
timestamptz: "datetime",
|
|
3671
|
+
date: "date",
|
|
3672
|
+
time: "time",
|
|
3673
|
+
json: "object",
|
|
3674
|
+
jsonb: "object",
|
|
3675
|
+
real: "number",
|
|
3676
|
+
float: "number",
|
|
3677
|
+
double: "number",
|
|
3678
|
+
numeric: "number",
|
|
3679
|
+
decimal: "number"
|
|
1337
3680
|
};
|
|
1338
3681
|
return typeMap[drizzleType.toLowerCase()] || "string";
|
|
1339
3682
|
}
|
|
@@ -1376,22 +3719,48 @@ function createSchemaAdapter(options) {
|
|
|
1376
3719
|
case "custom":
|
|
1377
3720
|
return options.adapter;
|
|
1378
3721
|
default:
|
|
1379
|
-
throw new Error(
|
|
3722
|
+
throw new Error(
|
|
3723
|
+
`Unsupported schema adapter type: ${options.type}`
|
|
3724
|
+
);
|
|
1380
3725
|
}
|
|
1381
3726
|
}
|
|
1382
3727
|
|
|
3728
|
+
// src/ai-utils.ts
|
|
3729
|
+
var generateComponent = async (prompt) => {
|
|
3730
|
+
console.warn(
|
|
3731
|
+
"generateComponent is a placeholder and will be implemented in a future version"
|
|
3732
|
+
);
|
|
3733
|
+
return `<div>Generated Component for: ${prompt}</div>`;
|
|
3734
|
+
};
|
|
3735
|
+
var generateUIDescription = async (prompt) => {
|
|
3736
|
+
console.warn(
|
|
3737
|
+
"generateUIDescription is a placeholder and will be implemented in a future version"
|
|
3738
|
+
);
|
|
3739
|
+
return `Description for ${prompt}`;
|
|
3740
|
+
};
|
|
3741
|
+
var generateUIComponent = async (prompt) => {
|
|
3742
|
+
console.warn(
|
|
3743
|
+
"generateUIComponent is a placeholder and will be implemented in a future version"
|
|
3744
|
+
);
|
|
3745
|
+
return `<div>Generated UI Component for: ${prompt}</div>`;
|
|
3746
|
+
};
|
|
3747
|
+
|
|
1383
3748
|
exports.ActionRouter = ActionRouter;
|
|
1384
3749
|
exports.ActionType = ActionType;
|
|
1385
3750
|
exports.AutoUI = AutoUI;
|
|
1386
3751
|
exports.DrizzleAdapter = DrizzleAdapter;
|
|
1387
3752
|
exports.SystemEventType = SystemEventType;
|
|
1388
|
-
exports.
|
|
3753
|
+
exports.componentType = componentType;
|
|
1389
3754
|
exports.createEventHook = createEventHook;
|
|
1390
3755
|
exports.createSchemaAdapter = createSchemaAdapter;
|
|
1391
3756
|
exports.createSystemEvent = createSystemEvent;
|
|
3757
|
+
exports.generateComponent = generateComponent;
|
|
3758
|
+
exports.generateUIComponent = generateUIComponent;
|
|
3759
|
+
exports.generateUIDescription = generateUIDescription;
|
|
3760
|
+
exports.openAIUISpec = openAIUISpec;
|
|
1392
3761
|
exports.systemEvents = systemEvents;
|
|
1393
3762
|
exports.uiEvent = uiEvent;
|
|
1394
3763
|
exports.uiEventType = uiEventType;
|
|
1395
3764
|
exports.uiSpecNode = uiSpecNode;
|
|
1396
|
-
//# sourceMappingURL=
|
|
3765
|
+
//# sourceMappingURL=out.js.map
|
|
1397
3766
|
//# sourceMappingURL=index.js.map
|