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