autoui-react 0.0.3-alpha → 0.0.4-alpha

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