autoui-react 0.0.3-alpha → 0.0.5-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,17 +1,100 @@
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 React, { useState, useEffect, useRef, useCallback, useReducer } from 'react';
7
+ import { createOpenAI } from '@ai-sdk/openai';
8
+ import { generateObject } from 'ai';
2
9
  import { z } from 'zod';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
4
- import { generateText } from 'ai';
5
- import { openai } from '@ai-sdk/openai';
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
+ });
6
89
 
7
90
  // src/core/reducer.ts
8
91
  function cloneNode(node) {
9
92
  return {
10
93
  ...node,
11
- props: node.props ? { ...node.props } : void 0,
12
- bindings: node.bindings ? { ...node.bindings } : void 0,
13
- events: node.events ? { ...node.events } : void 0,
14
- children: node.children?.map((child) => cloneNode(child))
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
15
98
  };
16
99
  }
17
100
  function findNodeById(tree, nodeId) {
@@ -55,18 +138,14 @@ function updateNodeById(tree, nodeId, updater) {
55
138
  const parent = path[path.length - 2];
56
139
  const updatedParent = {
57
140
  ...parent,
58
- children: parent.children?.map(
141
+ children: parent.children ? parent.children.map(
59
142
  (child) => child.id === nodeId ? updatedNode : child
60
- )
143
+ ) : null
61
144
  };
62
145
  if (path.length === 2) {
63
146
  return updatedParent;
64
147
  }
65
- return updateNodeById(
66
- result,
67
- parent.id,
68
- () => updatedParent
69
- );
148
+ return updateNodeById(result, parent.id, () => updatedParent);
70
149
  }
71
150
  function replaceNodeById(tree, nodeId, newNode) {
72
151
  return updateNodeById(tree, nodeId, () => newNode);
@@ -108,7 +187,7 @@ function removeNodeById(tree, nodeId) {
108
187
  return result;
109
188
  return updateNodeById(result, parent.id, (node) => ({
110
189
  ...node,
111
- children: node.children?.filter((child) => child.id !== nodeId)
190
+ children: node.children ? node.children.filter((child) => child.id !== nodeId) : null
112
191
  }));
113
192
  }
114
193
  function uiReducer(state, action) {
@@ -125,7 +204,7 @@ function uiReducer(state, action) {
125
204
  ...state,
126
205
  layout: action.node,
127
206
  loading: false,
128
- error: void 0
207
+ error: null
129
208
  };
130
209
  }
131
210
  case "PARTIAL_UPDATE": {
@@ -134,7 +213,7 @@ function uiReducer(state, action) {
134
213
  ...state,
135
214
  layout: action.node,
136
215
  loading: false,
137
- error: void 0
216
+ error: null
138
217
  };
139
218
  }
140
219
  if (action.nodeId === "root" || action.nodeId === state.layout.id) {
@@ -142,19 +221,23 @@ function uiReducer(state, action) {
142
221
  ...state,
143
222
  layout: action.node,
144
223
  loading: false,
145
- error: void 0
224
+ error: null
146
225
  };
147
226
  }
148
227
  return {
149
228
  ...state,
150
229
  layout: replaceNodeById(state.layout, action.nodeId, action.node),
151
230
  loading: false,
152
- error: void 0
231
+ error: null
153
232
  };
154
233
  }
155
234
  case "ADD_NODE": {
156
235
  if (!state.layout) {
157
- return state;
236
+ return {
237
+ ...state,
238
+ error: "Cannot add node: Layout is empty.",
239
+ loading: false
240
+ };
158
241
  }
159
242
  return {
160
243
  ...state,
@@ -162,21 +245,41 @@ function uiReducer(state, action) {
162
245
  state.layout,
163
246
  action.parentId,
164
247
  action.node,
165
- action.index
248
+ action.index === null ? void 0 : action.index
166
249
  ),
167
250
  loading: false,
168
- error: void 0
251
+ error: null
169
252
  };
170
253
  }
171
254
  case "REMOVE_NODE": {
172
255
  if (!state.layout) {
173
- return state;
256
+ return {
257
+ ...state,
258
+ error: "Cannot remove node: Layout is empty.",
259
+ loading: false
260
+ };
261
+ }
262
+ try {
263
+ return {
264
+ ...state,
265
+ layout: removeNodeById(state.layout, action.nodeId),
266
+ loading: false,
267
+ error: null
268
+ };
269
+ } catch (e) {
270
+ const errorMessage = e instanceof Error ? e.message : "Failed to remove node.";
271
+ return {
272
+ ...state,
273
+ error: errorMessage,
274
+ loading: false
275
+ };
174
276
  }
277
+ }
278
+ case "ERROR": {
175
279
  return {
176
280
  ...state,
177
- layout: removeNodeById(state.layout, action.nodeId),
178
- loading: false,
179
- error: void 0
281
+ error: action.message,
282
+ loading: false
180
283
  };
181
284
  }
182
285
  case "LOADING": {
@@ -185,23 +288,84 @@ function uiReducer(state, action) {
185
288
  loading: action.isLoading
186
289
  };
187
290
  }
188
- case "ERROR": {
189
- return {
190
- ...state,
191
- error: action.message,
192
- loading: false
193
- };
194
- }
195
291
  default:
196
292
  return state;
197
293
  }
198
294
  }
199
295
  var initialState = {
200
- loading: true,
201
- history: []
296
+ layout: null,
297
+ loading: false,
298
+ history: [],
299
+ error: null
202
300
  };
203
301
 
204
302
  // src/core/action-router.ts
303
+ var UI_GUIDANCE_BASE = `
304
+ UI Guidance:
305
+ 1. Create a focused interface that directly addresses the goal
306
+ 2. Use appropriate UI patterns (lists, forms, details, etc.)
307
+ 3. Include navigation between related views when needed
308
+ 4. Keep the interface simple and intuitive
309
+ 5. Bind to schema data where appropriate
310
+ 6. Provide event handlers for user interactions - make sure to always include both action and target properties`;
311
+ var LIST_BINDING_GUIDANCE = `7. **CRITICAL:** For \`ListView\` or \`Table\` nodes, the \`data\` binding key **MUST** point to the *exact path* of the data *array* within the context.`;
312
+ 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" } }\`).`;
313
+ var COMMON_UI_GUIDANCE = UI_GUIDANCE_BASE + "\n" + // Add a newline separator
314
+ LIST_BINDING_GUIDANCE + // Add the specific list binding rule
315
+ "\n" + // Add a newline separator
316
+ LIST_BINDING_EXAMPLE;
317
+ function processTemplate(template, values) {
318
+ return template.replace(/\${(.*?)}/g, (match, key) => {
319
+ const trimmedKey = key.trim();
320
+ return trimmedKey in values ? String(values[trimmedKey]) : match;
321
+ });
322
+ }
323
+ function buildPrompt(input, promptTemplate, templateValues) {
324
+ const { schema, goal, history, userContext } = input;
325
+ const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
326
+ const schemaString = typeof tableSchema === "object" && tableSchema !== null ? JSON.stringify(tableSchema) : String(tableSchema);
327
+ return `Table: ${tableName}
328
+ Schema: ${schemaString}`;
329
+ }).join("\n\n");
330
+ const recentEvents = history && history.length > 0 ? history.slice(-5).map(
331
+ (event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
332
+ ).join("\n") : "No recent events";
333
+ const userContextSection = userContext ? `
334
+
335
+ User Context:
336
+ ${JSON.stringify(userContext)}` : "";
337
+ if (promptTemplate && templateValues) {
338
+ const fullTemplateValues = {
339
+ ...templateValues,
340
+ schemaInfo,
341
+ recentEvents,
342
+ userContextString: userContextSection.trim(),
343
+ // Use trimmed version
344
+ commonUIGuidance: COMMON_UI_GUIDANCE,
345
+ goal
346
+ // Ensure goal is always available to templates
347
+ };
348
+ return processTemplate(promptTemplate, fullTemplateValues);
349
+ }
350
+ 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";
351
+ return `
352
+ You are an expert UI generator.
353
+ Create a user interface that achieves the following goal: "${goal}".
354
+ ${interactionDescription}.
355
+
356
+ Available data schema:
357
+ ${schemaInfo}
358
+
359
+ Recent user interactions:
360
+ ${recentEvents}${userContextSection}
361
+
362
+ Generate a complete UI specification in JSON format that matches the following TypeScript type:
363
+ 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[]; };
364
+ ${COMMON_UI_GUIDANCE}
365
+
366
+ Respond ONLY with the JSON UI specification and no other text.
367
+ `;
368
+ }
205
369
  var ActionType = /* @__PURE__ */ ((ActionType2) => {
206
370
  ActionType2["FULL_REFRESH"] = "FULL_REFRESH";
207
371
  ActionType2["UPDATE_NODE"] = "UPDATE_NODE";
@@ -236,19 +400,39 @@ var ActionRouter = class {
236
400
  * @returns Route resolution or null if no match
237
401
  */
238
402
  resolveRoute(event, schema, layout, dataContext, goal, userContext) {
403
+ console.log(
404
+ `[ActionRouter Debug] resolveRoute called for event type: ${event.type}`
405
+ );
239
406
  const routes = this.routes[event.type] || [];
407
+ console.log(
408
+ `[ActionRouter Debug] Found ${routes.length} routes for ${event.type}`
409
+ );
240
410
  if (routes.length === 0) {
241
- return {
411
+ console.log(
412
+ `[ActionRouter Debug] No specific route found for ${event.type}, using default FULL_REFRESH.`
413
+ );
414
+ const defaultPlannerInput = {
415
+ schema,
416
+ goal,
417
+ history: [event],
418
+ userContext: userContext || null
419
+ };
420
+ const defaultPrompt = buildPrompt(
421
+ defaultPlannerInput,
422
+ void 0,
423
+ void 0
424
+ );
425
+ const defaultResolution = {
242
426
  actionType: "FULL_REFRESH" /* FULL_REFRESH */,
243
427
  targetNodeId: layout?.id || "root",
244
- plannerInput: {
245
- schema,
246
- goal,
247
- history: [event],
248
- userContext
249
- },
250
- prompt: `Generate a new UI for the goal: "${goal}". The user just triggered: ${event.type} on node ${event.nodeId}`
428
+ plannerInput: defaultPlannerInput,
429
+ prompt: defaultPrompt
251
430
  };
431
+ console.log(
432
+ "[ActionRouter Debug] Default Resolution:",
433
+ defaultResolution
434
+ );
435
+ return defaultResolution;
252
436
  }
253
437
  const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
254
438
  const nodeConfig = sourceNode?.events?.[event.type];
@@ -261,6 +445,7 @@ var ActionRouter = class {
261
445
  if (!matchingRoute) {
262
446
  matchingRoute = routes[0];
263
447
  }
448
+ console.log("[ActionRouter Debug] Matching Route Config:", matchingRoute);
264
449
  const targetNodeId = nodeConfig?.target || matchingRoute.targetNodeId || event.nodeId;
265
450
  const additionalContext = {};
266
451
  if (matchingRoute.contextKeys) {
@@ -294,23 +479,35 @@ var ActionRouter = class {
294
479
  ...additionalContext
295
480
  }
296
481
  };
297
- const prompt = this.processTemplate(
482
+ const templateValues = {
483
+ goal,
484
+ eventType: event.type,
485
+ nodeId: event.nodeId,
486
+ targetNodeId,
487
+ actionType: matchingRoute.actionType,
488
+ ...userContext || {},
489
+ // Spread the original userContext (passed to resolveRoute)
490
+ ...additionalContext
491
+ // Spread additionalContext afterwards (can override userContext keys)
492
+ };
493
+ console.log("[ActionRouter Debug] Template Values:", templateValues);
494
+ const finalPrompt = buildPrompt(
495
+ plannerInput2,
298
496
  matchingRoute.promptTemplate,
299
- {
300
- goal,
301
- eventType: event.type,
302
- nodeId: event.nodeId,
303
- targetNodeId,
304
- actionType: matchingRoute.actionType,
305
- ...additionalContext
306
- }
497
+ // Pass template if it exists (can be undefined)
498
+ templateValues
499
+ // Pass templateValues (used only if promptTemplate exists)
307
500
  );
308
- return {
501
+ console.log("[ActionRouter Debug] Generated Prompt:", finalPrompt);
502
+ const finalResolution = {
309
503
  actionType: matchingRoute.actionType,
310
504
  targetNodeId,
311
505
  plannerInput: plannerInput2,
312
- prompt
506
+ prompt: finalPrompt
507
+ // Use the generated prompt
313
508
  };
509
+ console.log("[ActionRouter Debug] Final Resolution:", finalResolution);
510
+ return finalResolution;
314
511
  }
315
512
  /**
316
513
  * Process a prompt template with variables
@@ -328,8 +525,11 @@ function createDefaultRouter() {
328
525
  const router = new ActionRouter();
329
526
  router.registerRoute("CLICK", {
330
527
  actionType: "FULL_REFRESH" /* FULL_REFRESH */,
331
- targetNodeId: "root",
332
- promptTemplate: 'Generate a new UI for the goal: "${goal}". The user just clicked on node ${nodeId}'
528
+ targetNodeId: "root"
529
+ });
530
+ router.registerRoute("INIT", {
531
+ actionType: "FULL_REFRESH" /* FULL_REFRESH */,
532
+ targetNodeId: "root"
333
533
  });
334
534
  router.registerRoute("CLICK", {
335
535
  actionType: "SHOW_DETAIL" /* SHOW_DETAIL */,
@@ -359,7 +559,31 @@ function createDefaultRouter() {
359
559
  });
360
560
  return router;
361
561
  }
562
+ var componentType = z.enum([
563
+ // Layout components
564
+ "Container",
565
+ "Card",
566
+ "Header",
567
+ // Input components
568
+ "Button",
569
+ "Input",
570
+ "Select",
571
+ "Textarea",
572
+ "Checkbox",
573
+ "RadioGroup",
574
+ // Data display components
575
+ "ListView",
576
+ "Detail",
577
+ "Tabs",
578
+ "Dialog",
579
+ // Typography
580
+ "Heading",
581
+ "Text"
582
+ ]);
583
+
584
+ // src/schema/ui.ts
362
585
  var uiEventType = z.enum([
586
+ "INIT",
363
587
  "CLICK",
364
588
  "CHANGE",
365
589
  "SUBMIT",
@@ -371,27 +595,62 @@ var uiEventType = z.enum([
371
595
  var uiEvent = z.object({
372
596
  type: uiEventType,
373
597
  nodeId: z.string(),
374
- timestamp: z.number().optional(),
375
- payload: z.record(z.any()).optional()
598
+ timestamp: z.number().nullable(),
599
+ payload: z.record(z.unknown()).nullable()
376
600
  });
377
- z.enum([
378
- "AI_RESPONSE",
379
- "ERROR"
380
- ]);
381
- var uiSpecNode = z.lazy(() => z.object({
601
+ z.enum(["AI_RESPONSE", "ERROR"]);
602
+ var runtimeRecord = z.record(z.any()).nullable();
603
+ var openAISimplifiedValue = z.string().nullable();
604
+ var openAIRecordSimplifiedNullable = z.record(openAISimplifiedValue).nullable();
605
+ var openAIEventPayloadSimplifiedNullable = z.record(openAISimplifiedValue).nullable();
606
+ var openAIBaseNode = z.object({
607
+ id: z.string().describe("Unique identifier for the UI node."),
608
+ node_type: componentType.describe(
609
+ "The type of UI component (e.g., Container, Text, Button, ListView)."
610
+ ),
611
+ props: openAIRecordSimplifiedNullable.describe(
612
+ 'Component-specific properties (attributes). Values should be strings or null. E.g., for Header use { "title": "My Title" }; for Text use { "text": "My Text" }.'
613
+ ),
614
+ bindings: openAIRecordSimplifiedNullable.describe(
615
+ 'Data bindings map context paths to component props. Values are paths (e.g., "user.name") or templates (e.g., "{{item.title}}"). **CRITICAL for ListView/Table:** the `data` key MUST point to the *exact array path* (e.g., { "data": "tasks.data" } or { "data": "userList" }), NOT the parent object.'
616
+ ),
617
+ events: z.record(
618
+ z.string(),
619
+ z.object({
620
+ action: z.string(),
621
+ target: z.string(),
622
+ payload: openAIEventPayloadSimplifiedNullable
623
+ })
624
+ ).nullable(),
625
+ // Entire events object is nullable
626
+ children: z.null()
627
+ // Base children are null. When extended, it will be an array or null.
628
+ });
629
+ var openAINodeL4 = openAIBaseNode;
630
+ var openAINodeL3 = openAIBaseNode.extend({
631
+ children: z.array(openAINodeL4).nullable()
632
+ });
633
+ var openAINodeL2 = openAIBaseNode.extend({
634
+ children: z.array(openAINodeL3).nullable()
635
+ });
636
+ var openAIUISpec = openAIBaseNode.extend({
637
+ children: z.array(openAINodeL2).nullable()
638
+ });
639
+ var uiSpecNode = z.object({
382
640
  id: z.string(),
383
- type: z.string(),
384
- // e.g., "ListView", "Button", "TextField"
385
- props: z.record(z.any()).optional(),
386
- bindings: z.record(z.any()).optional(),
387
- // Data bindings
388
- events: z.record(z.string(), z.object({
389
- action: z.string(),
390
- target: z.string().optional(),
391
- payload: z.record(z.any()).optional()
392
- })).optional(),
393
- children: z.array(uiSpecNode).optional()
394
- }));
641
+ node_type: z.string(),
642
+ props: runtimeRecord,
643
+ bindings: runtimeRecord,
644
+ events: z.record(
645
+ z.string(),
646
+ z.object({
647
+ action: z.string(),
648
+ target: z.string(),
649
+ payload: runtimeRecord
650
+ })
651
+ ).nullable(),
652
+ children: z.lazy(() => z.array(uiSpecNode)).nullable()
653
+ });
395
654
  z.discriminatedUnion("type", [
396
655
  z.object({
397
656
  type: z.literal("UI_EVENT"),
@@ -410,7 +669,7 @@ z.discriminatedUnion("type", [
410
669
  type: z.literal("ADD_NODE"),
411
670
  parentId: z.string(),
412
671
  node: uiSpecNode,
413
- index: z.number().optional()
672
+ index: z.number().nullable()
414
673
  }),
415
674
  z.object({
416
675
  type: z.literal("REMOVE_NODE"),
@@ -426,16 +685,16 @@ z.discriminatedUnion("type", [
426
685
  })
427
686
  ]);
428
687
  z.object({
429
- layout: uiSpecNode.optional(),
688
+ layout: uiSpecNode.nullable(),
430
689
  loading: z.boolean(),
431
690
  history: z.array(uiEvent),
432
- error: z.string().optional()
691
+ error: z.string().nullable()
433
692
  });
434
693
  z.object({
435
694
  schema: z.record(z.unknown()),
436
695
  goal: z.string(),
437
- history: z.array(uiEvent).optional(),
438
- userContext: z.record(z.unknown()).optional()
696
+ history: z.array(uiEvent).nullable(),
697
+ userContext: z.record(z.unknown()).nullable().optional()
439
698
  });
440
699
 
441
700
  // src/core/system-events.ts
@@ -461,7 +720,7 @@ var SystemEventManager = class {
461
720
  }
462
721
  /**
463
722
  * Register a listener for a specific system event type
464
- *
723
+ *
465
724
  * @param eventType - The system event type to listen for
466
725
  * @param listener - The listener function
467
726
  * @returns Function to unregister the listener
@@ -481,7 +740,7 @@ var SystemEventManager = class {
481
740
  }
482
741
  /**
483
742
  * Emit a system event to all registered listeners
484
- *
743
+ *
485
744
  * @param event - The system event to emit
486
745
  */
487
746
  async emit(event) {
@@ -501,268 +760,917 @@ function createSystemEvent(type, data) {
501
760
  }
502
761
 
503
762
  // src/env.ts
504
- ({
505
- MOCK_PLANNER: import.meta.env?.VITE_MOCK_PLANNER || "1",
506
- NODE_ENV: import.meta.env?.MODE || "development"
507
- // Add other environment variables as needed
508
- });
763
+ var rawApiKeyFromEnv = process.env.VITE_OPENAI_API_KEY;
764
+ var env = {
765
+ MOCK_PLANNER: process.env.VITE_MOCK_PLANNER || "0",
766
+ // Simplified MOCK_PLANNER assignment
767
+ NODE_ENV: process.env.VITE_NODE_ENV || "production",
768
+ // Simplified NODE_ENV assignment
769
+ OPENAI_API_KEY: rawApiKeyFromEnv || ""
770
+ };
509
771
 
510
772
  // src/core/planner.ts
511
- function buildPrompt(input, targetNodeId, customPrompt) {
512
- const { schema, goal, history, userContext } = input;
513
- const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
514
- return `Table: ${tableName}
515
- Schema: ${JSON.stringify(tableSchema)}`;
516
- }).join("\n\n");
517
- const recentEvents = history?.slice(-5).map(
518
- (event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
519
- ).join("\n") || "No recent events";
520
- const userContextSection = userContext ? `
521
-
522
- User Context:
523
- ${JSON.stringify(userContext)}` : "";
524
- return `
525
- You are an expert UI generator.
526
- Create a user interface that achieves the following goal: "${goal}"
527
-
528
- Available data schema:
529
- ${schemaInfo}
530
-
531
- Recent user interactions:
532
- ${recentEvents}${userContextSection}
533
-
534
- Generate a complete UI specification in JSON format that matches the following TypeScript type:
535
- type UISpecNode = {
536
- id: string;
537
- type: string;
538
- props?: Record<string, any>;
539
- bindings?: Record<string, any>;
540
- events?: Record<string, { action: string; target?: string; payload?: Record<string, any>; }>;
541
- children?: UISpecNode[];
773
+ var getOpenAIClient = (apiKey) => {
774
+ return createOpenAI({
775
+ apiKey,
776
+ // Use the provided key directly
777
+ compatibility: "strict"
778
+ });
542
779
  };
543
-
544
- UI Guidance:
545
- 1. Create a focused interface that directly addresses the goal
546
- 2. Use appropriate UI patterns (lists, forms, details, etc.)
547
- 3. Include navigation between related views when needed
548
- 4. Keep the interface simple and intuitive
549
- 5. Bind to schema data where appropriate
550
- 6. Provide event handlers for user interactions
551
-
552
- Respond ONLY with the JSON UI specification and no other text.
553
- `;
554
- }
555
780
  function mockPlanner(input, targetNodeId, customPrompt) {
781
+ if (customPrompt) {
782
+ console.log("mockPlanner received customPrompt:", customPrompt);
783
+ }
784
+ const taskSchema = input.schema.tasks;
785
+ const taskData = taskSchema?.sampleData || [
786
+ {
787
+ id: "1",
788
+ title: "Example Task 1",
789
+ description: "This is a sample task",
790
+ status: "pending",
791
+ priority: "medium"
792
+ },
793
+ {
794
+ id: "2",
795
+ title: "Example Task 2",
796
+ description: "Another sample task",
797
+ status: "completed",
798
+ priority: "high"
799
+ }
800
+ ];
556
801
  const mockNode = {
557
802
  id: targetNodeId || "root",
558
- type: "Container",
559
- props: { title: "Mock UI" },
803
+ node_type: "Container",
804
+ props: {
805
+ className: "p-4 space-y-6"
806
+ },
807
+ bindings: null,
808
+ events: null,
560
809
  children: [
561
810
  {
562
- id: "text-1",
563
- type: "Text",
564
- props: { text: "This is a mock UI for testing" }
811
+ id: "header-1",
812
+ node_type: "Header",
813
+ props: {
814
+ title: "Task Management Dashboard",
815
+ className: "mb-4"
816
+ },
817
+ bindings: null,
818
+ events: null,
819
+ children: null
820
+ },
821
+ {
822
+ id: "main-content",
823
+ node_type: "Container",
824
+ props: {
825
+ className: "grid grid-cols-1 gap-6 md:grid-cols-3"
826
+ },
827
+ bindings: null,
828
+ events: null,
829
+ children: [
830
+ {
831
+ id: "tasks-container",
832
+ node_type: "Container",
833
+ props: {
834
+ className: "md:col-span-2"
835
+ },
836
+ bindings: null,
837
+ events: null,
838
+ children: [
839
+ {
840
+ id: "list-heading",
841
+ node_type: "Container",
842
+ props: {
843
+ className: "flex justify-between items-center mb-4"
844
+ },
845
+ bindings: null,
846
+ events: null,
847
+ children: [
848
+ {
849
+ id: "list-title",
850
+ node_type: "Header",
851
+ props: {
852
+ title: "Tasks",
853
+ className: "border-none p-0 m-0"
854
+ },
855
+ bindings: null,
856
+ events: null,
857
+ children: null
858
+ },
859
+ {
860
+ id: "add-task-button",
861
+ node_type: "Button",
862
+ props: {
863
+ label: "Add Task",
864
+ variant: "default"
865
+ },
866
+ bindings: null,
867
+ events: {
868
+ onClick: {
869
+ action: "ADD_TASK",
870
+ target: "tasks-container",
871
+ payload: {}
872
+ }
873
+ },
874
+ children: null
875
+ }
876
+ ]
877
+ },
878
+ {
879
+ id: "task-list",
880
+ node_type: "ListView",
881
+ props: {
882
+ selectable: "true"
883
+ },
884
+ bindings: {
885
+ data: "tasks.data",
886
+ fields: JSON.stringify([
887
+ { key: "id", label: "ID" },
888
+ { key: "title", label: "Title" },
889
+ { key: "status", label: "Status" },
890
+ { key: "priority", label: "Priority" }
891
+ ])
892
+ },
893
+ events: {
894
+ onSelect: {
895
+ action: "SELECT_TASK",
896
+ target: "task-detail",
897
+ payload: {
898
+ source: "task-list"
899
+ }
900
+ }
901
+ },
902
+ children: null
903
+ }
904
+ ]
905
+ },
906
+ {
907
+ id: "task-detail",
908
+ node_type: "Detail",
909
+ props: {
910
+ title: "Task Details",
911
+ visible: "true"
912
+ },
913
+ bindings: {
914
+ data: JSON.stringify(taskData[0]),
915
+ fields: JSON.stringify([
916
+ { key: "title", label: "Title", type: "heading" },
917
+ { key: "description", label: "Description", type: "content" },
918
+ { key: "status", label: "Status" },
919
+ { key: "priority", label: "Priority" },
920
+ { key: "dueDate", label: "Due Date" }
921
+ ])
922
+ },
923
+ events: {
924
+ onBack: {
925
+ action: "CLOSE_DETAIL",
926
+ target: "task-detail",
927
+ payload: {}
928
+ }
929
+ },
930
+ children: null
931
+ }
932
+ ]
565
933
  }
566
934
  ]
567
935
  };
568
936
  return mockNode;
569
937
  }
938
+ async function callPlannerLLM(input, openaiApiKey, routeResolution) {
939
+ await systemEvents.emit(
940
+ createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
941
+ );
942
+ if (env.MOCK_PLANNER === "1") {
943
+ console.warn(
944
+ `Using mock planner because MOCK_PLANNER environment variable is set to "1".`
945
+ );
946
+ return mockPlanner(input);
947
+ }
948
+ if (!openaiApiKey) {
949
+ console.warn(
950
+ `OpenAI API key was not provided to callPlannerLLM. Falling back to mock planner.`
951
+ );
952
+ return mockPlanner(input);
953
+ }
954
+ const startTime = Date.now();
955
+ const prompt = routeResolution?.prompt;
956
+ if (!prompt) {
957
+ throw new Error("ActionRouter did not provide a prompt to callPlannerLLM.");
958
+ }
959
+ await systemEvents.emit(
960
+ createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
961
+ );
962
+ try {
963
+ const { object: uiSpec } = await generateObject({
964
+ model: getOpenAIClient(openaiApiKey)("gpt-4o", {
965
+ structuredOutputs: true
966
+ }),
967
+ schema: openAIUISpec,
968
+ messages: [{ role: "user", content: prompt }],
969
+ temperature: 0.2,
970
+ maxTokens: 4e3
971
+ });
972
+ await systemEvents.emit(
973
+ createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
974
+ layout: uiSpec,
975
+ executionTimeMs: Date.now() - startTime
976
+ })
977
+ );
978
+ return uiSpec;
979
+ } catch (error) {
980
+ console.error("Error calling LLM planner:", error);
981
+ await systemEvents.emit(
982
+ createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
983
+ error: error instanceof Error ? error : new Error(String(error))
984
+ })
985
+ );
986
+ throw error;
987
+ }
988
+ }
989
+ async function processEvent(event, router, schema, layout, dataContext, goal, userContext, openaiApiKey) {
990
+ const routeResolution = await router.resolveRoute(
991
+ event,
992
+ schema,
993
+ layout || null,
994
+ dataContext,
995
+ goal,
996
+ userContext
997
+ );
998
+ if (!routeResolution) {
999
+ throw new Error(
1000
+ `No route found for event type: ${event.type}, node: ${event.nodeId}`
1001
+ );
1002
+ }
1003
+ if (routeResolution.actionType.toString() === "NoOp") {
1004
+ if (!layout)
1005
+ throw new Error("Layout is undefined and action is NoOp");
1006
+ return layout;
1007
+ }
1008
+ const plannerInputForLLM = routeResolution.plannerInput;
1009
+ const newLayout = await callPlannerLLM(
1010
+ plannerInputForLLM,
1011
+ openaiApiKey || "",
1012
+ routeResolution
1013
+ );
1014
+ return newLayout;
1015
+ }
570
1016
 
571
1017
  // src/core/state.ts
572
- var useChat = (config) => ({
573
- append: async (message) => {
574
- },
575
- data: { content: "{}" },
576
- isLoading: false,
577
- error: null,
578
- stop: () => {
579
- }
580
- });
581
1018
  function useUIStateEngine({
582
1019
  schema,
583
1020
  goal,
1021
+ openaiApiKey,
584
1022
  userContext,
585
1023
  mockMode = false,
586
- planningConfig = {},
1024
+ planningConfig,
587
1025
  router = createDefaultRouter(),
588
1026
  dataContext = {},
589
1027
  enablePartialUpdates = false
590
1028
  }) {
1029
+ if (userContext === null) {
1030
+ console.warn(
1031
+ "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."
1032
+ );
1033
+ }
591
1034
  const [state, dispatch] = useReducer(uiReducer, initialState);
592
- const { append, data, isLoading, error, stop } = useChat();
593
- const handleEvent = useCallback((event) => {
594
- dispatch({ type: "UI_EVENT", event });
595
- stop();
596
- if (enablePartialUpdates) {
597
- const route = router.resolveRoute(
598
- event,
599
- schema,
600
- state.layout,
601
- dataContext,
602
- goal,
603
- userContext
604
- );
605
- if (route) {
606
- console.log("Resolved route:", route);
607
- systemEvents.emit(
608
- createSystemEvent("PLAN_START" /* PLAN_START */, {
609
- plannerInput: route.plannerInput
610
- })
611
- );
612
- if (mockMode) {
613
- const node = mockPlanner(route.plannerInput, route.targetNodeId, route.prompt);
614
- switch (route.actionType) {
615
- case "FULL_REFRESH" /* FULL_REFRESH */:
616
- dispatch({ type: "AI_RESPONSE", node });
617
- break;
618
- case "UPDATE_NODE" /* UPDATE_NODE */:
619
- case "SHOW_DETAIL" /* SHOW_DETAIL */:
620
- case "HIDE_DETAIL" /* HIDE_DETAIL */:
621
- case "TOGGLE_STATE" /* TOGGLE_STATE */:
622
- case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
623
- case "UPDATE_FORM" /* UPDATE_FORM */:
624
- case "NAVIGATE" /* NAVIGATE */:
625
- dispatch({
626
- type: "PARTIAL_UPDATE",
627
- nodeId: route.targetNodeId,
628
- node
629
- });
630
- break;
1035
+ const handleEvent = useCallback(
1036
+ async (event) => {
1037
+ dispatch({ type: "UI_EVENT", event });
1038
+ dispatch({ type: "LOADING", isLoading: true });
1039
+ try {
1040
+ let resolvedNode;
1041
+ let actionTypeForDispatch = "FULL_REFRESH" /* FULL_REFRESH */;
1042
+ let targetNodeIdForDispatch = "root";
1043
+ if (enablePartialUpdates) {
1044
+ const route = router.resolveRoute(
1045
+ event,
1046
+ schema,
1047
+ state.layout,
1048
+ dataContext,
1049
+ goal,
1050
+ userContext
1051
+ );
1052
+ if (route) {
1053
+ console.log("Resolved route:", route);
1054
+ actionTypeForDispatch = route.actionType;
1055
+ targetNodeIdForDispatch = route.targetNodeId;
1056
+ systemEvents.emit(
1057
+ createSystemEvent("PLAN_START" /* PLAN_START */, {
1058
+ plannerInput: route.plannerInput
1059
+ })
1060
+ );
1061
+ if (mockMode) {
1062
+ resolvedNode = mockPlanner(
1063
+ route.plannerInput,
1064
+ route.targetNodeId,
1065
+ route.prompt
1066
+ );
1067
+ } else {
1068
+ resolvedNode = await callPlannerLLM(
1069
+ route.plannerInput,
1070
+ openaiApiKey || "",
1071
+ route
1072
+ );
1073
+ }
1074
+ } else {
1075
+ const input = {
1076
+ schema,
1077
+ goal,
1078
+ history: [...state.history, event],
1079
+ userContext
1080
+ };
1081
+ if (mockMode) {
1082
+ resolvedNode = mockPlanner(input);
1083
+ } else {
1084
+ resolvedNode = await callPlannerLLM(
1085
+ input,
1086
+ openaiApiKey || "",
1087
+ void 0
1088
+ );
1089
+ }
631
1090
  }
632
1091
  } else {
633
- const prompt = route.prompt;
634
- systemEvents.emit(
635
- createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
636
- );
637
- append({
638
- content: prompt,
639
- role: "user"
640
- });
641
- sessionStorage.setItem("currentRoute", JSON.stringify({
642
- actionType: route.actionType,
643
- targetNodeId: route.targetNodeId
644
- }));
1092
+ const input = {
1093
+ schema,
1094
+ goal,
1095
+ history: [...state.history, event],
1096
+ // event is already in history from UI_EVENT dispatch
1097
+ userContext
1098
+ };
1099
+ if (mockMode) {
1100
+ resolvedNode = mockPlanner(input);
1101
+ } else {
1102
+ resolvedNode = await callPlannerLLM(
1103
+ input,
1104
+ openaiApiKey || "",
1105
+ void 0
1106
+ );
1107
+ }
645
1108
  }
646
- return;
1109
+ switch (actionTypeForDispatch) {
1110
+ case "UPDATE_NODE" /* UPDATE_NODE */:
1111
+ case "SHOW_DETAIL" /* SHOW_DETAIL */:
1112
+ case "HIDE_DETAIL" /* HIDE_DETAIL */:
1113
+ case "TOGGLE_STATE" /* TOGGLE_STATE */:
1114
+ case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
1115
+ case "UPDATE_FORM" /* UPDATE_FORM */:
1116
+ case "NAVIGATE" /* NAVIGATE */:
1117
+ dispatch({
1118
+ type: "PARTIAL_UPDATE",
1119
+ nodeId: targetNodeIdForDispatch,
1120
+ node: resolvedNode
1121
+ });
1122
+ break;
1123
+ case "FULL_REFRESH" /* FULL_REFRESH */:
1124
+ default:
1125
+ dispatch({ type: "AI_RESPONSE", node: resolvedNode });
1126
+ break;
1127
+ }
1128
+ } catch (e) {
1129
+ const errorMessage = e instanceof Error ? e.message : String(e);
1130
+ dispatch({ type: "ERROR", message: errorMessage });
1131
+ systemEvents.emit(
1132
+ createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
1133
+ error: e instanceof Error ? e : new Error(String(e))
1134
+ })
1135
+ );
1136
+ } finally {
1137
+ dispatch({ type: "LOADING", isLoading: false });
647
1138
  }
648
- }
649
- const input = {
650
- schema,
1139
+ },
1140
+ [
1141
+ // append, // REMOVE
651
1142
  goal,
652
- history: [...state.history, event],
653
- userContext
654
- };
655
- if (mockMode) {
656
- const node = mockPlanner();
657
- dispatch({ type: "AI_RESPONSE", node });
658
- } else {
659
- const prompt = buildPrompt(input);
660
- append({
661
- content: prompt,
662
- role: "user"
663
- });
664
- }
665
- }, [append, goal, schema, state.history, state.layout, stop, userContext, router, mockMode, dataContext, enablePartialUpdates]);
1143
+ schema,
1144
+ state.history,
1145
+ // Keep state.history if input preparation needs it
1146
+ state.layout,
1147
+ // stop, // REMOVE
1148
+ userContext,
1149
+ router,
1150
+ mockMode,
1151
+ dataContext,
1152
+ openaiApiKey,
1153
+ enablePartialUpdates,
1154
+ dispatch
1155
+ // Add dispatch
1156
+ ]
1157
+ );
666
1158
  useEffect(() => {
667
- if (isLoading) {
1159
+ const initialFetch = async () => {
668
1160
  dispatch({ type: "LOADING", isLoading: true });
669
- } else if (error) {
670
- const errorMessage = error instanceof Error ? error.message : String(error);
671
- dispatch({ type: "ERROR", message: errorMessage });
672
- systemEvents.emit(
673
- createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
674
- error: error instanceof Error ? error : new Error(String(error))
675
- })
676
- );
677
- } else if (data.content) {
678
1161
  try {
679
- systemEvents.emit(
680
- createSystemEvent("PLAN_RESPONSE_CHUNK" /* PLAN_RESPONSE_CHUNK */, {
681
- chunk: data.content,
682
- isComplete: true
683
- })
684
- );
685
- const jsonMatch = data.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/) || [null, data.content];
686
- const jsonStr = jsonMatch[1].trim();
687
- const parsedJson = JSON.parse(jsonStr);
688
- const validatedNode = uiSpecNode.parse(parsedJson);
689
- const routeInfoStr = sessionStorage.getItem("currentRoute");
690
- if (routeInfoStr && enablePartialUpdates) {
691
- try {
692
- const routeInfo = JSON.parse(routeInfoStr);
693
- switch (routeInfo.actionType) {
694
- case "FULL_REFRESH" /* FULL_REFRESH */:
695
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
696
- break;
697
- case "UPDATE_NODE" /* UPDATE_NODE */:
698
- case "SHOW_DETAIL" /* SHOW_DETAIL */:
699
- case "HIDE_DETAIL" /* HIDE_DETAIL */:
700
- case "TOGGLE_STATE" /* TOGGLE_STATE */:
701
- case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
702
- case "UPDATE_FORM" /* UPDATE_FORM */:
703
- case "NAVIGATE" /* NAVIGATE */:
704
- dispatch({
705
- type: "PARTIAL_UPDATE",
706
- nodeId: routeInfo.targetNodeId,
707
- node: validatedNode
708
- });
709
- break;
710
- default:
711
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
712
- }
713
- sessionStorage.removeItem("currentRoute");
714
- } catch (e) {
715
- console.error("Error parsing route info:", e);
716
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
717
- }
1162
+ const input = {
1163
+ schema,
1164
+ goal,
1165
+ history: [],
1166
+ // Initial history is empty
1167
+ userContext
1168
+ };
1169
+ let node;
1170
+ if (mockMode) {
1171
+ node = mockPlanner(input);
718
1172
  } else {
719
- dispatch({ type: "AI_RESPONSE", node: validatedNode });
1173
+ const initEvent = {
1174
+ type: "INIT",
1175
+ // Assuming "INIT" is your initial event type
1176
+ nodeId: "system",
1177
+ // Or some other appropriate initial nodeId
1178
+ timestamp: Date.now(),
1179
+ payload: null
1180
+ };
1181
+ const route = router.resolveRoute(
1182
+ initEvent,
1183
+ schema,
1184
+ null,
1185
+ // No existing layout on initial fetch
1186
+ dataContext,
1187
+ goal,
1188
+ userContext
1189
+ );
1190
+ if (!route || !route.prompt) {
1191
+ console.error(
1192
+ "[UIStateEngine] Initial fetch: Failed to resolve route or get prompt for INIT event."
1193
+ );
1194
+ throw new Error("Failed to initialize UI due to routing error.");
1195
+ }
1196
+ systemEvents.emit(
1197
+ createSystemEvent("PLAN_START" /* PLAN_START */, {
1198
+ plannerInput: route.plannerInput
1199
+ })
1200
+ );
1201
+ node = await callPlannerLLM(
1202
+ route.plannerInput,
1203
+ // Use plannerInput from the resolved route
1204
+ openaiApiKey || "",
1205
+ route
1206
+ // Pass the entire route object
1207
+ );
720
1208
  }
1209
+ dispatch({ type: "AI_RESPONSE", node });
1210
+ } catch (e) {
1211
+ const errorMessage = e instanceof Error ? e.message : String(e);
1212
+ dispatch({ type: "ERROR", message: errorMessage });
721
1213
  systemEvents.emit(
722
- createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
723
- layout: validatedNode,
724
- executionTimeMs: 0
725
- // Not available here
726
- })
727
- );
728
- } catch (parseError) {
729
- console.error("Failed to parse LLM response:", parseError);
730
- dispatch({
731
- type: "ERROR",
732
- message: "Failed to parse LLM response"
733
- });
734
- systemEvents.emit(
1214
+ // Also emit system event for initial load error
735
1215
  createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
736
- error: parseError instanceof Error ? parseError : new Error("Parse error")
1216
+ error: e instanceof Error ? e : new Error(String(e))
737
1217
  })
738
1218
  );
1219
+ } finally {
1220
+ dispatch({ type: "LOADING", isLoading: false });
739
1221
  }
740
- }
741
- }, [data.content, error, isLoading, enablePartialUpdates]);
742
- useEffect(() => {
743
- const input = {
744
- schema,
745
- goal,
746
- history: [],
747
- userContext
748
1222
  };
749
- if (mockMode) {
750
- const node = mockPlanner();
751
- dispatch({ type: "AI_RESPONSE", node });
752
- } else {
753
- const prompt = buildPrompt(input);
754
- append({
755
- content: prompt,
756
- role: "user"
757
- });
758
- }
759
- }, [append, goal, schema, userContext, mockMode]);
1223
+ initialFetch();
1224
+ }, [goal, schema, userContext, mockMode, dispatch, openaiApiKey]);
760
1225
  return {
761
1226
  state,
762
1227
  dispatch,
763
1228
  handleEvent
764
1229
  };
765
1230
  }
1231
+
1232
+ // src/adapters/shadcn.tsx
1233
+ init_utils();
1234
+
1235
+ // components/ui/dialog.tsx
1236
+ init_utils();
1237
+ function Dialog({
1238
+ ...props
1239
+ }) {
1240
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
1241
+ }
1242
+ function DialogPortal({
1243
+ ...props
1244
+ }) {
1245
+ return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
1246
+ }
1247
+ function DialogOverlay({
1248
+ className,
1249
+ ...props
1250
+ }) {
1251
+ return /* @__PURE__ */ jsx(
1252
+ DialogPrimitive.Overlay,
1253
+ {
1254
+ "data-slot": "dialog-overlay",
1255
+ className: cn(
1256
+ "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",
1257
+ className
1258
+ ),
1259
+ ...props
1260
+ }
1261
+ );
1262
+ }
1263
+ function DialogContent({
1264
+ className,
1265
+ children,
1266
+ ...props
1267
+ }) {
1268
+ return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
1269
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
1270
+ /* @__PURE__ */ jsxs(
1271
+ DialogPrimitive.Content,
1272
+ {
1273
+ "data-slot": "dialog-content",
1274
+ className: cn(
1275
+ "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",
1276
+ className
1277
+ ),
1278
+ ...props,
1279
+ children: [
1280
+ children,
1281
+ /* @__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: [
1282
+ /* @__PURE__ */ jsx(XIcon, {}),
1283
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
1284
+ ] })
1285
+ ]
1286
+ }
1287
+ )
1288
+ ] });
1289
+ }
1290
+ function DialogHeader({ className, ...props }) {
1291
+ return /* @__PURE__ */ jsx(
1292
+ "div",
1293
+ {
1294
+ "data-slot": "dialog-header",
1295
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
1296
+ ...props
1297
+ }
1298
+ );
1299
+ }
1300
+ function DialogTitle({
1301
+ className,
1302
+ ...props
1303
+ }) {
1304
+ return /* @__PURE__ */ jsx(
1305
+ DialogPrimitive.Title,
1306
+ {
1307
+ "data-slot": "dialog-title",
1308
+ className: cn("text-lg leading-none font-semibold", className),
1309
+ ...props
1310
+ }
1311
+ );
1312
+ }
1313
+ function DialogDescription({
1314
+ className,
1315
+ ...props
1316
+ }) {
1317
+ return /* @__PURE__ */ jsx(
1318
+ DialogPrimitive.Description,
1319
+ {
1320
+ "data-slot": "dialog-description",
1321
+ className: cn("text-muted-foreground text-sm", className),
1322
+ ...props
1323
+ }
1324
+ );
1325
+ }
1326
+
1327
+ // components/ui/card.tsx
1328
+ init_utils();
1329
+ function Card({ className, ...props }) {
1330
+ return /* @__PURE__ */ jsx(
1331
+ "div",
1332
+ {
1333
+ "data-slot": "card",
1334
+ className: cn(
1335
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
1336
+ className
1337
+ ),
1338
+ ...props
1339
+ }
1340
+ );
1341
+ }
1342
+ function CardContent({ className, ...props }) {
1343
+ return /* @__PURE__ */ jsx(
1344
+ "div",
1345
+ {
1346
+ "data-slot": "card-content",
1347
+ className: cn("px-6", className),
1348
+ ...props
1349
+ }
1350
+ );
1351
+ }
1352
+
1353
+ // components/ui/input.tsx
1354
+ init_utils();
1355
+ function Input({ className, type, ...props }) {
1356
+ return /* @__PURE__ */ jsx(
1357
+ "input",
1358
+ {
1359
+ type,
1360
+ "data-slot": "input",
1361
+ className: cn(
1362
+ "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",
1363
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1364
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1365
+ className
1366
+ ),
1367
+ ...props
1368
+ }
1369
+ );
1370
+ }
1371
+
1372
+ // components/ui/textarea.tsx
1373
+ init_utils();
1374
+ function Textarea({ className, ...props }) {
1375
+ return /* @__PURE__ */ jsx(
1376
+ "textarea",
1377
+ {
1378
+ "data-slot": "textarea",
1379
+ className: cn(
1380
+ "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",
1381
+ className
1382
+ ),
1383
+ ...props
1384
+ }
1385
+ );
1386
+ }
1387
+
1388
+ // components/ui/select.tsx
1389
+ init_utils();
1390
+ function Select({
1391
+ ...props
1392
+ }) {
1393
+ return /* @__PURE__ */ jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
1394
+ }
1395
+ function SelectValue({
1396
+ ...props
1397
+ }) {
1398
+ return /* @__PURE__ */ jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
1399
+ }
1400
+ function SelectTrigger({
1401
+ className,
1402
+ size = "default",
1403
+ children,
1404
+ ...props
1405
+ }) {
1406
+ return /* @__PURE__ */ jsxs(
1407
+ SelectPrimitive.Trigger,
1408
+ {
1409
+ "data-slot": "select-trigger",
1410
+ "data-size": size,
1411
+ className: cn(
1412
+ "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",
1413
+ className
1414
+ ),
1415
+ ...props,
1416
+ children: [
1417
+ children,
1418
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })
1419
+ ]
1420
+ }
1421
+ );
1422
+ }
1423
+ function SelectContent({
1424
+ className,
1425
+ children,
1426
+ position = "popper",
1427
+ ...props
1428
+ }) {
1429
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
1430
+ SelectPrimitive.Content,
1431
+ {
1432
+ "data-slot": "select-content",
1433
+ className: cn(
1434
+ "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",
1435
+ 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",
1436
+ className
1437
+ ),
1438
+ position,
1439
+ ...props,
1440
+ children: [
1441
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1442
+ /* @__PURE__ */ jsx(
1443
+ SelectPrimitive.Viewport,
1444
+ {
1445
+ className: cn(
1446
+ "p-1",
1447
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
1448
+ ),
1449
+ children
1450
+ }
1451
+ ),
1452
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1453
+ ]
1454
+ }
1455
+ ) });
1456
+ }
1457
+ function SelectItem({
1458
+ className,
1459
+ children,
1460
+ ...props
1461
+ }) {
1462
+ return /* @__PURE__ */ jsxs(
1463
+ SelectPrimitive.Item,
1464
+ {
1465
+ "data-slot": "select-item",
1466
+ className: cn(
1467
+ "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",
1468
+ className
1469
+ ),
1470
+ ...props,
1471
+ children: [
1472
+ /* @__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" }) }) }),
1473
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
1474
+ ]
1475
+ }
1476
+ );
1477
+ }
1478
+ function SelectScrollUpButton({
1479
+ className,
1480
+ ...props
1481
+ }) {
1482
+ return /* @__PURE__ */ jsx(
1483
+ SelectPrimitive.ScrollUpButton,
1484
+ {
1485
+ "data-slot": "select-scroll-up-button",
1486
+ className: cn(
1487
+ "flex cursor-default items-center justify-center py-1",
1488
+ className
1489
+ ),
1490
+ ...props,
1491
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "size-4" })
1492
+ }
1493
+ );
1494
+ }
1495
+ function SelectScrollDownButton({
1496
+ className,
1497
+ ...props
1498
+ }) {
1499
+ return /* @__PURE__ */ jsx(
1500
+ SelectPrimitive.ScrollDownButton,
1501
+ {
1502
+ "data-slot": "select-scroll-down-button",
1503
+ className: cn(
1504
+ "flex cursor-default items-center justify-center py-1",
1505
+ className
1506
+ ),
1507
+ ...props,
1508
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4" })
1509
+ }
1510
+ );
1511
+ }
1512
+
1513
+ // components/ui/checkbox.tsx
1514
+ init_utils();
1515
+ function Checkbox({
1516
+ className,
1517
+ ...props
1518
+ }) {
1519
+ return /* @__PURE__ */ jsx(
1520
+ CheckboxPrimitive.Root,
1521
+ {
1522
+ "data-slot": "checkbox",
1523
+ className: cn(
1524
+ "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",
1525
+ className
1526
+ ),
1527
+ ...props,
1528
+ children: /* @__PURE__ */ jsx(
1529
+ CheckboxPrimitive.Indicator,
1530
+ {
1531
+ "data-slot": "checkbox-indicator",
1532
+ className: "flex items-center justify-center text-current transition-none",
1533
+ children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" })
1534
+ }
1535
+ )
1536
+ }
1537
+ );
1538
+ }
1539
+
1540
+ // components/ui/radio-group.tsx
1541
+ init_utils();
1542
+ function RadioGroup({
1543
+ className,
1544
+ ...props
1545
+ }) {
1546
+ return /* @__PURE__ */ jsx(
1547
+ RadioGroupPrimitive.Root,
1548
+ {
1549
+ "data-slot": "radio-group",
1550
+ className: cn("grid gap-3", className),
1551
+ ...props
1552
+ }
1553
+ );
1554
+ }
1555
+ function RadioGroupItem({
1556
+ className,
1557
+ ...props
1558
+ }) {
1559
+ return /* @__PURE__ */ jsx(
1560
+ RadioGroupPrimitive.Item,
1561
+ {
1562
+ "data-slot": "radio-group-item",
1563
+ className: cn(
1564
+ "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",
1565
+ className
1566
+ ),
1567
+ ...props,
1568
+ children: /* @__PURE__ */ jsx(
1569
+ RadioGroupPrimitive.Indicator,
1570
+ {
1571
+ "data-slot": "radio-group-indicator",
1572
+ className: "relative flex items-center justify-center",
1573
+ children: /* @__PURE__ */ jsx(CircleIcon, { className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" })
1574
+ }
1575
+ )
1576
+ }
1577
+ );
1578
+ }
1579
+
1580
+ // components/ui/tabs.tsx
1581
+ init_utils();
1582
+ function Tabs({
1583
+ className,
1584
+ ...props
1585
+ }) {
1586
+ return /* @__PURE__ */ jsx(
1587
+ TabsPrimitive.Root,
1588
+ {
1589
+ "data-slot": "tabs",
1590
+ className: cn("flex flex-col gap-2", className),
1591
+ ...props
1592
+ }
1593
+ );
1594
+ }
1595
+ function TabsList({
1596
+ className,
1597
+ ...props
1598
+ }) {
1599
+ return /* @__PURE__ */ jsx(
1600
+ TabsPrimitive.List,
1601
+ {
1602
+ "data-slot": "tabs-list",
1603
+ className: cn(
1604
+ "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
1605
+ className
1606
+ ),
1607
+ ...props
1608
+ }
1609
+ );
1610
+ }
1611
+ function TabsTrigger({
1612
+ className,
1613
+ ...props
1614
+ }) {
1615
+ return /* @__PURE__ */ jsx(
1616
+ TabsPrimitive.Trigger,
1617
+ {
1618
+ "data-slot": "tabs-trigger",
1619
+ className: cn(
1620
+ "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",
1621
+ className
1622
+ ),
1623
+ ...props
1624
+ }
1625
+ );
1626
+ }
1627
+ function TabsContent({
1628
+ className,
1629
+ ...props
1630
+ }) {
1631
+ return /* @__PURE__ */ jsx(
1632
+ TabsPrimitive.Content,
1633
+ {
1634
+ "data-slot": "tabs-content",
1635
+ className: cn("flex-1 outline-none", className),
1636
+ ...props
1637
+ }
1638
+ );
1639
+ }
1640
+
1641
+ // components/ui/label.tsx
1642
+ init_utils();
1643
+ function Label2({
1644
+ className,
1645
+ ...props
1646
+ }) {
1647
+ return /* @__PURE__ */ jsx(
1648
+ LabelPrimitive.Root,
1649
+ {
1650
+ "data-slot": "label",
1651
+ className: cn(
1652
+ "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",
1653
+ className
1654
+ ),
1655
+ ...props
1656
+ }
1657
+ );
1658
+ }
1659
+ var parseStyleString = (styleString) => {
1660
+ if (typeof styleString !== "string") {
1661
+ return typeof styleString === "object" ? styleString : {};
1662
+ }
1663
+ const style = {};
1664
+ styleString.split(";").forEach((declaration) => {
1665
+ const [property, value] = declaration.split(":");
1666
+ if (property && value) {
1667
+ const camelCasedProperty = property.trim().replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1668
+ style[camelCasedProperty] = value.trim();
1669
+ }
1670
+ });
1671
+ return style;
1672
+ };
1673
+ var isArrayOf = (guard) => (arr) => Array.isArray(arr) && arr.every(guard);
766
1674
  var ShimmerBlock = () => /* @__PURE__ */ jsx("div", { className: "w-full h-8 bg-gray-200 animate-pulse rounded" });
767
1675
  var ShimmerTable = ({ rows = 3 }) => /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2", children: [
768
1676
  /* @__PURE__ */ jsx("div", { className: "w-full h-10 bg-gray-200 animate-pulse rounded" }),
@@ -776,119 +1684,696 @@ var ShimmerCard = () => /* @__PURE__ */ jsxs("div", { className: "w-full p-4 spa
776
1684
  /* @__PURE__ */ jsx("div", { className: "w-5/6 h-4 bg-gray-200 animate-pulse rounded" })
777
1685
  ] })
778
1686
  ] });
779
- var Container = (props) => /* @__PURE__ */ jsx("div", { className: `w-full ${props.className || ""}`, style: props.style, children: props.children });
780
- 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 }) });
1687
+ var Container = (props) => /* @__PURE__ */ jsx("div", { className: `autoui-mock-container ${props.className || ""}`, children: props.children });
1688
+ var Header = ({
1689
+ title,
1690
+ className
1691
+ }) => /* @__PURE__ */ jsx(
1692
+ "header",
1693
+ {
1694
+ className: `py-4 px-6 border-b border-gray-300 mb-4 bg-gray-50 dark:bg-gray-800 ${className || ""}`,
1695
+ children: /* @__PURE__ */ jsx("h1", { className: "text-xl font-semibold text-gray-800 dark:text-white", children: title })
1696
+ }
1697
+ );
781
1698
  var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsx(
782
1699
  "button",
783
1700
  {
784
- className: `px-4 py-2 rounded font-medium ${variant === "default" ? "bg-blue-600 text-white" : variant === "outline" ? "border border-gray-300 text-gray-700" : "bg-red-600 text-white"}`,
785
- onClick,
1701
+ 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"}`,
1702
+ onClick: () => onClick?.(),
786
1703
  children
787
1704
  }
788
1705
  );
789
- var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__ */ jsx("div", { className: "w-full border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
790
- /* @__PURE__ */ jsx("thead", { className: "bg-gray-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)) }) }),
791
- /* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: items.map((item, index) => /* @__PURE__ */ jsx(
792
- "tr",
793
- {
794
- onClick: () => selectable && onSelect && onSelect(item),
795
- className: selectable ? "cursor-pointer hover:bg-gray-50" : "",
796
- 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))
797
- },
798
- index
799
- )) })
800
- ] }) });
801
1706
  var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
802
1707
  if (!visible)
803
1708
  return null;
804
- return /* @__PURE__ */ jsxs("div", { className: "w-full border rounded-lg p-6 space-y-4", children: [
805
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
806
- title && /* @__PURE__ */ jsx("h2", { className: "text-lg font-medium", children: title }),
1709
+ 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: [
1710
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-3", children: [
1711
+ title && /* @__PURE__ */ jsx("h2", { className: "text-lg font-medium text-gray-800 dark:text-white", children: title }),
807
1712
  onBack && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
808
1713
  ] }),
809
1714
  /* @__PURE__ */ jsx("div", { className: "space-y-4", children: fields.map((field) => {
810
1715
  if (field.type === "heading") {
811
- return /* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold", children: data?.[field.key] }, field.key);
1716
+ return /* @__PURE__ */ jsx(
1717
+ "h3",
1718
+ {
1719
+ className: "text-xl font-semibold text-gray-800 dark:text-white",
1720
+ children: data?.[field.key] ?? ""
1721
+ },
1722
+ field.key
1723
+ );
812
1724
  }
813
1725
  if (field.type === "content") {
814
- return /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-700", children: data?.[field.key] }, field.key);
1726
+ return /* @__PURE__ */ jsx(
1727
+ "div",
1728
+ {
1729
+ className: "text-sm text-gray-700 dark:text-gray-300",
1730
+ children: data?.[field.key] ?? ""
1731
+ },
1732
+ field.key
1733
+ );
815
1734
  }
816
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
817
- field.label && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500", children: field.label }),
818
- /* @__PURE__ */ jsx("span", { className: "text-sm", children: data?.[field.key] })
819
- ] }, field.key);
1735
+ return /* @__PURE__ */ jsxs(
1736
+ "div",
1737
+ {
1738
+ className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
1739
+ children: [
1740
+ field.label && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
1741
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
1742
+ ]
1743
+ },
1744
+ field.key
1745
+ );
820
1746
  }) })
821
1747
  ] });
822
1748
  };
823
- var createEventHandler = (node, eventName) => {
824
- const eventConfig = node.events?.[eventName];
825
- if (!eventConfig)
1749
+ var getSafeProp = (props, key, validator, defaultValue) => {
1750
+ if (props && typeof props === "object" && key in props) {
1751
+ const value = props[key];
1752
+ if (validator(value)) {
1753
+ return value;
1754
+ }
1755
+ }
1756
+ return defaultValue;
1757
+ };
1758
+ var isObject = (value) => typeof value === "object" && value !== null;
1759
+ var isString = (value) => typeof value === "string";
1760
+ var isBoolean = (value) => typeof value === "boolean";
1761
+ var isButtonVariant = (value) => isString(value) && ["default", "outline", "destructive"].includes(value);
1762
+ var getSafeBinding = (bindings, key, validator, defaultValue) => {
1763
+ if (bindings && typeof bindings === "object" && key in bindings) {
1764
+ const value = bindings[key];
1765
+ if (validator(value)) {
1766
+ return value;
1767
+ }
1768
+ }
1769
+ return defaultValue;
1770
+ };
1771
+ var isReactNode = (value) => {
1772
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null || typeof value === "undefined" || typeof value === "object" && value !== null && "$$typeof" in value;
1773
+ };
1774
+ var isRecordWithReactNodeValues = (value) => isObject(value) && Object.values(value).every(isReactNode);
1775
+ var isSelectOptionObject = (item) => isObject(item) && isString(item.value) && isString(item.label);
1776
+ var isTabObject = (item) => (
1777
+ // Allow content to be optional initially
1778
+ isObject(item) && isString(item.value) && isString(item.label) && (item.content === void 0 || isUISpecNode(item.content))
1779
+ );
1780
+ var isUISpecNode = (value) => {
1781
+ if (!isObject(value))
1782
+ return false;
1783
+ return isString(value.id) && isString(value.node_type);
1784
+ };
1785
+ var isDetailFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label) && (item.type === void 0 || isString(item.type));
1786
+ var createEventHandler = (node, eventName, uiEventType2, processEvent2) => {
1787
+ const eventConfig = node.events?.[uiEventType2];
1788
+ if (!processEvent2 || !eventConfig)
826
1789
  return void 0;
827
- return () => {
828
- console.log(`Event triggered: ${eventName} on node ${node.id}`, {
829
- action: eventConfig.action,
830
- target: eventConfig.target,
831
- payload: eventConfig.payload
832
- });
1790
+ return (eventPayload) => {
1791
+ const fullEvent = {
1792
+ type: uiEventType2,
1793
+ nodeId: node.id,
1794
+ timestamp: Date.now(),
1795
+ payload: {
1796
+ ...eventConfig.payload || {},
1797
+ ...eventPayload || {}
1798
+ }
1799
+ };
1800
+ processEvent2(fullEvent);
833
1801
  };
834
1802
  };
835
1803
  var adapterMap = {
836
- Container: (node) => /* @__PURE__ */ jsx(Container, { style: node.props?.style, className: node.props?.className, children: node.children?.map((child) => renderNode(child)) }),
837
- Header: (node) => /* @__PURE__ */ jsx(Header, { title: node.props?.title || "Untitled" }),
838
- Button: (node) => /* @__PURE__ */ jsx(
839
- Button,
1804
+ Container: (node, processEvent2) => {
1805
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
1806
+ const children = node.children?.map(
1807
+ (child) => (
1808
+ // Use React.cloneElement to add the key prop to the element returned by renderNode
1809
+ React.cloneElement(renderNode(child, processEvent2), { key: child.id })
1810
+ )
1811
+ );
1812
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
1813
+ return /* @__PURE__ */ jsxs(
1814
+ "div",
1815
+ {
1816
+ className: cn("autoui-container", className),
1817
+ style,
1818
+ ...restProps,
1819
+ "data-id": node.id,
1820
+ children: [
1821
+ (() => {
1822
+ console.log(
1823
+ `[Adapter Debug] Rendering Container: id=${node.id}, props=`,
1824
+ node.props
1825
+ );
1826
+ return null;
1827
+ })(),
1828
+ children
1829
+ ]
1830
+ },
1831
+ key
1832
+ );
1833
+ },
1834
+ Header: (node) => /* @__PURE__ */ jsx(
1835
+ Header,
840
1836
  {
841
- variant: node.props?.variant,
842
- onClick: createEventHandler(node, "onClick"),
843
- children: node.props?.label || "Button"
1837
+ title: getSafeProp(node.props, "title", isString, "Untitled"),
1838
+ className: getSafeProp(node.props, "className", isString, "")
844
1839
  }
845
1840
  ),
846
- ListView: (node) => /* @__PURE__ */ jsx(
847
- Table,
1841
+ Button: (node, processEvent2) => /* @__PURE__ */ jsx(
1842
+ Button,
848
1843
  {
849
- items: node.bindings?.items || [],
850
- fields: node.bindings?.fields || [],
851
- selectable: node.props?.selectable,
852
- onSelect: createEventHandler(node, "onSelect")
1844
+ variant: getSafeProp(node.props, "variant", isButtonVariant, "default"),
1845
+ onClick: createEventHandler(node, "onClick", "CLICK", processEvent2),
1846
+ children: getSafeProp(node.props, "label", isString, "Button")
853
1847
  }
854
1848
  ),
855
- Detail: (node) => /* @__PURE__ */ jsx(
856
- Detail,
857
- {
858
- data: node.bindings?.data,
859
- fields: node.bindings?.fields || [],
860
- title: node.props?.title,
861
- visible: node.props?.visible !== false,
862
- onBack: createEventHandler(node, "onBack")
1849
+ ListView: (node, processEvent2) => {
1850
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
1851
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
1852
+ console.log(
1853
+ `[Adapter Debug] Rendering ListView: id=${node.id}, props=`,
1854
+ node.props
1855
+ );
1856
+ const children = node.children?.map(
1857
+ (child) => React.cloneElement(renderNode(child, processEvent2), { key: child.id })
1858
+ );
1859
+ return /* @__PURE__ */ jsx(
1860
+ "div",
1861
+ {
1862
+ className: cn(
1863
+ "autoui-listview-container space-y-2",
1864
+ className
1865
+ ),
1866
+ style,
1867
+ ...restProps,
1868
+ "data-id": node.id,
1869
+ children
1870
+ },
1871
+ key
1872
+ );
1873
+ },
1874
+ Detail: (node, processEvent2) => {
1875
+ const data = getSafeBinding(
1876
+ node.bindings,
1877
+ "data",
1878
+ isRecordWithReactNodeValues,
1879
+ {}
1880
+ );
1881
+ const fields = getSafeBinding(
1882
+ node.bindings,
1883
+ "fields",
1884
+ isArrayOf(isDetailFieldObject),
1885
+ []
1886
+ );
1887
+ const title = getSafeProp(node.props, "title", isString, "");
1888
+ const visible = getSafeProp(node.props, "visible", isBoolean, true);
1889
+ return /* @__PURE__ */ jsx(
1890
+ Detail,
1891
+ {
1892
+ data,
1893
+ fields,
1894
+ title,
1895
+ visible,
1896
+ onBack: createEventHandler(node, "onBack", "CLICK", processEvent2)
1897
+ }
1898
+ );
1899
+ },
1900
+ Card: (node, processEvent2) => {
1901
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
1902
+ const children = node.children?.map(
1903
+ (child) => React.cloneElement(renderNode(child, processEvent2), { key: child.id })
1904
+ );
1905
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
1906
+ return /* @__PURE__ */ jsx(
1907
+ Card,
1908
+ {
1909
+ className: cn("autoui-card", className),
1910
+ style,
1911
+ ...restProps,
1912
+ "data-id": node.id,
1913
+ children: /* @__PURE__ */ jsxs(CardContent, { className: "p-0", children: [
1914
+ " ",
1915
+ children
1916
+ ] })
1917
+ },
1918
+ key
1919
+ );
1920
+ },
1921
+ Input: (node, processEvent2) => {
1922
+ const name = getSafeProp(node.props, "name", isString, "inputName");
1923
+ const label = getSafeProp(node.props, "label", isString, "");
1924
+ const value = getSafeBinding(node.bindings, "value", isString, "");
1925
+ const placeholder = getSafeProp(node.props, "placeholder", isString, "");
1926
+ const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
1927
+ const className = getSafeProp(node.props, "className", isString, "");
1928
+ const handleChange = (e) => {
1929
+ const handler = createEventHandler(
1930
+ node,
1931
+ "onChange",
1932
+ "CHANGE",
1933
+ processEvent2
1934
+ );
1935
+ if (handler)
1936
+ handler({ value: e.target.value });
1937
+ };
1938
+ const handleFocus = () => {
1939
+ const handler = createEventHandler(
1940
+ node,
1941
+ "onFocus",
1942
+ "FOCUS",
1943
+ processEvent2
1944
+ );
1945
+ if (handler)
1946
+ handler({});
1947
+ };
1948
+ const handleBlur = () => {
1949
+ const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
1950
+ if (handler)
1951
+ handler({});
1952
+ };
1953
+ return /* @__PURE__ */ jsxs("div", { className: "grid w-full max-w-sm items-center gap-1.5", children: [
1954
+ label && /* @__PURE__ */ jsx(Label2, { htmlFor: name, children: label }),
1955
+ /* @__PURE__ */ jsx(
1956
+ Input,
1957
+ {
1958
+ id: name,
1959
+ name,
1960
+ placeholder,
1961
+ disabled,
1962
+ value,
1963
+ onChange: handleChange,
1964
+ onFocus: handleFocus,
1965
+ onBlur: handleBlur,
1966
+ className
1967
+ }
1968
+ )
1969
+ ] });
1970
+ },
1971
+ Select: (node, processEvent2) => {
1972
+ const name = getSafeProp(node.props, "name", isString, "selectName");
1973
+ const label = getSafeProp(node.props, "label", isString, "");
1974
+ const placeholder = getSafeProp(
1975
+ node.props,
1976
+ "placeholder",
1977
+ isString,
1978
+ "Select..."
1979
+ );
1980
+ const disabled = getSafeProp(node.props, "disabled", isBoolean, false);
1981
+ const value = getSafeBinding(node.bindings, "value", isString, "");
1982
+ const options = getSafeBinding(
1983
+ node.bindings,
1984
+ "options",
1985
+ isArrayOf(isSelectOptionObject),
1986
+ []
1987
+ );
1988
+ const className = getSafeProp(node.props, "className", isString, "");
1989
+ const handleValueChange = (selectedValue) => {
1990
+ const handler = createEventHandler(
1991
+ node,
1992
+ "onValueChange",
1993
+ "CHANGE",
1994
+ processEvent2
1995
+ );
1996
+ if (handler)
1997
+ handler({ value: selectedValue });
1998
+ };
1999
+ return /* @__PURE__ */ jsxs(
2000
+ "div",
2001
+ {
2002
+ className: cn("grid w-full max-w-sm items-center gap-1.5", className),
2003
+ children: [
2004
+ label && /* @__PURE__ */ jsx(Label2, { htmlFor: name, children: label }),
2005
+ /* @__PURE__ */ jsxs(
2006
+ Select,
2007
+ {
2008
+ name,
2009
+ value,
2010
+ onValueChange: handleValueChange,
2011
+ disabled,
2012
+ children: [
2013
+ /* @__PURE__ */ jsx(SelectTrigger, { id: name, children: /* @__PURE__ */ jsx(SelectValue, { placeholder }) }),
2014
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.label }, option.value)) })
2015
+ ]
2016
+ }
2017
+ )
2018
+ ]
2019
+ }
2020
+ );
2021
+ },
2022
+ Textarea: (node, processEvent2) => {
2023
+ const { key, ...propsWithoutKey } = node.props || {};
2024
+ const name = getSafeProp(propsWithoutKey, "name", isString, "textareaName");
2025
+ const label = getSafeProp(propsWithoutKey, "label", isString, "");
2026
+ const placeholder = getSafeProp(
2027
+ propsWithoutKey,
2028
+ "placeholder",
2029
+ isString,
2030
+ ""
2031
+ );
2032
+ const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
2033
+ const rows = getSafeProp(
2034
+ propsWithoutKey,
2035
+ "rows",
2036
+ (v) => typeof v === "number",
2037
+ 3
2038
+ );
2039
+ const value = getSafeBinding(node.bindings, "value", isString, "");
2040
+ const className = getSafeProp(propsWithoutKey, "className", isString, "");
2041
+ const handleChange = (e) => {
2042
+ const handler = createEventHandler(
2043
+ node,
2044
+ "onChange",
2045
+ "CHANGE",
2046
+ processEvent2
2047
+ );
2048
+ if (handler)
2049
+ handler({ value: e.target.value });
2050
+ };
2051
+ const handleFocus = () => {
2052
+ const handler = createEventHandler(
2053
+ node,
2054
+ "onFocus",
2055
+ "FOCUS",
2056
+ processEvent2
2057
+ );
2058
+ if (handler)
2059
+ handler({});
2060
+ };
2061
+ const handleBlur = () => {
2062
+ const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
2063
+ if (handler)
2064
+ handler({});
2065
+ };
2066
+ return /* @__PURE__ */ jsxs("div", { className: "grid w-full gap-1.5", children: [
2067
+ label && /* @__PURE__ */ jsx(Label2, { htmlFor: name, children: label }),
2068
+ /* @__PURE__ */ jsx(
2069
+ Textarea,
2070
+ {
2071
+ id: name,
2072
+ name,
2073
+ placeholder,
2074
+ disabled,
2075
+ rows,
2076
+ value,
2077
+ onChange: handleChange,
2078
+ onFocus: handleFocus,
2079
+ onBlur: handleBlur,
2080
+ className
2081
+ }
2082
+ )
2083
+ ] }, key);
2084
+ },
2085
+ Checkbox: (node, processEvent2) => {
2086
+ const { key, ...propsWithoutKey } = node.props || {};
2087
+ const name = getSafeProp(propsWithoutKey, "name", isString, "checkboxName");
2088
+ const label = getSafeProp(propsWithoutKey, "label", isString, "");
2089
+ const checked = getSafeBinding(node.bindings, "checked", isBoolean, false);
2090
+ const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
2091
+ const className = getSafeProp(propsWithoutKey, "className", isString, "");
2092
+ const handleCheckedChange = (isChecked) => {
2093
+ if (typeof isChecked === "boolean") {
2094
+ const handler = createEventHandler(
2095
+ node,
2096
+ "onCheckedChange",
2097
+ "CHANGE",
2098
+ processEvent2
2099
+ );
2100
+ if (handler)
2101
+ handler({ checked: isChecked });
2102
+ }
2103
+ };
2104
+ return /* @__PURE__ */ jsxs(
2105
+ "div",
2106
+ {
2107
+ className: cn("flex items-center space-x-2", className),
2108
+ children: [
2109
+ /* @__PURE__ */ jsx(
2110
+ Checkbox,
2111
+ {
2112
+ id: name,
2113
+ name,
2114
+ checked,
2115
+ disabled,
2116
+ onCheckedChange: handleCheckedChange
2117
+ }
2118
+ ),
2119
+ label && /* @__PURE__ */ jsx(Label2, { htmlFor: name, className: "cursor-pointer", children: label })
2120
+ ]
2121
+ },
2122
+ key
2123
+ );
2124
+ },
2125
+ RadioGroup: (node, processEvent2) => {
2126
+ const { key, ...propsWithoutKey } = node.props || {};
2127
+ const name = getSafeProp(
2128
+ propsWithoutKey,
2129
+ "name",
2130
+ isString,
2131
+ "radioGroupName"
2132
+ );
2133
+ const label = getSafeProp(propsWithoutKey, "label", isString, "");
2134
+ const value = getSafeBinding(node.bindings, "value", isString, "");
2135
+ const options = getSafeBinding(
2136
+ node.bindings,
2137
+ "options",
2138
+ isArrayOf(isSelectOptionObject),
2139
+ []
2140
+ );
2141
+ const disabled = getSafeProp(propsWithoutKey, "disabled", isBoolean, false);
2142
+ const className = getSafeProp(propsWithoutKey, "className", isString, "");
2143
+ const handleValueChange = (selectedValue) => {
2144
+ const handler = createEventHandler(
2145
+ node,
2146
+ "onValueChange",
2147
+ "CHANGE",
2148
+ processEvent2
2149
+ );
2150
+ if (handler)
2151
+ handler({ value: selectedValue });
2152
+ };
2153
+ return /* @__PURE__ */ jsxs(
2154
+ "div",
2155
+ {
2156
+ className: cn("grid gap-1.5", className),
2157
+ children: [
2158
+ label && /* @__PURE__ */ jsx(Label2, { className: "mb-1", children: label }),
2159
+ /* @__PURE__ */ jsx(
2160
+ RadioGroup,
2161
+ {
2162
+ name,
2163
+ value,
2164
+ onValueChange: handleValueChange,
2165
+ disabled,
2166
+ className: "flex flex-col space-y-1",
2167
+ children: options.map((option) => /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
2168
+ /* @__PURE__ */ jsx(
2169
+ RadioGroupItem,
2170
+ {
2171
+ value: option.value,
2172
+ id: `${name}-${option.value}`
2173
+ }
2174
+ ),
2175
+ /* @__PURE__ */ jsx(
2176
+ Label2,
2177
+ {
2178
+ htmlFor: `${name}-${option.value}`,
2179
+ className: "cursor-pointer",
2180
+ children: option.label
2181
+ }
2182
+ )
2183
+ ] }, option.value))
2184
+ }
2185
+ )
2186
+ ]
2187
+ },
2188
+ key
2189
+ );
2190
+ },
2191
+ Tabs: (node, processEvent2) => {
2192
+ const { key, ...propsWithoutKey } = node.props || {};
2193
+ const rawTabs = getSafeBinding(
2194
+ node.bindings,
2195
+ "tabs",
2196
+ isArrayOf(isTabObject),
2197
+ []
2198
+ );
2199
+ const defaultValue = getSafeProp(
2200
+ propsWithoutKey,
2201
+ "defaultValue",
2202
+ isString,
2203
+ rawTabs[0]?.value || ""
2204
+ );
2205
+ const className = getSafeProp(propsWithoutKey, "className", isString, "");
2206
+ const handleValueChange = (value) => {
2207
+ const handler = createEventHandler(
2208
+ node,
2209
+ "onValueChange",
2210
+ "CHANGE",
2211
+ processEvent2
2212
+ );
2213
+ if (handler)
2214
+ handler({ value });
2215
+ };
2216
+ return /* @__PURE__ */ jsxs(
2217
+ Tabs,
2218
+ {
2219
+ defaultValue,
2220
+ onValueChange: handleValueChange,
2221
+ className: cn("autoui-tabs w-full", className),
2222
+ "data-id": node.id,
2223
+ children: [
2224
+ /* @__PURE__ */ jsx(TabsList, { children: rawTabs.map((tab) => /* @__PURE__ */ jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value)) }),
2225
+ rawTabs.map((tab) => /* @__PURE__ */ jsx(TabsContent, { value: tab.value, children: tab.content ? renderNode(tab.content, processEvent2) : null }, tab.value))
2226
+ ]
2227
+ },
2228
+ key
2229
+ );
2230
+ },
2231
+ Dialog: (node, processEvent2) => {
2232
+ const isOpen = getSafeBinding(
2233
+ node.bindings,
2234
+ "open",
2235
+ isBoolean,
2236
+ getSafeProp(node.props, "open", isBoolean, false)
2237
+ );
2238
+ const {
2239
+ title,
2240
+ description,
2241
+ className,
2242
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2243
+ style: _styleProp,
2244
+ key,
2245
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2246
+ open: _openProp,
2247
+ ...restProps
2248
+ } = node.props || {};
2249
+ const children = node.children?.map(
2250
+ (child) => React.cloneElement(renderNode(child, processEvent2), { key: child.id })
2251
+ );
2252
+ const handleOpenChange = (open) => {
2253
+ if (!open) {
2254
+ const handler = createEventHandler(
2255
+ node,
2256
+ "onClose",
2257
+ // Assumed event name in UISpec
2258
+ "CLICK",
2259
+ // Use CLICK as the event type for closing dialogs
2260
+ processEvent2
2261
+ );
2262
+ if (handler) {
2263
+ handler({});
2264
+ }
2265
+ }
2266
+ };
2267
+ console.log(
2268
+ `[Adapter Debug] Rendering Dialog: id=${node.id}, props=`,
2269
+ node.props,
2270
+ `isOpen=${isOpen}`
2271
+ );
2272
+ return /* @__PURE__ */ jsx(
2273
+ Dialog,
2274
+ {
2275
+ open: isOpen,
2276
+ onOpenChange: handleOpenChange,
2277
+ children: /* @__PURE__ */ jsxs(
2278
+ DialogContent,
2279
+ {
2280
+ className: cn("autoui-dialog-content", className),
2281
+ ...restProps,
2282
+ "data-id": node.id,
2283
+ children: [
2284
+ (title || description) && /* @__PURE__ */ jsxs(DialogHeader, { children: [
2285
+ title && /* @__PURE__ */ jsx(DialogTitle, { children: title }),
2286
+ description && /* @__PURE__ */ jsx(DialogDescription, { children: description })
2287
+ ] }),
2288
+ children
2289
+ ]
2290
+ }
2291
+ )
2292
+ },
2293
+ key
2294
+ );
2295
+ },
2296
+ Heading: (node) => {
2297
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
2298
+ const text = getSafeProp(node.props, "text", isString, "Heading");
2299
+ let level = getSafeProp(
2300
+ node.props,
2301
+ "level",
2302
+ (v) => typeof v === "number" && v >= 1 && v <= 6,
2303
+ 2
2304
+ );
2305
+ if (typeof level !== "number" || level < 1 || level > 6) {
2306
+ level = 2;
863
2307
  }
864
- )
2308
+ const Tag = `h${level}`;
2309
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
2310
+ const headingStyles = {
2311
+ 1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
2312
+ 2: "scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0",
2313
+ 3: "scroll-m-20 text-2xl font-semibold tracking-tight",
2314
+ 4: "scroll-m-20 text-xl font-semibold tracking-tight"
2315
+ // Add styles for h5, h6 if needed, using text-lg, text-base etc.
2316
+ }[level] || "text-lg font-semibold";
2317
+ return /* @__PURE__ */ jsx(
2318
+ Tag,
2319
+ {
2320
+ className: cn(headingStyles, className),
2321
+ style,
2322
+ ...restProps,
2323
+ children: text
2324
+ },
2325
+ key
2326
+ );
2327
+ },
2328
+ Text: (node) => {
2329
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
2330
+ const text = getSafeProp(node.props, "text", isString, "Some text");
2331
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
2332
+ return /* @__PURE__ */ jsx(
2333
+ "p",
2334
+ {
2335
+ className: cn("leading-7", className),
2336
+ style,
2337
+ ...restProps,
2338
+ children: text
2339
+ },
2340
+ key
2341
+ );
2342
+ }
865
2343
  };
866
- function renderNode(node) {
867
- const Component = adapterMap[node.type];
868
- if (Component) {
869
- return Component(node);
870
- }
871
- return /* @__PURE__ */ jsxs("div", { className: "p-2 border border-red-300 rounded", children: [
872
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-red-500", children: [
873
- "Unsupported component: ",
874
- node.type
875
- ] }),
876
- node.children?.map((child) => renderNode(child))
877
- ] });
2344
+ function renderNode(node, processEvent2) {
2345
+ const mappedComponent = adapterMap[node.node_type];
2346
+ if (mappedComponent) {
2347
+ return mappedComponent(node, processEvent2);
2348
+ }
2349
+ console.warn(`Unknown node type: ${node.node_type}`);
2350
+ return React.createElement(
2351
+ Container,
2352
+ {},
2353
+ `Unknown node type: ${node.node_type}`
2354
+ );
878
2355
  }
879
- async function renderNode2(node, adapter = "shadcn") {
2356
+ var renderedNodesCache = /* @__PURE__ */ new Map();
2357
+ var MAX_CACHE_SIZE = 10;
2358
+ var CACHE_TTL = 5e3;
2359
+ async function renderNode2(node, adapter = "shadcn", processEvent2) {
880
2360
  const startTime = Date.now();
2361
+ const nodeId = node.id;
2362
+ const cachedItem = renderedNodesCache.get(nodeId);
2363
+ if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
2364
+ return cachedItem.element;
2365
+ }
881
2366
  await systemEvents.emit(
882
2367
  createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
883
2368
  );
884
2369
  let result;
885
2370
  switch (adapter) {
886
2371
  case "shadcn":
887
- result = renderNode(node);
2372
+ result = renderNode(node, processEvent2);
888
2373
  break;
889
2374
  default:
890
2375
  console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
891
- result = renderNode(node);
2376
+ result = renderNode(node, processEvent2);
892
2377
  }
893
2378
  await systemEvents.emit(
894
2379
  createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
@@ -896,13 +2381,23 @@ async function renderNode2(node, adapter = "shadcn") {
896
2381
  renderTimeMs: Date.now() - startTime
897
2382
  })
898
2383
  );
2384
+ renderedNodesCache.set(nodeId, {
2385
+ element: result,
2386
+ timestamp: startTime
2387
+ });
2388
+ if (renderedNodesCache.size > MAX_CACHE_SIZE) {
2389
+ const oldestKey = [...renderedNodesCache.entries()].sort(
2390
+ ([, a], [, b]) => a.timestamp - b.timestamp
2391
+ )[0][0];
2392
+ renderedNodesCache.delete(oldestKey);
2393
+ }
899
2394
  return result;
900
2395
  }
901
2396
  function renderShimmer(node, adapter = "shadcn") {
902
2397
  if (!node) {
903
2398
  return /* @__PURE__ */ jsx(ShimmerBlock, {});
904
2399
  }
905
- switch (node.type) {
2400
+ switch (node.node_type) {
906
2401
  case "ListView":
907
2402
  return /* @__PURE__ */ jsx(ShimmerTable, { rows: 3 });
908
2403
  case "Detail":
@@ -915,6 +2410,16 @@ function renderShimmer(node, adapter = "shadcn") {
915
2410
  }
916
2411
 
917
2412
  // src/core/bindings.ts
2413
+ var bindingsCache = /* @__PURE__ */ new Map();
2414
+ var MAX_CACHE_SIZE2 = 50;
2415
+ var CACHE_TTL2 = 2e3;
2416
+ var nodeCacheTimestamps = /* @__PURE__ */ new Map();
2417
+ function hashDataContext(context) {
2418
+ return JSON.stringify(context);
2419
+ }
2420
+ function createCacheKey(nodeId, context) {
2421
+ return `${nodeId}:${hashDataContext(context)}`;
2422
+ }
918
2423
  function getValueByPath(context, path) {
919
2424
  const parts = path.split(".");
920
2425
  let current = context;
@@ -930,68 +2435,265 @@ function getValueByPath(context, path) {
930
2435
  return current;
931
2436
  }
932
2437
  function setValueByPath(context, path, value) {
2438
+ if (!path) {
2439
+ return context;
2440
+ }
933
2441
  const result = { ...context };
934
2442
  const parts = path.split(".");
935
2443
  let current = result;
936
2444
  for (let i = 0; i < parts.length - 1; i++) {
937
2445
  const part = parts[i];
938
- if (!(part in current) || current[part] === null || current[part] === void 0) {
939
- current[part] = {};
2446
+ if (typeof current !== "object" || current === null) {
2447
+ console.warn(
2448
+ `setValueByPath: Cannot traverse path "${path}". Parent segment "${parts[i - 1] || "(root)"}" is not an object.`
2449
+ );
2450
+ return context;
940
2451
  }
941
- current = current[part];
942
- if (typeof current !== "object") {
943
- current = {};
2452
+ const currentAsObject = current;
2453
+ const nextPartValue = currentAsObject[part];
2454
+ if (nextPartValue === void 0 || nextPartValue === null) {
2455
+ currentAsObject[part] = {};
2456
+ } else if (typeof nextPartValue !== "object") {
2457
+ console.warn(
2458
+ `setValueByPath: Cannot create nested path "${path}". Segment "${part}" is not an object.`
2459
+ );
2460
+ return context;
2461
+ } else {
2462
+ currentAsObject[part] = { ...nextPartValue };
944
2463
  }
2464
+ current = currentAsObject[part];
945
2465
  }
946
2466
  const lastPart = parts[parts.length - 1];
947
- current[lastPart] = value;
2467
+ if (typeof current === "object" && current !== null) {
2468
+ current[lastPart] = value;
2469
+ } else {
2470
+ console.warn(
2471
+ `setValueByPath: Could not set value for path "${path}". Final segment parent is not an object.`
2472
+ );
2473
+ return context;
2474
+ }
948
2475
  return result;
949
2476
  }
950
- function processBinding(binding, context) {
2477
+ function processBinding(binding, context, itemData) {
951
2478
  if (typeof binding === "string") {
952
- return getValueByPath(context, binding);
2479
+ const exactMatchArr = binding.match(/^{{(.*)}}$/);
2480
+ const pathInsideExact = exactMatchArr ? exactMatchArr[1].trim() : null;
2481
+ if (pathInsideExact !== null && !pathInsideExact.includes("{{") && !pathInsideExact.includes("}}")) {
2482
+ const pathToResolve = pathInsideExact;
2483
+ let resolvedValue = void 0;
2484
+ console.log(
2485
+ `[processBinding Debug] Processing EXACT template: "${binding}", Path: "${pathToResolve}", Has itemData: ${!!itemData}`
2486
+ );
2487
+ if (itemData) {
2488
+ try {
2489
+ console.log(
2490
+ `[processBinding Debug] itemData content (EXACT):`,
2491
+ JSON.parse(JSON.stringify(itemData))
2492
+ );
2493
+ } catch {
2494
+ }
2495
+ }
2496
+ if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
2497
+ if (pathToResolve.startsWith("item.")) {
2498
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
2499
+ } else {
2500
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
2501
+ }
2502
+ } else if (itemData && pathToResolve in itemData) {
2503
+ resolvedValue = getValueByPath(itemData, pathToResolve);
2504
+ }
2505
+ if (resolvedValue === void 0) {
2506
+ resolvedValue = getValueByPath(context, pathToResolve);
2507
+ }
2508
+ return resolvedValue;
2509
+ } else if (binding.includes("{{") && binding.includes("}}")) {
2510
+ console.log(
2511
+ `[processBinding Debug] Processing EMBEDDED templates: "${binding}", Has itemData: ${!!itemData}`
2512
+ );
2513
+ if (itemData) {
2514
+ try {
2515
+ console.log(
2516
+ `[processBinding Debug] itemData content (EMBEDDED):`,
2517
+ JSON.parse(JSON.stringify(itemData))
2518
+ );
2519
+ } catch {
2520
+ }
2521
+ }
2522
+ const resolvedString = binding.replaceAll(
2523
+ /{{(.*?)}}/g,
2524
+ // Non-greedy match inside braces
2525
+ (match, path) => {
2526
+ const trimmedPath = path.trim();
2527
+ let resolvedValue = void 0;
2528
+ if ((trimmedPath.startsWith("item.") || trimmedPath.startsWith("row.")) && itemData) {
2529
+ if (trimmedPath.startsWith("item.")) {
2530
+ resolvedValue = getValueByPath(
2531
+ itemData,
2532
+ trimmedPath.substring(5)
2533
+ );
2534
+ } else {
2535
+ resolvedValue = getValueByPath(
2536
+ itemData,
2537
+ trimmedPath.substring(4)
2538
+ );
2539
+ }
2540
+ } else if (itemData && trimmedPath in itemData) {
2541
+ resolvedValue = getValueByPath(itemData, trimmedPath);
2542
+ }
2543
+ if (resolvedValue === void 0) {
2544
+ resolvedValue = getValueByPath(context, trimmedPath);
2545
+ }
2546
+ return resolvedValue === null || resolvedValue === void 0 ? "" : String(resolvedValue);
2547
+ }
2548
+ );
2549
+ return resolvedString;
2550
+ } else {
2551
+ const pathToResolve = binding;
2552
+ let resolvedValue = void 0;
2553
+ console.log(
2554
+ `[processBinding Debug] Processing PATH string: "${pathToResolve}", Has itemData: ${!!itemData}`
2555
+ );
2556
+ if (itemData && !pathToResolve.includes(".") && pathToResolve in itemData) {
2557
+ resolvedValue = getValueByPath(itemData, pathToResolve);
2558
+ }
2559
+ if (resolvedValue === void 0) {
2560
+ resolvedValue = getValueByPath(context, pathToResolve);
2561
+ }
2562
+ return resolvedValue;
2563
+ }
953
2564
  }
954
2565
  if (Array.isArray(binding)) {
955
- return binding.map((item) => processBinding(item, context));
2566
+ return binding.map((item) => processBinding(item, context, itemData));
956
2567
  }
957
2568
  if (binding !== null && typeof binding === "object") {
958
2569
  const result = {};
959
2570
  for (const [key, value] of Object.entries(binding)) {
960
- result[key] = processBinding(value, context);
2571
+ result[key] = processBinding(value, context, itemData);
961
2572
  }
962
2573
  return result;
963
2574
  }
964
2575
  return binding;
965
2576
  }
966
- async function resolveBindings(node, context) {
967
- await systemEvents.emit(
968
- createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, { layout: node })
969
- );
2577
+ async function resolveBindings(node, context, itemData) {
2578
+ const effectiveContext = itemData ? { ...context, item: itemData } : context;
2579
+ const currentTime = Date.now();
2580
+ const cacheKey = createCacheKey(node.id, effectiveContext);
2581
+ const cachedNode = bindingsCache.get(cacheKey);
2582
+ const cachedTimestamp = nodeCacheTimestamps.get(cacheKey);
2583
+ if (cachedNode && cachedTimestamp && currentTime - cachedTimestamp < CACHE_TTL2) {
2584
+ return cachedNode;
2585
+ }
2586
+ if (!itemData) {
2587
+ await systemEvents.emit(
2588
+ createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
2589
+ layout: node
2590
+ })
2591
+ );
2592
+ }
970
2593
  const result = {
971
2594
  ...node,
972
- props: node.props ? { ...node.props } : void 0,
973
- events: node.events ? { ...node.events } : void 0
2595
+ props: node.props ? JSON.parse(JSON.stringify(node.props)) : null,
2596
+ events: node.events ? JSON.parse(JSON.stringify(node.events)) : null,
2597
+ bindings: node.bindings ? JSON.parse(JSON.stringify(node.bindings)) : null,
2598
+ children: null
2599
+ // Initialize children to null
974
2600
  };
2601
+ const resolvedBindings = {};
975
2602
  if (node.bindings) {
976
- for (const [key, binding] of Object.entries(node.bindings)) {
977
- const value = processBinding(binding, context);
978
- if (value !== void 0) {
979
- if (!result.props) {
2603
+ for (const [key, bindingValue] of Object.entries(node.bindings)) {
2604
+ const resolvedValue = processBinding(bindingValue, context, itemData);
2605
+ resolvedBindings[key] = resolvedValue;
2606
+ if (resolvedValue !== void 0) {
2607
+ if (!result.props)
980
2608
  result.props = {};
981
- }
982
- result.props[key] = value;
2609
+ result.props[key] = resolvedValue;
983
2610
  }
984
2611
  }
985
2612
  }
986
- if (node.children) {
987
- result.children = await Promise.all(node.children.map((child) => resolveBindings(child, context)));
2613
+ result.bindings = null;
2614
+ if (node.events) {
2615
+ result.events = processBinding(
2616
+ node.events,
2617
+ context,
2618
+ itemData
2619
+ );
2620
+ } else {
2621
+ result.events = null;
2622
+ }
2623
+ const dataBindingValue = resolvedBindings["data"] ?? resolvedBindings["items"];
2624
+ if ((node.node_type === "ListView" || node.node_type === "Table") && Array.isArray(dataBindingValue) && node.children && node.children.length > 0) {
2625
+ const templateChild = node.children[0];
2626
+ const mappedChildren = await Promise.all(
2627
+ dataBindingValue.map(async (currentItemData, index) => {
2628
+ try {
2629
+ if (typeof currentItemData !== "object" || currentItemData === null) {
2630
+ console.warn(
2631
+ `List item at index ${index} for node ${node.id} is not an object:`,
2632
+ currentItemData
2633
+ );
2634
+ return null;
2635
+ }
2636
+ const currentItemAsRecord = currentItemData;
2637
+ const itemId = currentItemAsRecord.id;
2638
+ const instanceId = `${templateChild.id}-${itemId || index}`;
2639
+ const childNodeInstance = JSON.parse(
2640
+ JSON.stringify(templateChild)
2641
+ );
2642
+ childNodeInstance.id = instanceId;
2643
+ const resolvedChild = await resolveBindings(
2644
+ childNodeInstance,
2645
+ context,
2646
+ currentItemAsRecord
2647
+ );
2648
+ if (!resolvedChild.props)
2649
+ resolvedChild.props = {};
2650
+ resolvedChild.props.key = itemId || `${node.id}-item-${index}`;
2651
+ return resolvedChild;
2652
+ } catch (error) {
2653
+ console.error(
2654
+ `[resolveBindings Error] Error processing item at index ${index} for node ${node.id}:`,
2655
+ error,
2656
+ "Item Data:",
2657
+ currentItemData
2658
+ );
2659
+ return null;
2660
+ }
2661
+ })
2662
+ );
2663
+ result.children = mappedChildren.filter(
2664
+ (child) => child !== null
2665
+ );
2666
+ } else if (node.children && node.children.length > 0) {
2667
+ result.children = await Promise.all(
2668
+ node.children.map((child) => resolveBindings(child, context, itemData))
2669
+ );
2670
+ } else {
2671
+ result.children = [];
2672
+ }
2673
+ if (!itemData) {
2674
+ await systemEvents.emit(
2675
+ createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
2676
+ originalLayout: node,
2677
+ resolvedLayout: result
2678
+ })
2679
+ );
2680
+ }
2681
+ bindingsCache.set(cacheKey, result);
2682
+ nodeCacheTimestamps.set(cacheKey, currentTime);
2683
+ if (bindingsCache.size > MAX_CACHE_SIZE2) {
2684
+ let oldestKey = null;
2685
+ let oldestTimestamp = currentTime;
2686
+ for (const [key, timestamp] of nodeCacheTimestamps.entries()) {
2687
+ if (timestamp < oldestTimestamp) {
2688
+ oldestTimestamp = timestamp;
2689
+ oldestKey = key;
2690
+ }
2691
+ }
2692
+ if (oldestKey) {
2693
+ bindingsCache.delete(oldestKey);
2694
+ nodeCacheTimestamps.delete(oldestKey);
2695
+ }
988
2696
  }
989
- await systemEvents.emit(
990
- createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
991
- originalLayout: node,
992
- resolvedLayout: result
993
- })
994
- );
995
2697
  return result;
996
2698
  }
997
2699
  function executeAction(action, targetId, payload, context = {}, layoutTree) {
@@ -1027,7 +2729,7 @@ var EventManager = class {
1027
2729
  }
1028
2730
  /**
1029
2731
  * Register a hook for specific event types
1030
- *
2732
+ *
1031
2733
  * @param eventTypes - Event types to register for, or 'all' for all events
1032
2734
  * @param hook - Hook function to execute
1033
2735
  * @returns Unregister function
@@ -1060,7 +2762,7 @@ var EventManager = class {
1060
2762
  }
1061
2763
  /**
1062
2764
  * Process an event through all registered hooks
1063
- *
2765
+ *
1064
2766
  * @param event - The UI event to process
1065
2767
  * @returns Whether the default action should proceed
1066
2768
  */
@@ -1106,6 +2808,45 @@ function createEventHook(eventTypes, hook, options) {
1106
2808
  }
1107
2809
  };
1108
2810
  }
2811
+
2812
+ // src/core/component-detection.ts
2813
+ function areShadcnComponentsAvailable() {
2814
+ try {
2815
+ init_button();
2816
+ return true;
2817
+ } catch (error) {
2818
+ return false;
2819
+ }
2820
+ }
2821
+ function getMissingComponentsMessage() {
2822
+ return `Missing required shadcn components. Please run:
2823
+ > npm run setup-shadcn`;
2824
+ }
2825
+ function correctListBindingsRecursive(node, dataContext) {
2826
+ const correctedNode = JSON.parse(JSON.stringify(node));
2827
+ if ((correctedNode.node_type === "ListView" || correctedNode.node_type === "Table") && correctedNode.bindings?.data) {
2828
+ const bindingPath = correctedNode.bindings.data;
2829
+ if (typeof bindingPath === "string") {
2830
+ const pathSegments = bindingPath.split(".");
2831
+ const mainKey = pathSegments[0];
2832
+ if (pathSegments.length === 1) {
2833
+ const potentialDataContextEntry = dataContext[mainKey];
2834
+ if (potentialDataContextEntry && typeof potentialDataContextEntry === "object" && potentialDataContextEntry !== null && "data" in potentialDataContextEntry && Array.isArray(potentialDataContextEntry.data)) {
2835
+ correctedNode.bindings.data = `${mainKey}.data`;
2836
+ console.log(
2837
+ `[AutoUI Debug] Corrected list binding for node '${correctedNode.id}': from '${mainKey}' to '${mainKey}.data'`
2838
+ );
2839
+ }
2840
+ }
2841
+ }
2842
+ }
2843
+ if (correctedNode.children) {
2844
+ correctedNode.children = correctedNode.children.map(
2845
+ (child) => correctListBindingsRecursive(child, dataContext)
2846
+ );
2847
+ }
2848
+ return correctedNode;
2849
+ }
1109
2850
  var AutoUI = ({
1110
2851
  schema,
1111
2852
  goal,
@@ -1115,17 +2856,23 @@ var AutoUI = ({
1115
2856
  eventHooks,
1116
2857
  systemEventHooks,
1117
2858
  debugMode = false,
1118
- mockMode = true,
1119
- databaseConfig,
2859
+ mockMode = false,
1120
2860
  planningConfig,
1121
2861
  integration = {},
1122
2862
  scope = {},
1123
- enablePartialUpdates = false
2863
+ enablePartialUpdates = false,
2864
+ openaiApiKey
1124
2865
  }) => {
1125
- const [schemaAdapterInstance, setSchemaAdapterInstance] = useState(null);
2866
+ const [schemaAdapterInstance] = useState(null);
1126
2867
  const [dataContext, setDataContext] = useState({});
2868
+ const [componentsAvailable, setComponentsAvailable] = useState(true);
1127
2869
  const effectiveSchema = schema;
1128
2870
  const scopedGoal = goal;
2871
+ useEffect(() => {
2872
+ if (componentAdapter === "shadcn") {
2873
+ setComponentsAvailable(areShadcnComponentsAvailable());
2874
+ }
2875
+ }, [componentAdapter]);
1129
2876
  useEffect(() => {
1130
2877
  const unregisters = [];
1131
2878
  if (systemEventHooks) {
@@ -1133,7 +2880,10 @@ var AutoUI = ({
1133
2880
  if (!hooks)
1134
2881
  return;
1135
2882
  hooks.forEach((hook) => {
1136
- const unregister = systemEvents.on(eventType, hook);
2883
+ const unregister = systemEvents.on(
2884
+ eventType,
2885
+ hook
2886
+ );
1137
2887
  unregisters.push(unregister);
1138
2888
  });
1139
2889
  });
@@ -1142,7 +2892,9 @@ var AutoUI = ({
1142
2892
  const debugHook = (event) => {
1143
2893
  console.debug(`[AutoUI Debug] System Event:`, event);
1144
2894
  };
1145
- Object.values(SystemEventType).forEach((eventType) => {
2895
+ Object.values(SystemEventType).filter(
2896
+ (eventType) => eventType !== "RENDER_START" /* RENDER_START */ && eventType !== "BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */
2897
+ ).forEach((eventType) => {
1146
2898
  const unregister = systemEvents.on(eventType, debugHook);
1147
2899
  unregisters.push(unregister);
1148
2900
  });
@@ -1181,7 +2933,8 @@ var AutoUI = ({
1181
2933
  planningConfig,
1182
2934
  router: void 0,
1183
2935
  dataContext,
1184
- enablePartialUpdates
2936
+ enablePartialUpdates,
2937
+ openaiApiKey
1185
2938
  });
1186
2939
  const eventManagerRef = useRef(new EventManager());
1187
2940
  useEffect(() => {
@@ -1189,95 +2942,153 @@ var AutoUI = ({
1189
2942
  return;
1190
2943
  const unregisters = [];
1191
2944
  if (eventHooks.all) {
1192
- const unregister = eventManagerRef.current.register("all", async (ctx) => {
1193
- for (const hook of eventHooks.all || []) {
1194
- await hook(ctx);
1195
- if (ctx.isPropagationStopped())
1196
- break;
2945
+ const unregister = eventManagerRef.current.register(
2946
+ "all",
2947
+ async (ctx) => {
2948
+ for (const hook of eventHooks.all || []) {
2949
+ await hook(ctx);
2950
+ if (ctx.isPropagationStopped())
2951
+ break;
2952
+ }
1197
2953
  }
1198
- });
2954
+ );
1199
2955
  unregisters.push(unregister);
1200
2956
  }
1201
2957
  Object.entries(eventHooks).forEach(([type, hooks]) => {
1202
2958
  if (type === "all" || !hooks)
1203
2959
  return;
1204
- const unregister = eventManagerRef.current.register([type], async (ctx) => {
1205
- for (const hook of hooks) {
1206
- await hook(ctx);
1207
- if (ctx.isPropagationStopped())
1208
- break;
2960
+ const unregister = eventManagerRef.current.register(
2961
+ [type],
2962
+ async (ctx) => {
2963
+ for (const hook of hooks) {
2964
+ await hook(ctx);
2965
+ if (ctx.isPropagationStopped())
2966
+ break;
2967
+ }
1209
2968
  }
1210
- });
2969
+ );
1211
2970
  unregisters.push(unregister);
1212
2971
  });
1213
2972
  return () => {
1214
2973
  unregisters.forEach((unregister) => unregister());
1215
2974
  };
1216
2975
  }, [eventHooks]);
1217
- useCallback(async (event) => {
1218
- const shouldProceed = await eventManagerRef.current.processEvent(event);
1219
- if (onEvent) {
1220
- onEvent(event);
1221
- }
1222
- if (!shouldProceed) {
1223
- console.info("Event processing was prevented by hooks", event);
1224
- return;
1225
- }
1226
- const findNodeById2 = (node, id) => {
1227
- if (!node)
1228
- return void 0;
1229
- if (node.id === id)
1230
- return node;
1231
- if (node.children) {
1232
- for (const child of node.children) {
1233
- const found = findNodeById2(child, id);
1234
- if (found)
1235
- return found;
2976
+ const processEvent2 = useCallback(
2977
+ async (event) => {
2978
+ const shouldProceed = await eventManagerRef.current.processEvent(event);
2979
+ if (onEvent) {
2980
+ onEvent(event);
2981
+ }
2982
+ if (!shouldProceed) {
2983
+ console.info("Event processing was prevented by hooks", event);
2984
+ return;
2985
+ }
2986
+ const findNodeById2 = (node, id) => {
2987
+ if (!node)
2988
+ return void 0;
2989
+ if (node.id === id)
2990
+ return node;
2991
+ if (node.children) {
2992
+ for (const child of node.children) {
2993
+ const found = findNodeById2(child, id);
2994
+ if (found)
2995
+ return found;
2996
+ }
1236
2997
  }
2998
+ return void 0;
2999
+ };
3000
+ const sourceNode = findNodeById2(state.layout, event.nodeId);
3001
+ if (!sourceNode) {
3002
+ console.warn(`Node not found for event: ${event.nodeId}`);
3003
+ handleEvent(event);
3004
+ return;
1237
3005
  }
1238
- return void 0;
1239
- };
1240
- const sourceNode = findNodeById2(state.layout, event.nodeId);
1241
- if (!sourceNode) {
1242
- console.warn(`Node not found for event: ${event.nodeId}`);
1243
- handleEvent(event);
1244
- return;
1245
- }
1246
- const eventConfig = sourceNode.events?.[event.type];
1247
- if (!eventConfig) {
1248
- console.warn(`No event config found for ${event.type} on node ${event.nodeId}`);
3006
+ const eventConfig = sourceNode.events?.[event.type];
3007
+ if (!eventConfig) {
3008
+ console.warn(
3009
+ `No event config found for ${event.type} on node ${event.nodeId}`
3010
+ );
3011
+ handleEvent(event);
3012
+ return;
3013
+ }
3014
+ const newContext = executeAction(
3015
+ eventConfig.action,
3016
+ eventConfig.target || "",
3017
+ // Provide empty string as fallback if target is null
3018
+ {
3019
+ ...eventConfig.payload,
3020
+ ...event.payload
3021
+ },
3022
+ dataContext,
3023
+ state.layout || void 0
3024
+ );
3025
+ setDataContext(newContext);
1249
3026
  handleEvent(event);
1250
- return;
1251
- }
1252
- const newContext = executeAction(
1253
- eventConfig.action,
1254
- eventConfig.target,
1255
- {
1256
- ...eventConfig.payload,
1257
- ...event.payload
1258
- },
1259
- dataContext,
1260
- state.layout
1261
- );
1262
- setDataContext(newContext);
1263
- handleEvent(event);
1264
- }, [dataContext, handleEvent, onEvent, state.layout]);
1265
- const [resolvedLayout, setResolvedLayout] = useState(void 0);
1266
- const [renderedNode, setRenderedNode] = useState(null);
1267
- useEffect(() => {
1268
- if (state.layout) {
1269
- resolveBindings(state.layout, dataContext).then((resolved) => setResolvedLayout(resolved)).catch((err) => console.error("Error resolving bindings:", err));
3027
+ },
3028
+ [dataContext, handleEvent, onEvent, state.layout]
3029
+ );
3030
+ const [resolvedLayout, setResolvedLayout] = useState(
3031
+ void 0
3032
+ );
3033
+ const [renderedNode, setRenderedNode] = useState(
3034
+ null
3035
+ );
3036
+ const resolveLayoutBindings = useCallback(async () => {
3037
+ if (state.layout && dataContext) {
3038
+ console.log(
3039
+ "[AutoUI Debug] DataContext before resolving bindings:",
3040
+ JSON.stringify(dataContext, null, 2)
3041
+ );
3042
+ console.log(
3043
+ "[AutoUI Debug] Raw layout before resolving (from planner):",
3044
+ JSON.stringify(state.layout, null, 2)
3045
+ );
3046
+ const correctedLayout = correctListBindingsRecursive(
3047
+ state.layout,
3048
+ dataContext
3049
+ );
3050
+ console.log(
3051
+ "[AutoUI Debug] Layout after binding correction (before resolving):",
3052
+ JSON.stringify(correctedLayout, null, 2)
3053
+ );
3054
+ const resolved = await resolveBindings(correctedLayout, dataContext);
3055
+ setResolvedLayout(resolved);
3056
+ console.log(
3057
+ "[AutoUI Debug] Resolved layout after bindings:",
3058
+ JSON.stringify(resolved, null, 2)
3059
+ );
1270
3060
  } else {
1271
3061
  setResolvedLayout(void 0);
1272
3062
  }
1273
3063
  }, [state.layout, dataContext]);
1274
3064
  useEffect(() => {
3065
+ resolveLayoutBindings();
3066
+ }, [resolveLayoutBindings]);
3067
+ const renderResolvedLayout = useCallback(async () => {
1275
3068
  if (resolvedLayout) {
1276
- renderNode2(resolvedLayout, componentAdapter).then((rendered) => setRenderedNode(rendered)).catch((err) => console.error("Error rendering node:", err));
3069
+ try {
3070
+ const rendered = await renderNode2(
3071
+ resolvedLayout,
3072
+ componentAdapter,
3073
+ processEvent2
3074
+ );
3075
+ setRenderedNode(rendered);
3076
+ } catch (err) {
3077
+ console.error("Error rendering node:", err);
3078
+ }
1277
3079
  } else {
1278
3080
  setRenderedNode(null);
1279
3081
  }
1280
- }, [resolvedLayout, componentAdapter]);
3082
+ }, [resolvedLayout, componentAdapter, processEvent2]);
3083
+ useEffect(() => {
3084
+ renderResolvedLayout();
3085
+ }, [renderResolvedLayout]);
3086
+ if (!componentsAvailable) {
3087
+ return /* @__PURE__ */ jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 text-red-700 rounded", children: [
3088
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: "Component Library Not Found" }),
3089
+ /* @__PURE__ */ jsx("p", { className: "text-sm whitespace-pre-line", children: getMissingComponentsMessage() })
3090
+ ] });
3091
+ }
1281
3092
  return /* @__PURE__ */ jsxs(
1282
3093
  "div",
1283
3094
  {
@@ -1296,9 +3107,22 @@ var AutoUI = ({
1296
3107
  // Render the resolved layout
1297
3108
  /* @__PURE__ */ jsx("div", { className: "autoui-content", children: renderedNode })
1298
3109
  ),
1299
- state.error && /* @__PURE__ */ jsxs("div", { className: "autoui-error", children: [
1300
- /* @__PURE__ */ jsx("p", { className: "autoui-error-title", children: "Error generating UI" }),
1301
- /* @__PURE__ */ jsx("p", { className: "autoui-error-message", children: state.error })
3110
+ state.error && /* @__PURE__ */ jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 dark:bg-red-900 dark:border-red-700 rounded-md", children: [
3111
+ /* @__PURE__ */ jsx("p", { className: "autoui-error-title text-lg font-semibold text-red-700 dark:text-red-300 mb-2", children: "Error generating UI" }),
3112
+ /* @__PURE__ */ jsx("p", { className: "autoui-error-message text-sm text-red-600 dark:text-red-300", children: state.error }),
3113
+ !mockMode && /* @__PURE__ */ jsxs("div", { className: "mt-4 text-sm text-red-600 dark:text-red-300", children: [
3114
+ /* @__PURE__ */ jsx("p", { children: "This could be because:" }),
3115
+ /* @__PURE__ */ jsxs("ul", { className: "list-disc pl-5 mt-2", children: [
3116
+ /* @__PURE__ */ jsx("li", { children: "Your OpenAI API key is missing or invalid" }),
3117
+ /* @__PURE__ */ jsx("li", { children: "The OpenAI service is experiencing issues" }),
3118
+ /* @__PURE__ */ jsx("li", { children: "Your API rate limit has been exceeded" })
3119
+ ] }),
3120
+ /* @__PURE__ */ jsxs("p", { className: "mt-2", children: [
3121
+ "Try setting ",
3122
+ /* @__PURE__ */ jsx("code", { children: "mockMode=true" }),
3123
+ " to use sample data instead."
3124
+ ] })
3125
+ ] })
1302
3126
  ] })
1303
3127
  ]
1304
3128
  }
@@ -1351,26 +3175,26 @@ var DrizzleAdapter = class {
1351
3175
  */
1352
3176
  mapDataType(drizzleType) {
1353
3177
  const typeMap = {
1354
- "serial": "integer",
1355
- "integer": "integer",
1356
- "int": "integer",
1357
- "bigint": "integer",
1358
- "text": "string",
1359
- "varchar": "string",
1360
- "char": "string",
1361
- "boolean": "boolean",
1362
- "bool": "boolean",
1363
- "timestamp": "datetime",
1364
- "timestamptz": "datetime",
1365
- "date": "date",
1366
- "time": "time",
1367
- "json": "object",
1368
- "jsonb": "object",
1369
- "real": "number",
1370
- "float": "number",
1371
- "double": "number",
1372
- "numeric": "number",
1373
- "decimal": "number"
3178
+ serial: "integer",
3179
+ integer: "integer",
3180
+ int: "integer",
3181
+ bigint: "integer",
3182
+ text: "string",
3183
+ varchar: "string",
3184
+ char: "string",
3185
+ boolean: "boolean",
3186
+ bool: "boolean",
3187
+ timestamp: "datetime",
3188
+ timestamptz: "datetime",
3189
+ date: "date",
3190
+ time: "time",
3191
+ json: "object",
3192
+ jsonb: "object",
3193
+ real: "number",
3194
+ float: "number",
3195
+ double: "number",
3196
+ numeric: "number",
3197
+ decimal: "number"
1374
3198
  };
1375
3199
  return typeMap[drizzleType.toLowerCase()] || "string";
1376
3200
  }
@@ -1413,37 +3237,122 @@ function createSchemaAdapter(options) {
1413
3237
  case "custom":
1414
3238
  return options.adapter;
1415
3239
  default:
1416
- throw new Error(`Unsupported schema adapter type: ${options.type}`);
1417
- }
1418
- }
1419
- async function generateComponent(schema) {
1420
- try {
1421
- const { text } = await generateText({
1422
- model: openai("gpt-4"),
1423
- prompt: `Generate a React component based on this data schema: ${JSON.stringify(schema)}`
1424
- });
1425
- return text;
1426
- } catch (error) {
1427
- console.error("Error generating component:", error);
1428
- throw error;
3240
+ throw new Error(
3241
+ `Unsupported schema adapter type: ${options.type}`
3242
+ );
1429
3243
  }
1430
3244
  }
1431
- async function generateUIDescription(schema) {
1432
- const { text } = await generateText({
1433
- model: openai("gpt-4"),
1434
- prompt: `Generate a React component description based on this data schema: ${JSON.stringify(schema)}.
1435
- Include what fields should be displayed and what user interactions might be needed.`
1436
- });
1437
- return text;
1438
- }
1439
- async function generateUIComponent(schema) {
1440
- const { text } = await generateText({
1441
- model: openai("gpt-4"),
1442
- prompt: `Generate a basic React component based on this data schema: ${JSON.stringify(schema)}.`
1443
- });
1444
- return text;
3245
+
3246
+ // src/ai-utils.ts
3247
+ var generateComponent = async (prompt) => {
3248
+ console.warn(
3249
+ "generateComponent is a placeholder and will be implemented in a future version"
3250
+ );
3251
+ return `<div>Generated Component for: ${prompt}</div>`;
3252
+ };
3253
+ var generateUIDescription = async (prompt) => {
3254
+ console.warn(
3255
+ "generateUIDescription is a placeholder and will be implemented in a future version"
3256
+ );
3257
+ return `Description for ${prompt}`;
3258
+ };
3259
+ var generateUIComponent = async (prompt) => {
3260
+ console.warn(
3261
+ "generateUIComponent is a placeholder and will be implemented in a future version"
3262
+ );
3263
+ return `<div>Generated UI Component for: ${prompt}</div>`;
3264
+ };
3265
+ function usePlanner(options) {
3266
+ const {
3267
+ schema,
3268
+ goal,
3269
+ openaiApiKey,
3270
+ userContext,
3271
+ initialLayout,
3272
+ mockMode: optionsMockMode
3273
+ } = options;
3274
+ const [layout, setLayout] = useState(
3275
+ initialLayout || void 0
3276
+ );
3277
+ const [loading, setLoading] = useState(false);
3278
+ const [error, setError] = useState(null);
3279
+ const router = options.router || createDefaultRouter();
3280
+ const dataContext = {};
3281
+ const initialFetchAttempted = useRef(false);
3282
+ const mockMode = optionsMockMode || !openaiApiKey;
3283
+ const generateInitialLayout = useCallback(async () => {
3284
+ setLoading(true);
3285
+ setError(null);
3286
+ try {
3287
+ const plannerInput2 = {
3288
+ schema,
3289
+ goal,
3290
+ userContext: userContext || null,
3291
+ history: null
3292
+ };
3293
+ let generatedLayout;
3294
+ if (mockMode) {
3295
+ console.warn(
3296
+ "Using mock planner in usePlanner hook (mockMode enabled or API key missing)."
3297
+ );
3298
+ generatedLayout = mockPlanner(plannerInput2);
3299
+ } else {
3300
+ generatedLayout = await callPlannerLLM(
3301
+ plannerInput2,
3302
+ openaiApiKey,
3303
+ void 0
3304
+ );
3305
+ }
3306
+ setLayout(generatedLayout);
3307
+ } catch (err) {
3308
+ setError(err instanceof Error ? err : new Error(String(err)));
3309
+ } finally {
3310
+ setLoading(false);
3311
+ }
3312
+ }, [schema, goal, userContext, openaiApiKey, mockMode]);
3313
+ const handleEvent = useCallback(
3314
+ async (event) => {
3315
+ if (!layout) {
3316
+ setError(new Error("Cannot handle event - no layout exists"));
3317
+ return;
3318
+ }
3319
+ setLoading(true);
3320
+ setError(null);
3321
+ try {
3322
+ const updatedLayout = await processEvent(
3323
+ event,
3324
+ router,
3325
+ schema,
3326
+ layout,
3327
+ dataContext,
3328
+ goal,
3329
+ userContext,
3330
+ openaiApiKey
3331
+ );
3332
+ setLayout(updatedLayout);
3333
+ } catch (err) {
3334
+ setError(err instanceof Error ? err : new Error(String(err)));
3335
+ } finally {
3336
+ setLoading(false);
3337
+ }
3338
+ },
3339
+ [layout, router, schema, dataContext, goal, userContext, openaiApiKey]
3340
+ );
3341
+ useEffect(() => {
3342
+ if (options.initialLayout === void 0 && layout === void 0 && !initialFetchAttempted.current) {
3343
+ initialFetchAttempted.current = true;
3344
+ generateInitialLayout();
3345
+ }
3346
+ }, [options.initialLayout, layout, generateInitialLayout]);
3347
+ return {
3348
+ layout,
3349
+ loading,
3350
+ error,
3351
+ handleEvent,
3352
+ generateInitialLayout
3353
+ };
1445
3354
  }
1446
3355
 
1447
- export { ActionRouter, ActionType, AutoUI, DrizzleAdapter, SystemEventType, createDefaultRouter, createEventHook, createSchemaAdapter, createSystemEvent, generateComponent, generateUIComponent, generateUIDescription, systemEvents, uiEvent, uiEventType, uiSpecNode };
3356
+ export { ActionRouter, ActionType, AutoUI, DrizzleAdapter, SystemEventType, createDefaultRouter, createEventHook, createSchemaAdapter, createSystemEvent, generateComponent, generateUIComponent, generateUIDescription, systemEvents, uiEvent, uiEventType, uiSpecNode, usePlanner };
1448
3357
  //# sourceMappingURL=out.js.map
1449
3358
  //# sourceMappingURL=index.mjs.map