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/dist/index.js CHANGED
@@ -1,26 +1,142 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
4
- var zod = require('zod');
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 } : void 0,
12
- bindings: node.bindings ? { ...node.bindings } : void 0,
13
- events: node.events ? { ...node.events } : void 0,
14
- children: node.children?.map((child) => cloneNode(child))
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) return void 0;
19
- if (tree.id === nodeId) return tree;
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) return 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) return 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) return result;
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?.map(
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) return 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) return result;
216
+ if (!parent)
217
+ return result;
102
218
  return updateNodeById(result, parent.id, (node) => ({
103
219
  ...node,
104
- children: node.children?.filter((child) => child.id !== nodeId)
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: void 0
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: void 0
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: void 0
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: void 0
265
+ error: null
146
266
  };
147
267
  }
148
268
  case "ADD_NODE": {
149
269
  if (!state.layout) {
150
- return state;
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: void 0
285
+ error: null
162
286
  };
163
287
  }
164
288
  case "REMOVE_NODE": {
165
289
  if (!state.layout) {
166
- return state;
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
- layout: removeNodeById(state.layout, action.nodeId),
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 "ERROR": {
325
+ case "SET_DATA_CONTEXT": {
182
326
  return {
183
327
  ...state,
184
- error: action.message,
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
- history: []
338
+ error: null,
339
+ history: [],
340
+ dataContext: {}
195
341
  };
196
342
 
197
- // src/core/action-router.ts
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/env.ts
497
- ({
498
- MOCK_PLANNER: undefined?.VITE_MOCK_PLANNER || "1",
499
- NODE_ENV: undefined?.MODE || "development"
500
- // Add other environment variables as needed
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
- function buildPrompt(input, targetNodeId, customPrompt) {
505
- const { schema, goal, history, userContext } = input;
506
- const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
507
- return `Table: ${tableName}
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
- UI Guidance:
538
- 1. Create a focused interface that directly addresses the goal
539
- 2. Use appropriate UI patterns (lists, forms, details, etc.)
540
- 3. Include navigation between related views when needed
541
- 4. Keep the interface simple and intuitive
542
- 5. Bind to schema data where appropriate
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: "text-1",
556
- type: "Text",
557
- props: { text: "This is a mock UI for testing" }
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
- // src/core/state.ts
565
- var useChat = (config) => ({
566
- append: async (message) => {
567
- },
568
- data: { content: "{}" },
569
- isLoading: false,
570
- error: null,
571
- stop: () => {
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
- function useUIStateEngine({
575
- schema,
576
- goal,
577
- userContext,
578
- mockMode = false,
579
- planningConfig = {},
580
- router = createDefaultRouter(),
581
- dataContext = {},
582
- enablePartialUpdates = false
583
- }) {
584
- const [state, dispatch] = react.useReducer(uiReducer, initialState);
585
- const { append, data, isLoading, error, stop } = useChat();
586
- const handleEvent = react.useCallback((event) => {
587
- dispatch({ type: "UI_EVENT", event });
588
- if (enablePartialUpdates) {
589
- const route = router.resolveRoute(
590
- event,
591
- schema,
592
- state.layout,
593
- dataContext,
594
- goal,
595
- userContext
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
- return;
639
- }
640
- }
641
- const input = {
642
- schema,
643
- goal,
644
- history: [...state.history, event],
645
- userContext
686
+ ]
646
687
  };
647
- if (mockMode) {
648
- const node = mockPlanner();
649
- dispatch({ type: "AI_RESPONSE", node });
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 prompt = buildPrompt(input);
652
- append({
653
- content: prompt,
654
- role: "user"
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
- }, [append, goal, schema, state.history, state.layout, stop, userContext, router, mockMode, dataContext, enablePartialUpdates]);
658
- react.useEffect(() => {
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
- try {
661
- systemEvents.emit(
662
- createSystemEvent("PLAN_RESPONSE_CHUNK" /* PLAN_RESPONSE_CHUNK */, {
663
- chunk: data.content,
664
- isComplete: true
665
- })
666
- );
667
- const jsonMatch = data.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/) || [null, data.content];
668
- const jsonStr = jsonMatch[1].trim();
669
- const parsedJson = JSON.parse(jsonStr);
670
- const validatedNode = uiSpecNode.parse(parsedJson);
671
- const routeInfoStr = sessionStorage.getItem("currentRoute");
672
- if (routeInfoStr && enablePartialUpdates) {
673
- try {
674
- const routeInfo = JSON.parse(routeInfoStr);
675
- switch (routeInfo.actionType) {
676
- case "FULL_REFRESH" /* FULL_REFRESH */:
677
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
678
- break;
679
- case "UPDATE_NODE" /* UPDATE_NODE */:
680
- case "SHOW_DETAIL" /* SHOW_DETAIL */:
681
- case "HIDE_DETAIL" /* HIDE_DETAIL */:
682
- case "TOGGLE_STATE" /* TOGGLE_STATE */:
683
- case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
684
- case "UPDATE_FORM" /* UPDATE_FORM */:
685
- case "NAVIGATE" /* NAVIGATE */:
686
- dispatch({
687
- type: "PARTIAL_UPDATE",
688
- nodeId: routeInfo.targetNodeId,
689
- node: validatedNode
690
- });
691
- break;
692
- default:
693
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
694
- }
695
- sessionStorage.removeItem("currentRoute");
696
- } catch (e) {
697
- console.error("Error parsing route info:", e);
698
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
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
- } else {
701
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
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
- systemEvents.emit(
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
- }, [data.content, error, isLoading, enablePartialUpdates]);
724
- react.useEffect(() => {
725
- const input = {
726
- schema,
727
- goal,
728
- history: [],
729
- userContext
730
- };
731
- if (mockMode) {
732
- const node = mockPlanner();
733
- dispatch({ type: "AI_RESPONSE", node });
734
- } else {
735
- const prompt = buildPrompt(input);
736
- append({
737
- content: prompt,
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
- }, [append, goal, schema, userContext, mockMode]);
742
- return {
743
- state,
744
- dispatch,
745
- handleEvent
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: `w-full ${props.className || ""}`, style: props.style, children: props.children });
762
- var Header = ({ title }) => /* @__PURE__ */ jsxRuntime.jsx("header", { className: "py-4 px-6 border-b mb-4", children: /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl font-semibold", children: title }) });
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) return null;
785
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full border rounded-lg p-6 space-y-4", children: [
786
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
787
- title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium", children: title }),
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
- if (field.type === "heading") {
792
- return /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-semibold", children: data?.[field.key] }, field.key);
793
- }
794
- if (field.type === "content") {
795
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-gray-700", children: data?.[field.key] }, field.key);
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 createEventHandler = (node, eventName) => {
805
- const eventConfig = node.events?.[eventName];
806
- if (!eventConfig) return void 0;
807
- return () => {
808
- console.log(`Event triggered: ${eventName} on node ${node.id}`, {
809
- action: eventConfig.action,
810
- target: eventConfig.target,
811
- payload: eventConfig.payload
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) => /* @__PURE__ */ jsxRuntime.jsx(Container, { style: node.props?.style, className: node.props?.className, children: node.children?.map((child) => renderNode(child)) }),
817
- Header: (node) => /* @__PURE__ */ jsxRuntime.jsx(Header, { title: node.props?.title || "Untitled" }),
818
- Button: (node) => /* @__PURE__ */ jsxRuntime.jsx(
819
- Button,
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
- variant: node.props?.variant,
822
- onClick: createEventHandler(node, "onClick"),
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
- ListView: (node) => /* @__PURE__ */ jsxRuntime.jsx(
827
- Table,
2319
+ Button: (node, processEvent) => /* @__PURE__ */ jsxRuntime.jsx(
2320
+ Button,
828
2321
  {
829
- items: node.bindings?.items || [],
830
- fields: node.bindings?.fields || [],
831
- selectable: node.props?.selectable,
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
- Detail: (node) => /* @__PURE__ */ jsxRuntime.jsx(
836
- Detail,
837
- {
838
- data: node.bindings?.data,
839
- fields: node.bindings?.fields || [],
840
- title: node.props?.title,
841
- visible: node.props?.visible !== false,
842
- onBack: createEventHandler(node, "onBack")
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 Component = adapterMap[node.type];
848
- if (Component) {
849
- return Component(node);
850
- }
851
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border border-red-300 rounded", children: [
852
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-red-500", children: [
853
- "Unsupported component: ",
854
- node.type
855
- ] }),
856
- node.children?.map((child) => renderNode(child))
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
- async function renderNode2(node, adapter = "shadcn") {
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.type) {
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) break;
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) break;
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
- scope = {},
1102
- enablePartialUpdates = false
3154
+ enablePartialUpdates = true,
3155
+ apiKey
1103
3156
  }) => {
1104
- const [schemaAdapterInstance, setSchemaAdapterInstance] = react.useState(null);
1105
- const [dataContext, setDataContext] = react.useState({});
1106
- const effectiveSchema = schema;
1107
- const scopedGoal = goal;
1108
- react.useEffect(() => {
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) return;
3181
+ if (!hooks)
3182
+ return;
1113
3183
  hooks.forEach((hook) => {
1114
- const unregister = systemEvents.on(eventType, hook);
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).forEach((eventType) => {
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
- react.useEffect(() => {
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
- enablePartialUpdates
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 = react.useRef(new EventManager());
1165
- react.useEffect(() => {
1166
- if (!eventHooks) return;
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("all", async (ctx) => {
1170
- for (const hook of eventHooks.all || []) {
1171
- await hook(ctx);
1172
- if (ctx.isPropagationStopped()) break;
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) return;
1179
- const unregister = eventManagerRef.current.register([type], async (ctx) => {
1180
- for (const hook of hooks) {
1181
- await hook(ctx);
1182
- if (ctx.isPropagationStopped()) break;
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
- react.useCallback(async (event) => {
1192
- const shouldProceed = await eventManagerRef.current.processEvent(event);
1193
- if (onEvent) {
1194
- onEvent(event);
1195
- }
1196
- if (!shouldProceed) {
1197
- console.info("Event processing was prevented by hooks", event);
1198
- return;
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
- const findNodeById2 = (node, id) => {
1201
- if (!node) return void 0;
1202
- if (node.id === id) return node;
1203
- if (node.children) {
1204
- for (const child of node.children) {
1205
- const found = findNodeById2(child, id);
1206
- if (found) return found;
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
- const sourceNode = findNodeById2(state.layout, event.nodeId);
1212
- if (!sourceNode) {
1213
- console.warn(`Node not found for event: ${event.nodeId}`);
1214
- handleEvent(event);
1215
- return;
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
- const eventConfig = sourceNode.events?.[event.type];
1218
- if (!eventConfig) {
1219
- console.warn(`No event config found for ${event.type} on node ${event.nodeId}`);
1220
- handleEvent(event);
1221
- return;
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
- const newContext = executeAction(
1224
- eventConfig.action,
1225
- eventConfig.target,
1226
- {
1227
- ...eventConfig.payload,
1228
- ...event.payload
1229
- },
1230
- dataContext,
1231
- state.layout
1232
- );
1233
- setDataContext(newContext);
1234
- handleEvent(event);
1235
- }, [dataContext, handleEvent, onEvent, state.layout]);
1236
- const [resolvedLayout, setResolvedLayout] = react.useState(void 0);
1237
- react.useEffect(() => {
1238
- if (state.layout) {
1239
- resolveBindings(state.layout, dataContext).then((resolved) => setResolvedLayout(resolved)).catch((err) => console.error("Error resolving bindings:", err));
1240
- } else {
1241
- setResolvedLayout(void 0);
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
- }, [state.layout, dataContext]);
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
- state.loading || !resolvedLayout ? (
1253
- // Render shimmer loading state
1254
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-loading", children: state.layout ? renderShimmer(state.layout, componentAdapter) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-shimmer-container", children: [
1255
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-shimmer-header" }),
1256
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-shimmer-content" })
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
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-title", children: "Error generating UI" }),
1264
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message", children: state.error })
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
- "serial": "integer",
1318
- "integer": "integer",
1319
- "int": "integer",
1320
- "bigint": "integer",
1321
- "text": "string",
1322
- "varchar": "string",
1323
- "char": "string",
1324
- "boolean": "boolean",
1325
- "bool": "boolean",
1326
- "timestamp": "datetime",
1327
- "timestamptz": "datetime",
1328
- "date": "date",
1329
- "time": "time",
1330
- "json": "object",
1331
- "jsonb": "object",
1332
- "real": "number",
1333
- "float": "number",
1334
- "double": "number",
1335
- "numeric": "number",
1336
- "decimal": "number"
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(`Unsupported schema adapter type: ${options.type}`);
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.createDefaultRouter = createDefaultRouter;
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=index.js.map
3765
+ //# sourceMappingURL=out.js.map
1397
3766
  //# sourceMappingURL=index.js.map