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