autoui-react 0.0.3-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 ADDED
@@ -0,0 +1,1449 @@
1
+ import { useState, useEffect, useRef, useCallback, useReducer } from 'react';
2
+ import { z } from 'zod';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { generateText } from 'ai';
5
+ import { openai } from '@ai-sdk/openai';
6
+
7
+ // src/core/reducer.ts
8
+ function cloneNode(node) {
9
+ return {
10
+ ...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))
15
+ };
16
+ }
17
+ function findNodeById(tree, nodeId) {
18
+ if (!tree)
19
+ return void 0;
20
+ if (tree.id === nodeId)
21
+ return tree;
22
+ if (tree.children) {
23
+ for (const child of tree.children) {
24
+ const found = findNodeById(child, nodeId);
25
+ if (found)
26
+ return found;
27
+ }
28
+ }
29
+ return void 0;
30
+ }
31
+ function updateNodeById(tree, nodeId, updater) {
32
+ const result = cloneNode(tree);
33
+ function findPath(node, id, currentPath = []) {
34
+ const newPath = [...currentPath, node];
35
+ if (node.id === id) {
36
+ return newPath;
37
+ }
38
+ if (node.children) {
39
+ for (const child of node.children) {
40
+ const path2 = findPath(child, id, newPath);
41
+ if (path2)
42
+ return path2;
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+ const path = findPath(result, nodeId);
48
+ if (!path)
49
+ return result;
50
+ const nodeToUpdate = path[path.length - 1];
51
+ const updatedNode = updater(nodeToUpdate);
52
+ if (path.length === 1) {
53
+ return updatedNode;
54
+ }
55
+ const parent = path[path.length - 2];
56
+ const updatedParent = {
57
+ ...parent,
58
+ children: parent.children?.map(
59
+ (child) => child.id === nodeId ? updatedNode : child
60
+ )
61
+ };
62
+ if (path.length === 2) {
63
+ return updatedParent;
64
+ }
65
+ return updateNodeById(
66
+ result,
67
+ parent.id,
68
+ () => updatedParent
69
+ );
70
+ }
71
+ function replaceNodeById(tree, nodeId, newNode) {
72
+ return updateNodeById(tree, nodeId, () => newNode);
73
+ }
74
+ function addChildNode(tree, parentId, newChild, index) {
75
+ return updateNodeById(tree, parentId, (node) => {
76
+ const children = node.children ? [...node.children] : [];
77
+ if (index !== void 0 && index >= 0 && index <= children.length) {
78
+ children.splice(index, 0, newChild);
79
+ } else {
80
+ children.push(newChild);
81
+ }
82
+ return {
83
+ ...node,
84
+ children
85
+ };
86
+ });
87
+ }
88
+ function removeNodeById(tree, nodeId) {
89
+ function findParent(node, id) {
90
+ if (node.children) {
91
+ if (node.children.some((child) => child.id === id)) {
92
+ return node;
93
+ }
94
+ for (const child of node.children) {
95
+ const parent2 = findParent(child, id);
96
+ if (parent2)
97
+ return parent2;
98
+ }
99
+ }
100
+ return null;
101
+ }
102
+ const result = cloneNode(tree);
103
+ if (result.id === nodeId) {
104
+ throw new Error("Cannot remove root node");
105
+ }
106
+ const parent = findParent(result, nodeId);
107
+ if (!parent)
108
+ return result;
109
+ return updateNodeById(result, parent.id, (node) => ({
110
+ ...node,
111
+ children: node.children?.filter((child) => child.id !== nodeId)
112
+ }));
113
+ }
114
+ function uiReducer(state, action) {
115
+ switch (action.type) {
116
+ case "UI_EVENT": {
117
+ return {
118
+ ...state,
119
+ loading: true,
120
+ history: [...state.history, action.event]
121
+ };
122
+ }
123
+ case "AI_RESPONSE": {
124
+ return {
125
+ ...state,
126
+ layout: action.node,
127
+ loading: false,
128
+ error: void 0
129
+ };
130
+ }
131
+ case "PARTIAL_UPDATE": {
132
+ if (!state.layout) {
133
+ return {
134
+ ...state,
135
+ layout: action.node,
136
+ loading: false,
137
+ error: void 0
138
+ };
139
+ }
140
+ if (action.nodeId === "root" || action.nodeId === state.layout.id) {
141
+ return {
142
+ ...state,
143
+ layout: action.node,
144
+ loading: false,
145
+ error: void 0
146
+ };
147
+ }
148
+ return {
149
+ ...state,
150
+ layout: replaceNodeById(state.layout, action.nodeId, action.node),
151
+ loading: false,
152
+ error: void 0
153
+ };
154
+ }
155
+ case "ADD_NODE": {
156
+ if (!state.layout) {
157
+ return state;
158
+ }
159
+ return {
160
+ ...state,
161
+ layout: addChildNode(
162
+ state.layout,
163
+ action.parentId,
164
+ action.node,
165
+ action.index
166
+ ),
167
+ loading: false,
168
+ error: void 0
169
+ };
170
+ }
171
+ case "REMOVE_NODE": {
172
+ if (!state.layout) {
173
+ return state;
174
+ }
175
+ return {
176
+ ...state,
177
+ layout: removeNodeById(state.layout, action.nodeId),
178
+ loading: false,
179
+ error: void 0
180
+ };
181
+ }
182
+ case "LOADING": {
183
+ return {
184
+ ...state,
185
+ loading: action.isLoading
186
+ };
187
+ }
188
+ case "ERROR": {
189
+ return {
190
+ ...state,
191
+ error: action.message,
192
+ loading: false
193
+ };
194
+ }
195
+ default:
196
+ return state;
197
+ }
198
+ }
199
+ var initialState = {
200
+ loading: true,
201
+ history: []
202
+ };
203
+
204
+ // src/core/action-router.ts
205
+ var ActionType = /* @__PURE__ */ ((ActionType2) => {
206
+ ActionType2["FULL_REFRESH"] = "FULL_REFRESH";
207
+ ActionType2["UPDATE_NODE"] = "UPDATE_NODE";
208
+ ActionType2["ADD_DROPDOWN"] = "ADD_DROPDOWN";
209
+ ActionType2["SHOW_DETAIL"] = "SHOW_DETAIL";
210
+ ActionType2["HIDE_DETAIL"] = "HIDE_DETAIL";
211
+ ActionType2["TOGGLE_STATE"] = "TOGGLE_STATE";
212
+ ActionType2["UPDATE_FORM"] = "UPDATE_FORM";
213
+ ActionType2["NAVIGATE"] = "NAVIGATE";
214
+ return ActionType2;
215
+ })(ActionType || {});
216
+ var ActionRouter = class {
217
+ constructor() {
218
+ this.routes = {};
219
+ }
220
+ /**
221
+ * Register a new action route
222
+ * @param eventType - UI event type to route
223
+ * @param config - Route configuration
224
+ */
225
+ registerRoute(eventType, config) {
226
+ if (!this.routes[eventType]) {
227
+ this.routes[eventType] = [];
228
+ }
229
+ this.routes[eventType].push(config);
230
+ }
231
+ /**
232
+ * Find the appropriate route for an event
233
+ * @param event - UI event
234
+ * @param layout - Current UI layout
235
+ * @param dataContext - Current data context
236
+ * @returns Route resolution or null if no match
237
+ */
238
+ resolveRoute(event, schema, layout, dataContext, goal, userContext) {
239
+ const routes = this.routes[event.type] || [];
240
+ if (routes.length === 0) {
241
+ return {
242
+ actionType: "FULL_REFRESH" /* FULL_REFRESH */,
243
+ targetNodeId: layout?.id || "root",
244
+ plannerInput: {
245
+ schema,
246
+ goal,
247
+ history: [event],
248
+ userContext
249
+ },
250
+ prompt: `Generate a new UI for the goal: "${goal}". The user just triggered: ${event.type} on node ${event.nodeId}`
251
+ };
252
+ }
253
+ const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
254
+ const nodeConfig = sourceNode?.events?.[event.type];
255
+ let matchingRoute;
256
+ if (nodeConfig) {
257
+ matchingRoute = routes.find(
258
+ (route) => route.actionType.toString() === nodeConfig.action
259
+ );
260
+ }
261
+ if (!matchingRoute) {
262
+ matchingRoute = routes[0];
263
+ }
264
+ const targetNodeId = nodeConfig?.target || matchingRoute.targetNodeId || event.nodeId;
265
+ const additionalContext = {};
266
+ if (matchingRoute.contextKeys) {
267
+ matchingRoute.contextKeys.forEach((key) => {
268
+ additionalContext[key] = dataContext[key];
269
+ });
270
+ }
271
+ if (sourceNode) {
272
+ additionalContext.sourceNode = sourceNode;
273
+ }
274
+ if (layout) {
275
+ const targetNode = findNodeById(layout, targetNodeId);
276
+ if (targetNode) {
277
+ additionalContext.targetNode = targetNode;
278
+ }
279
+ }
280
+ if (event.payload) {
281
+ additionalContext.eventPayload = event.payload;
282
+ }
283
+ if (nodeConfig?.payload) {
284
+ Object.entries(nodeConfig.payload).forEach(([key, value]) => {
285
+ additionalContext[key] = value;
286
+ });
287
+ }
288
+ const plannerInput2 = {
289
+ schema,
290
+ goal,
291
+ history: [event],
292
+ userContext: {
293
+ ...userContext,
294
+ ...additionalContext
295
+ }
296
+ };
297
+ const prompt = this.processTemplate(
298
+ matchingRoute.promptTemplate,
299
+ {
300
+ goal,
301
+ eventType: event.type,
302
+ nodeId: event.nodeId,
303
+ targetNodeId,
304
+ actionType: matchingRoute.actionType,
305
+ ...additionalContext
306
+ }
307
+ );
308
+ return {
309
+ actionType: matchingRoute.actionType,
310
+ targetNodeId,
311
+ plannerInput: plannerInput2,
312
+ prompt
313
+ };
314
+ }
315
+ /**
316
+ * Process a prompt template with variables
317
+ * @param template - Template string with ${var} placeholders
318
+ * @param values - Values to substitute
319
+ * @returns Processed string
320
+ */
321
+ processTemplate(template, values) {
322
+ return template.replace(/\${(\w+)}/g, (_, key) => {
323
+ return values[key] !== void 0 ? String(values[key]) : `\${${key}}`;
324
+ });
325
+ }
326
+ };
327
+ function createDefaultRouter() {
328
+ const router = new ActionRouter();
329
+ router.registerRoute("CLICK", {
330
+ actionType: "FULL_REFRESH" /* FULL_REFRESH */,
331
+ targetNodeId: "root",
332
+ promptTemplate: 'Generate a new UI for the goal: "${goal}". The user just clicked on node ${nodeId}'
333
+ });
334
+ router.registerRoute("CLICK", {
335
+ actionType: "SHOW_DETAIL" /* SHOW_DETAIL */,
336
+ targetNodeId: "${targetNodeId}",
337
+ promptTemplate: "Update the UI to show details for the selected item. Current node: ${nodeId}, Target node: ${targetNodeId}",
338
+ contextKeys: ["selected"]
339
+ });
340
+ router.registerRoute("CLICK", {
341
+ actionType: "NAVIGATE" /* NAVIGATE */,
342
+ targetNodeId: "root",
343
+ promptTemplate: "Navigate to a new view based on the user clicking ${nodeId}. Current goal: ${goal}"
344
+ });
345
+ router.registerRoute("CLICK", {
346
+ actionType: "ADD_DROPDOWN" /* ADD_DROPDOWN */,
347
+ targetNodeId: "${targetNodeId}",
348
+ promptTemplate: "Add a dropdown menu to node ${targetNodeId} with options relevant to the clicked element ${nodeId}"
349
+ });
350
+ router.registerRoute("CLICK", {
351
+ actionType: "TOGGLE_STATE" /* TOGGLE_STATE */,
352
+ targetNodeId: "${nodeId}",
353
+ promptTemplate: "Toggle the state of node ${nodeId} (e.g., expanded/collapsed, selected/unselected)"
354
+ });
355
+ router.registerRoute("CHANGE", {
356
+ actionType: "UPDATE_FORM" /* UPDATE_FORM */,
357
+ targetNodeId: "${targetNodeId}",
358
+ promptTemplate: "Update the form based on the user changing the value of ${nodeId}"
359
+ });
360
+ return router;
361
+ }
362
+ var uiEventType = z.enum([
363
+ "CLICK",
364
+ "CHANGE",
365
+ "SUBMIT",
366
+ "MOUSEOVER",
367
+ "MOUSEOUT",
368
+ "FOCUS",
369
+ "BLUR"
370
+ ]);
371
+ var uiEvent = z.object({
372
+ type: uiEventType,
373
+ nodeId: z.string(),
374
+ timestamp: z.number().optional(),
375
+ payload: z.record(z.any()).optional()
376
+ });
377
+ z.enum([
378
+ "AI_RESPONSE",
379
+ "ERROR"
380
+ ]);
381
+ var uiSpecNode = z.lazy(() => z.object({
382
+ 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
+ }));
395
+ z.discriminatedUnion("type", [
396
+ z.object({
397
+ type: z.literal("UI_EVENT"),
398
+ event: uiEvent
399
+ }),
400
+ z.object({
401
+ type: z.literal("AI_RESPONSE"),
402
+ node: uiSpecNode
403
+ }),
404
+ z.object({
405
+ type: z.literal("PARTIAL_UPDATE"),
406
+ nodeId: z.string(),
407
+ node: uiSpecNode
408
+ }),
409
+ z.object({
410
+ type: z.literal("ADD_NODE"),
411
+ parentId: z.string(),
412
+ node: uiSpecNode,
413
+ index: z.number().optional()
414
+ }),
415
+ z.object({
416
+ type: z.literal("REMOVE_NODE"),
417
+ nodeId: z.string()
418
+ }),
419
+ z.object({
420
+ type: z.literal("ERROR"),
421
+ message: z.string()
422
+ }),
423
+ z.object({
424
+ type: z.literal("LOADING"),
425
+ isLoading: z.boolean()
426
+ })
427
+ ]);
428
+ z.object({
429
+ layout: uiSpecNode.optional(),
430
+ loading: z.boolean(),
431
+ history: z.array(uiEvent),
432
+ error: z.string().optional()
433
+ });
434
+ z.object({
435
+ schema: z.record(z.unknown()),
436
+ goal: z.string(),
437
+ history: z.array(uiEvent).optional(),
438
+ userContext: z.record(z.unknown()).optional()
439
+ });
440
+
441
+ // src/core/system-events.ts
442
+ var SystemEventType = /* @__PURE__ */ ((SystemEventType2) => {
443
+ SystemEventType2["PLAN_START"] = "PLAN_START";
444
+ SystemEventType2["PLAN_PROMPT_CREATED"] = "PLAN_PROMPT_CREATED";
445
+ SystemEventType2["PLAN_RESPONSE_CHUNK"] = "PLAN_RESPONSE_CHUNK";
446
+ SystemEventType2["PLAN_COMPLETE"] = "PLAN_COMPLETE";
447
+ SystemEventType2["PLAN_ERROR"] = "PLAN_ERROR";
448
+ SystemEventType2["BINDING_RESOLUTION_START"] = "BINDING_RESOLUTION_START";
449
+ SystemEventType2["BINDING_RESOLUTION_COMPLETE"] = "BINDING_RESOLUTION_COMPLETE";
450
+ SystemEventType2["DATA_FETCH_START"] = "DATA_FETCH_START";
451
+ SystemEventType2["DATA_FETCH_COMPLETE"] = "DATA_FETCH_COMPLETE";
452
+ SystemEventType2["RENDER_START"] = "RENDER_START";
453
+ SystemEventType2["RENDER_COMPLETE"] = "RENDER_COMPLETE";
454
+ SystemEventType2["PREFETCH_START"] = "PREFETCH_START";
455
+ SystemEventType2["PREFETCH_COMPLETE"] = "PREFETCH_COMPLETE";
456
+ return SystemEventType2;
457
+ })(SystemEventType || {});
458
+ var SystemEventManager = class {
459
+ constructor() {
460
+ this.listeners = {};
461
+ }
462
+ /**
463
+ * Register a listener for a specific system event type
464
+ *
465
+ * @param eventType - The system event type to listen for
466
+ * @param listener - The listener function
467
+ * @returns Function to unregister the listener
468
+ */
469
+ on(eventType, listener) {
470
+ if (!this.listeners[eventType]) {
471
+ this.listeners[eventType] = [];
472
+ }
473
+ this.listeners[eventType]?.push(listener);
474
+ return () => {
475
+ if (this.listeners[eventType]) {
476
+ this.listeners[eventType] = this.listeners[eventType]?.filter(
477
+ (l) => l !== listener
478
+ );
479
+ }
480
+ };
481
+ }
482
+ /**
483
+ * Emit a system event to all registered listeners
484
+ *
485
+ * @param event - The system event to emit
486
+ */
487
+ async emit(event) {
488
+ const listeners = this.listeners[event.type] || [];
489
+ for (const listener of listeners) {
490
+ await listener(event);
491
+ }
492
+ }
493
+ };
494
+ var systemEvents = new SystemEventManager();
495
+ function createSystemEvent(type, data) {
496
+ return {
497
+ type,
498
+ timestamp: Date.now(),
499
+ ...data
500
+ };
501
+ }
502
+
503
+ // 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
+ });
509
+
510
+ // src/core/planner.ts
511
+ function buildPrompt(input, targetNodeId, customPrompt) {
512
+ const { schema, goal, history, userContext } = input;
513
+ const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
514
+ return `Table: ${tableName}
515
+ Schema: ${JSON.stringify(tableSchema)}`;
516
+ }).join("\n\n");
517
+ const recentEvents = history?.slice(-5).map(
518
+ (event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
519
+ ).join("\n") || "No recent events";
520
+ const userContextSection = userContext ? `
521
+
522
+ User Context:
523
+ ${JSON.stringify(userContext)}` : "";
524
+ return `
525
+ You are an expert UI generator.
526
+ Create a user interface that achieves the following goal: "${goal}"
527
+
528
+ Available data schema:
529
+ ${schemaInfo}
530
+
531
+ Recent user interactions:
532
+ ${recentEvents}${userContextSection}
533
+
534
+ Generate a complete UI specification in JSON format that matches the following TypeScript type:
535
+ type UISpecNode = {
536
+ id: string;
537
+ type: string;
538
+ props?: Record<string, any>;
539
+ bindings?: Record<string, any>;
540
+ events?: Record<string, { action: string; target?: string; payload?: Record<string, any>; }>;
541
+ children?: UISpecNode[];
542
+ };
543
+
544
+ UI Guidance:
545
+ 1. Create a focused interface that directly addresses the goal
546
+ 2. Use appropriate UI patterns (lists, forms, details, etc.)
547
+ 3. Include navigation between related views when needed
548
+ 4. Keep the interface simple and intuitive
549
+ 5. Bind to schema data where appropriate
550
+ 6. Provide event handlers for user interactions
551
+
552
+ Respond ONLY with the JSON UI specification and no other text.
553
+ `;
554
+ }
555
+ function mockPlanner(input, targetNodeId, customPrompt) {
556
+ const mockNode = {
557
+ id: targetNodeId || "root",
558
+ type: "Container",
559
+ props: { title: "Mock UI" },
560
+ children: [
561
+ {
562
+ id: "text-1",
563
+ type: "Text",
564
+ props: { text: "This is a mock UI for testing" }
565
+ }
566
+ ]
567
+ };
568
+ return mockNode;
569
+ }
570
+
571
+ // 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
+ function useUIStateEngine({
582
+ schema,
583
+ goal,
584
+ userContext,
585
+ mockMode = false,
586
+ planningConfig = {},
587
+ router = createDefaultRouter(),
588
+ dataContext = {},
589
+ enablePartialUpdates = false
590
+ }) {
591
+ 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;
631
+ }
632
+ } 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
+ }));
645
+ }
646
+ return;
647
+ }
648
+ }
649
+ const input = {
650
+ schema,
651
+ 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]);
666
+ useEffect(() => {
667
+ if (isLoading) {
668
+ 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
+ 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
+ }
718
+ } else {
719
+ dispatch({ type: "AI_RESPONSE", node: validatedNode });
720
+ }
721
+ 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(
735
+ createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
736
+ error: parseError instanceof Error ? parseError : new Error("Parse error")
737
+ })
738
+ );
739
+ }
740
+ }
741
+ }, [data.content, error, isLoading, enablePartialUpdates]);
742
+ useEffect(() => {
743
+ const input = {
744
+ schema,
745
+ goal,
746
+ history: [],
747
+ userContext
748
+ };
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]);
760
+ return {
761
+ state,
762
+ dispatch,
763
+ handleEvent
764
+ };
765
+ }
766
+ var ShimmerBlock = () => /* @__PURE__ */ jsx("div", { className: "w-full h-8 bg-gray-200 animate-pulse rounded" });
767
+ var ShimmerTable = ({ rows = 3 }) => /* @__PURE__ */ jsxs("div", { className: "w-full space-y-2", children: [
768
+ /* @__PURE__ */ jsx("div", { className: "w-full h-10 bg-gray-200 animate-pulse rounded" }),
769
+ Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsx("div", { className: "w-full h-12 bg-gray-200 animate-pulse rounded" }, i))
770
+ ] });
771
+ var ShimmerCard = () => /* @__PURE__ */ jsxs("div", { className: "w-full p-4 space-y-4 border rounded-lg", children: [
772
+ /* @__PURE__ */ jsx("div", { className: "w-3/4 h-6 bg-gray-200 animate-pulse rounded" }),
773
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
774
+ /* @__PURE__ */ jsx("div", { className: "w-full h-4 bg-gray-200 animate-pulse rounded" }),
775
+ /* @__PURE__ */ jsx("div", { className: "w-full h-4 bg-gray-200 animate-pulse rounded" }),
776
+ /* @__PURE__ */ jsx("div", { className: "w-5/6 h-4 bg-gray-200 animate-pulse rounded" })
777
+ ] })
778
+ ] });
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 }) });
781
+ var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsx(
782
+ "button",
783
+ {
784
+ className: `px-4 py-2 rounded font-medium ${variant === "default" ? "bg-blue-600 text-white" : variant === "outline" ? "border border-gray-300 text-gray-700" : "bg-red-600 text-white"}`,
785
+ onClick,
786
+ children
787
+ }
788
+ );
789
+ var Table = ({ items = [], fields = [], onSelect, selectable }) => /* @__PURE__ */ jsx("div", { className: "w-full border rounded-lg overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
790
+ /* @__PURE__ */ jsx("thead", { className: "bg-gray-50", children: /* @__PURE__ */ jsx("tr", { children: fields.map((field) => /* @__PURE__ */ jsx("th", { className: "px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider", children: field.label }, field.key)) }) }),
791
+ /* @__PURE__ */ jsx("tbody", { className: "bg-white divide-y divide-gray-200", children: items.map((item, index) => /* @__PURE__ */ jsx(
792
+ "tr",
793
+ {
794
+ onClick: () => selectable && onSelect && onSelect(item),
795
+ className: selectable ? "cursor-pointer hover:bg-gray-50" : "",
796
+ children: fields.map((field) => /* @__PURE__ */ jsx("td", { className: "px-6 py-4 whitespace-nowrap text-sm text-gray-500", children: item[field.key] }, field.key))
797
+ },
798
+ index
799
+ )) })
800
+ ] }) });
801
+ var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
802
+ if (!visible)
803
+ 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 }),
807
+ onBack && /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
808
+ ] }),
809
+ /* @__PURE__ */ jsx("div", { className: "space-y-4", children: fields.map((field) => {
810
+ if (field.type === "heading") {
811
+ return /* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold", children: data?.[field.key] }, field.key);
812
+ }
813
+ if (field.type === "content") {
814
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-700", children: data?.[field.key] }, field.key);
815
+ }
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);
820
+ }) })
821
+ ] });
822
+ };
823
+ var createEventHandler = (node, eventName) => {
824
+ const eventConfig = node.events?.[eventName];
825
+ if (!eventConfig)
826
+ 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
+ });
833
+ };
834
+ };
835
+ 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,
840
+ {
841
+ variant: node.props?.variant,
842
+ onClick: createEventHandler(node, "onClick"),
843
+ children: node.props?.label || "Button"
844
+ }
845
+ ),
846
+ ListView: (node) => /* @__PURE__ */ jsx(
847
+ Table,
848
+ {
849
+ items: node.bindings?.items || [],
850
+ fields: node.bindings?.fields || [],
851
+ selectable: node.props?.selectable,
852
+ onSelect: createEventHandler(node, "onSelect")
853
+ }
854
+ ),
855
+ Detail: (node) => /* @__PURE__ */ jsx(
856
+ Detail,
857
+ {
858
+ data: node.bindings?.data,
859
+ fields: node.bindings?.fields || [],
860
+ title: node.props?.title,
861
+ visible: node.props?.visible !== false,
862
+ onBack: createEventHandler(node, "onBack")
863
+ }
864
+ )
865
+ };
866
+ function renderNode(node) {
867
+ const Component = adapterMap[node.type];
868
+ if (Component) {
869
+ return Component(node);
870
+ }
871
+ return /* @__PURE__ */ jsxs("div", { className: "p-2 border border-red-300 rounded", children: [
872
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-red-500", children: [
873
+ "Unsupported component: ",
874
+ node.type
875
+ ] }),
876
+ node.children?.map((child) => renderNode(child))
877
+ ] });
878
+ }
879
+ async function renderNode2(node, adapter = "shadcn") {
880
+ const startTime = Date.now();
881
+ await systemEvents.emit(
882
+ createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
883
+ );
884
+ let result;
885
+ switch (adapter) {
886
+ case "shadcn":
887
+ result = renderNode(node);
888
+ break;
889
+ default:
890
+ console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
891
+ result = renderNode(node);
892
+ }
893
+ await systemEvents.emit(
894
+ createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
895
+ layout: node,
896
+ renderTimeMs: Date.now() - startTime
897
+ })
898
+ );
899
+ return result;
900
+ }
901
+ function renderShimmer(node, adapter = "shadcn") {
902
+ if (!node) {
903
+ return /* @__PURE__ */ jsx(ShimmerBlock, {});
904
+ }
905
+ switch (node.type) {
906
+ case "ListView":
907
+ return /* @__PURE__ */ jsx(ShimmerTable, { rows: 3 });
908
+ case "Detail":
909
+ return /* @__PURE__ */ jsx(ShimmerCard, {});
910
+ case "Container":
911
+ return /* @__PURE__ */ jsx("div", { className: "space-y-4", children: node.children?.map((child, index) => /* @__PURE__ */ jsx("div", { children: renderShimmer(child, adapter) }, index)) });
912
+ default:
913
+ return /* @__PURE__ */ jsx(ShimmerBlock, {});
914
+ }
915
+ }
916
+
917
+ // src/core/bindings.ts
918
+ function getValueByPath(context, path) {
919
+ const parts = path.split(".");
920
+ let current = context;
921
+ for (const part of parts) {
922
+ if (current === null || current === void 0) {
923
+ return void 0;
924
+ }
925
+ if (typeof current !== "object") {
926
+ return void 0;
927
+ }
928
+ current = current[part];
929
+ }
930
+ return current;
931
+ }
932
+ function setValueByPath(context, path, value) {
933
+ const result = { ...context };
934
+ const parts = path.split(".");
935
+ let current = result;
936
+ for (let i = 0; i < parts.length - 1; i++) {
937
+ const part = parts[i];
938
+ if (!(part in current) || current[part] === null || current[part] === void 0) {
939
+ current[part] = {};
940
+ }
941
+ current = current[part];
942
+ if (typeof current !== "object") {
943
+ current = {};
944
+ }
945
+ }
946
+ const lastPart = parts[parts.length - 1];
947
+ current[lastPart] = value;
948
+ return result;
949
+ }
950
+ function processBinding(binding, context) {
951
+ if (typeof binding === "string") {
952
+ return getValueByPath(context, binding);
953
+ }
954
+ if (Array.isArray(binding)) {
955
+ return binding.map((item) => processBinding(item, context));
956
+ }
957
+ if (binding !== null && typeof binding === "object") {
958
+ const result = {};
959
+ for (const [key, value] of Object.entries(binding)) {
960
+ result[key] = processBinding(value, context);
961
+ }
962
+ return result;
963
+ }
964
+ return binding;
965
+ }
966
+ async function resolveBindings(node, context) {
967
+ await systemEvents.emit(
968
+ createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, { layout: node })
969
+ );
970
+ const result = {
971
+ ...node,
972
+ props: node.props ? { ...node.props } : void 0,
973
+ events: node.events ? { ...node.events } : void 0
974
+ };
975
+ if (node.bindings) {
976
+ for (const [key, binding] of Object.entries(node.bindings)) {
977
+ const value = processBinding(binding, context);
978
+ if (value !== void 0) {
979
+ if (!result.props) {
980
+ result.props = {};
981
+ }
982
+ result.props[key] = value;
983
+ }
984
+ }
985
+ }
986
+ if (node.children) {
987
+ result.children = await Promise.all(node.children.map((child) => resolveBindings(child, context)));
988
+ }
989
+ await systemEvents.emit(
990
+ createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
991
+ originalLayout: node,
992
+ resolvedLayout: result
993
+ })
994
+ );
995
+ return result;
996
+ }
997
+ function executeAction(action, targetId, payload, context = {}, layoutTree) {
998
+ let newContext = { ...context };
999
+ switch (action) {
1000
+ case "VIEW_DETAIL": {
1001
+ if (payload?.item) {
1002
+ newContext = setValueByPath(newContext, "selected", payload.item);
1003
+ }
1004
+ break;
1005
+ }
1006
+ case "HIDE_DETAIL": {
1007
+ newContext = setValueByPath(newContext, "selected", null);
1008
+ break;
1009
+ }
1010
+ case "SET_VALUE": {
1011
+ if (payload?.path && "value" in payload) {
1012
+ const path = String(payload.path);
1013
+ newContext = setValueByPath(newContext, path, payload.value);
1014
+ }
1015
+ break;
1016
+ }
1017
+ default:
1018
+ console.warn(`Unknown action: ${action}`);
1019
+ }
1020
+ return newContext;
1021
+ }
1022
+
1023
+ // src/core/events.ts
1024
+ var EventManager = class {
1025
+ constructor() {
1026
+ this.hooks = {};
1027
+ }
1028
+ /**
1029
+ * Register a hook for specific event types
1030
+ *
1031
+ * @param eventTypes - Event types to register for, or 'all' for all events
1032
+ * @param hook - Hook function to execute
1033
+ * @returns Unregister function
1034
+ */
1035
+ register(eventTypes, hook) {
1036
+ if (eventTypes === "all") {
1037
+ if (!this.hooks.all) {
1038
+ this.hooks.all = [];
1039
+ }
1040
+ this.hooks.all.push(hook);
1041
+ return () => {
1042
+ if (this.hooks.all) {
1043
+ this.hooks.all = this.hooks.all.filter((h) => h !== hook);
1044
+ }
1045
+ };
1046
+ }
1047
+ eventTypes.forEach((type) => {
1048
+ if (!this.hooks[type]) {
1049
+ this.hooks[type] = [];
1050
+ }
1051
+ this.hooks[type]?.push(hook);
1052
+ });
1053
+ return () => {
1054
+ eventTypes.forEach((type) => {
1055
+ if (this.hooks[type]) {
1056
+ this.hooks[type] = this.hooks[type]?.filter((h) => h !== hook);
1057
+ }
1058
+ });
1059
+ };
1060
+ }
1061
+ /**
1062
+ * Process an event through all registered hooks
1063
+ *
1064
+ * @param event - The UI event to process
1065
+ * @returns Whether the default action should proceed
1066
+ */
1067
+ async processEvent(event) {
1068
+ let defaultPrevented = false;
1069
+ let propagationStopped = false;
1070
+ const context = {
1071
+ originalEvent: event,
1072
+ preventDefault: () => {
1073
+ defaultPrevented = true;
1074
+ },
1075
+ stopPropagation: () => {
1076
+ propagationStopped = true;
1077
+ },
1078
+ isDefaultPrevented: () => defaultPrevented,
1079
+ isPropagationStopped: () => propagationStopped
1080
+ };
1081
+ if (this.hooks.all) {
1082
+ for (const hook of this.hooks.all) {
1083
+ await hook(context);
1084
+ if (propagationStopped)
1085
+ break;
1086
+ }
1087
+ }
1088
+ if (!propagationStopped && this.hooks[event.type]) {
1089
+ for (const hook of this.hooks[event.type] || []) {
1090
+ await hook(context);
1091
+ if (propagationStopped)
1092
+ break;
1093
+ }
1094
+ }
1095
+ return !defaultPrevented;
1096
+ }
1097
+ };
1098
+ function createEventHook(eventTypes, hook, options) {
1099
+ return async (context) => {
1100
+ await hook(context);
1101
+ if (options?.preventDefault) {
1102
+ context.preventDefault();
1103
+ }
1104
+ if (options?.stopPropagation) {
1105
+ context.stopPropagation();
1106
+ }
1107
+ };
1108
+ }
1109
+ var AutoUI = ({
1110
+ schema,
1111
+ goal,
1112
+ componentAdapter = "shadcn",
1113
+ userContext,
1114
+ onEvent,
1115
+ eventHooks,
1116
+ systemEventHooks,
1117
+ debugMode = false,
1118
+ mockMode = true,
1119
+ databaseConfig,
1120
+ planningConfig,
1121
+ integration = {},
1122
+ scope = {},
1123
+ enablePartialUpdates = false
1124
+ }) => {
1125
+ const [schemaAdapterInstance, setSchemaAdapterInstance] = useState(null);
1126
+ const [dataContext, setDataContext] = useState({});
1127
+ const effectiveSchema = schema;
1128
+ const scopedGoal = goal;
1129
+ useEffect(() => {
1130
+ const unregisters = [];
1131
+ if (systemEventHooks) {
1132
+ Object.entries(systemEventHooks).forEach(([eventType, hooks]) => {
1133
+ if (!hooks)
1134
+ return;
1135
+ hooks.forEach((hook) => {
1136
+ const unregister = systemEvents.on(eventType, hook);
1137
+ unregisters.push(unregister);
1138
+ });
1139
+ });
1140
+ }
1141
+ if (debugMode) {
1142
+ const debugHook = (event) => {
1143
+ console.debug(`[AutoUI Debug] System Event:`, event);
1144
+ };
1145
+ Object.values(SystemEventType).forEach((eventType) => {
1146
+ const unregister = systemEvents.on(eventType, debugHook);
1147
+ unregisters.push(unregister);
1148
+ });
1149
+ }
1150
+ return () => {
1151
+ unregisters.forEach((unregister) => unregister());
1152
+ };
1153
+ }, [systemEventHooks, debugMode]);
1154
+ useEffect(() => {
1155
+ const initializeDataContext = async () => {
1156
+ let initialData = {};
1157
+ if (schemaAdapterInstance) {
1158
+ initialData = await schemaAdapterInstance.initializeDataContext();
1159
+ } else if (effectiveSchema) {
1160
+ Object.entries(effectiveSchema).forEach(([key, tableSchema]) => {
1161
+ initialData[key] = {
1162
+ schema: tableSchema,
1163
+ // For development, add sample data if available
1164
+ data: tableSchema?.sampleData || [],
1165
+ selected: null
1166
+ };
1167
+ });
1168
+ }
1169
+ if (userContext) {
1170
+ initialData.user = userContext;
1171
+ }
1172
+ setDataContext(initialData);
1173
+ };
1174
+ initializeDataContext();
1175
+ }, [effectiveSchema, schemaAdapterInstance, userContext]);
1176
+ const { state, handleEvent } = useUIStateEngine({
1177
+ schema: effectiveSchema,
1178
+ goal: scopedGoal,
1179
+ userContext,
1180
+ mockMode,
1181
+ planningConfig,
1182
+ router: void 0,
1183
+ dataContext,
1184
+ enablePartialUpdates
1185
+ });
1186
+ const eventManagerRef = useRef(new EventManager());
1187
+ useEffect(() => {
1188
+ if (!eventHooks)
1189
+ return;
1190
+ const unregisters = [];
1191
+ 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;
1197
+ }
1198
+ });
1199
+ unregisters.push(unregister);
1200
+ }
1201
+ Object.entries(eventHooks).forEach(([type, hooks]) => {
1202
+ if (type === "all" || !hooks)
1203
+ 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;
1209
+ }
1210
+ });
1211
+ unregisters.push(unregister);
1212
+ });
1213
+ return () => {
1214
+ unregisters.forEach((unregister) => unregister());
1215
+ };
1216
+ }, [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;
1236
+ }
1237
+ }
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}`);
1249
+ handleEvent(event);
1250
+ return;
1251
+ }
1252
+ const newContext = executeAction(
1253
+ eventConfig.action,
1254
+ eventConfig.target,
1255
+ {
1256
+ ...eventConfig.payload,
1257
+ ...event.payload
1258
+ },
1259
+ dataContext,
1260
+ state.layout
1261
+ );
1262
+ setDataContext(newContext);
1263
+ handleEvent(event);
1264
+ }, [dataContext, handleEvent, onEvent, state.layout]);
1265
+ const [resolvedLayout, setResolvedLayout] = useState(void 0);
1266
+ const [renderedNode, setRenderedNode] = useState(null);
1267
+ useEffect(() => {
1268
+ if (state.layout) {
1269
+ resolveBindings(state.layout, dataContext).then((resolved) => setResolvedLayout(resolved)).catch((err) => console.error("Error resolving bindings:", err));
1270
+ } else {
1271
+ setResolvedLayout(void 0);
1272
+ }
1273
+ }, [state.layout, dataContext]);
1274
+ useEffect(() => {
1275
+ if (resolvedLayout) {
1276
+ renderNode2(resolvedLayout, componentAdapter).then((rendered) => setRenderedNode(rendered)).catch((err) => console.error("Error rendering node:", err));
1277
+ } else {
1278
+ setRenderedNode(null);
1279
+ }
1280
+ }, [resolvedLayout, componentAdapter]);
1281
+ return /* @__PURE__ */ jsxs(
1282
+ "div",
1283
+ {
1284
+ className: `autoui-root ${integration.className || ""}`,
1285
+ id: integration.id,
1286
+ "data-mode": integration.mode,
1287
+ "data-scope": scope?.type || "full",
1288
+ children: [
1289
+ state.loading || !resolvedLayout ? (
1290
+ // Render shimmer loading state
1291
+ /* @__PURE__ */ jsx("div", { className: "autoui-loading", children: state.layout ? renderShimmer(state.layout, componentAdapter) : /* @__PURE__ */ jsxs("div", { className: "autoui-shimmer-container", children: [
1292
+ /* @__PURE__ */ jsx("div", { className: "autoui-shimmer-header" }),
1293
+ /* @__PURE__ */ jsx("div", { className: "autoui-shimmer-content" })
1294
+ ] }) })
1295
+ ) : (
1296
+ // Render the resolved layout
1297
+ /* @__PURE__ */ jsx("div", { className: "autoui-content", children: renderedNode })
1298
+ ),
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 })
1302
+ ] })
1303
+ ]
1304
+ }
1305
+ );
1306
+ };
1307
+
1308
+ // src/adapters/schema/drizzle.ts
1309
+ var DrizzleAdapter = class {
1310
+ constructor(options) {
1311
+ this.schema = options.schema;
1312
+ this.client = options.client;
1313
+ this.useMockData = options.useMockData ?? !options.client;
1314
+ this.mockData = options.mockData ?? {};
1315
+ }
1316
+ /**
1317
+ * Convert Drizzle schema to AutoUI schema format
1318
+ */
1319
+ getSchema() {
1320
+ const result = {};
1321
+ Object.entries(this.schema).forEach(([tableName, table]) => {
1322
+ result[tableName] = {
1323
+ tableName: table.name,
1324
+ schema: table.schema,
1325
+ columns: this.convertColumns(table.columns),
1326
+ // Include mock data if available and mock mode is enabled
1327
+ ...this.useMockData && this.mockData[tableName] ? { sampleData: this.mockData[tableName] } : {}
1328
+ };
1329
+ });
1330
+ return result;
1331
+ }
1332
+ /**
1333
+ * Convert Drizzle columns to AutoUI column format
1334
+ */
1335
+ convertColumns(columns) {
1336
+ const result = {};
1337
+ Object.entries(columns).forEach(([columnName, column]) => {
1338
+ result[columnName] = {
1339
+ type: this.mapDataType(column.dataType),
1340
+ notNull: column.notNull,
1341
+ defaultValue: column.defaultValue,
1342
+ primaryKey: column.primaryKey,
1343
+ unique: column.unique,
1344
+ references: column.references
1345
+ };
1346
+ });
1347
+ return result;
1348
+ }
1349
+ /**
1350
+ * Map Drizzle data types to standard types
1351
+ */
1352
+ mapDataType(drizzleType) {
1353
+ 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"
1374
+ };
1375
+ return typeMap[drizzleType.toLowerCase()] || "string";
1376
+ }
1377
+ /**
1378
+ * Execute a query against the database
1379
+ */
1380
+ async query(tableName, query) {
1381
+ if (this.useMockData) {
1382
+ return this.mockData[tableName] || [];
1383
+ }
1384
+ if (!this.client) {
1385
+ throw new Error("No database client provided and mock mode is disabled");
1386
+ }
1387
+ if (this.client.queryFn) {
1388
+ return this.client.queryFn(tableName, query);
1389
+ }
1390
+ throw new Error("No query function provided in client config");
1391
+ }
1392
+ /**
1393
+ * Initialize the data context with schema information and optional mock data
1394
+ */
1395
+ async initializeDataContext() {
1396
+ const context = {};
1397
+ for (const [tableName, table] of Object.entries(this.schema)) {
1398
+ context[tableName] = {
1399
+ schema: table,
1400
+ data: this.useMockData ? this.mockData[tableName] || [] : [],
1401
+ selected: null
1402
+ };
1403
+ }
1404
+ return context;
1405
+ }
1406
+ };
1407
+
1408
+ // src/adapters/schema/index.ts
1409
+ function createSchemaAdapter(options) {
1410
+ switch (options.type) {
1411
+ case "drizzle":
1412
+ return new DrizzleAdapter(options.options);
1413
+ case "custom":
1414
+ return options.adapter;
1415
+ 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;
1429
+ }
1430
+ }
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;
1445
+ }
1446
+
1447
+ export { ActionRouter, ActionType, AutoUI, DrizzleAdapter, SystemEventType, createDefaultRouter, createEventHook, createSchemaAdapter, createSystemEvent, generateComponent, generateUIComponent, generateUIDescription, systemEvents, uiEvent, uiEventType, uiSpecNode };
1448
+ //# sourceMappingURL=out.js.map
1449
+ //# sourceMappingURL=index.mjs.map