autoui-react 0.0.5-alpha → 0.1.1-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,10 +5,10 @@ var tailwindMerge = require('tailwind-merge');
5
5
  var reactSlot = require('@radix-ui/react-slot');
6
6
  var classVarianceAuthority = require('class-variance-authority');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
- var React = require('react');
9
- var openai = require('@ai-sdk/openai');
8
+ var anthropic = require('@ai-sdk/anthropic');
10
9
  var ai = require('ai');
11
10
  var zod = require('zod');
11
+ var React = require('react');
12
12
  var DialogPrimitive = require('@radix-ui/react-dialog');
13
13
  var lucideReact = require('lucide-react');
14
14
  var SelectPrimitive = require('@radix-ui/react-select');
@@ -223,6 +223,10 @@ function removeNodeById(tree, nodeId) {
223
223
  function uiReducer(state, action) {
224
224
  switch (action.type) {
225
225
  case "UI_EVENT": {
226
+ if (action.event.type === "INIT" && state.history.some((e) => e.type === "INIT")) {
227
+ console.log("[AutoUI uiReducer] Ignoring duplicate INIT event");
228
+ return state;
229
+ }
226
230
  return {
227
231
  ...state,
228
232
  loading: true,
@@ -318,276 +322,105 @@ function uiReducer(state, action) {
318
322
  loading: action.isLoading
319
323
  };
320
324
  }
325
+ case "SET_DATA_CONTEXT": {
326
+ return {
327
+ ...state,
328
+ dataContext: action.payload
329
+ };
330
+ }
321
331
  default:
322
332
  return state;
323
333
  }
324
334
  }
325
335
  var initialState = {
326
336
  layout: null,
327
- loading: false,
337
+ loading: true,
338
+ error: null,
328
339
  history: [],
329
- error: null
340
+ dataContext: {}
330
341
  };
331
342
 
332
- // src/core/action-router.ts
333
- var UI_GUIDANCE_BASE = `
334
- UI Guidance:
335
- 1. Create a focused interface that directly addresses the goal
336
- 2. Use appropriate UI patterns (lists, forms, details, etc.)
337
- 3. Include navigation between related views when needed
338
- 4. Keep the interface simple and intuitive
339
- 5. Bind to schema data where appropriate
340
- 6. Provide event handlers for user interactions - make sure to always include both action and target properties`;
341
- var LIST_BINDING_GUIDANCE = `7. **CRITICAL:** For \`ListView\` or \`Table\` nodes, the \`data\` binding key **MUST** point to the *exact path* of the data *array* within the context.`;
342
- var LIST_BINDING_EXAMPLE = `Example: If the context has \`{ tasks: { data: [...] } }\`, the binding **MUST** be \`{ "bindings": { "data": "tasks.data" } }\`. If the context has \`{ userList: [...] }\`, the binding **MUST** be \`{ "bindings": { "data": "userList" } }\`. **NEVER** bind to the parent object containing the array (e.g., DO NOT USE \`{ "bindings": { "data": "tasks" } }\`).`;
343
- var COMMON_UI_GUIDANCE = UI_GUIDANCE_BASE + "\n" + // Add a newline separator
344
- LIST_BINDING_GUIDANCE + // Add the specific list binding rule
345
- "\n" + // Add a newline separator
346
- LIST_BINDING_EXAMPLE;
347
- function processTemplate(template, values) {
348
- return template.replace(/\${(.*?)}/g, (match, key) => {
349
- const trimmedKey = key.trim();
350
- return trimmedKey in values ? String(values[trimmedKey]) : match;
351
- });
352
- }
353
- function buildPrompt(input, promptTemplate, templateValues) {
354
- const { schema, goal, history, userContext } = input;
355
- const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
356
- const schemaString = typeof tableSchema === "object" && tableSchema !== null ? JSON.stringify(tableSchema) : String(tableSchema);
357
- return `Table: ${tableName}
358
- Schema: ${schemaString}`;
359
- }).join("\n\n");
360
- const recentEvents = history && history.length > 0 ? history.slice(-5).map(
361
- (event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
362
- ).join("\n") : "No recent events";
363
- const userContextSection = userContext ? `
364
-
365
- User Context:
366
- ${JSON.stringify(userContext)}` : "";
367
- if (promptTemplate && templateValues) {
368
- const fullTemplateValues = {
369
- ...templateValues,
370
- schemaInfo,
371
- recentEvents,
372
- userContextString: userContextSection.trim(),
373
- // Use trimmed version
374
- commonUIGuidance: COMMON_UI_GUIDANCE,
375
- goal
376
- // Ensure goal is always available to templates
377
- };
378
- return processTemplate(promptTemplate, fullTemplateValues);
379
- }
380
- const interactionDescription = history && history.length > 0 ? `The user's last action was: ${history[history.length - 1].type} on node ${history[history.length - 1].nodeId}` : "The user initiated the session for the goal";
381
- return `
382
- You are an expert UI generator.
383
- Create a user interface that achieves the following goal: "${goal}".
384
- ${interactionDescription}.
385
-
386
- Available data schema:
387
- ${schemaInfo}
388
-
389
- Recent user interactions:
390
- ${recentEvents}${userContextSection}
391
-
392
- Generate a complete UI specification in JSON format that matches the following TypeScript type:
393
- type UISpecNode = { id: string; node_type: string; props?: Record<string, unknown>; bindings?: Record<string, unknown>; events?: Record<string, { action: string; target: string; payload?: Record<string, unknown>; }>; children?: UISpecNode[]; };
394
- ${COMMON_UI_GUIDANCE}
395
-
396
- Respond ONLY with the JSON UI specification and no other text.
397
- `;
398
- }
343
+ // src/schema/action-types.ts
399
344
  var ActionType = /* @__PURE__ */ ((ActionType2) => {
400
345
  ActionType2["FULL_REFRESH"] = "FULL_REFRESH";
401
346
  ActionType2["UPDATE_NODE"] = "UPDATE_NODE";
347
+ ActionType2["UPDATE_DATA"] = "UPDATE_DATA";
348
+ ActionType2["ADD_ITEM"] = "ADD_ITEM";
349
+ ActionType2["DELETE_ITEM"] = "DELETE_ITEM";
402
350
  ActionType2["ADD_DROPDOWN"] = "ADD_DROPDOWN";
403
351
  ActionType2["SHOW_DETAIL"] = "SHOW_DETAIL";
404
352
  ActionType2["HIDE_DETAIL"] = "HIDE_DETAIL";
353
+ ActionType2["HIDE_DIALOG"] = "HIDE_DIALOG";
354
+ ActionType2["SAVE_TASK_CHANGES"] = "SAVE_TASK_CHANGES";
405
355
  ActionType2["TOGGLE_STATE"] = "TOGGLE_STATE";
406
356
  ActionType2["UPDATE_FORM"] = "UPDATE_FORM";
407
357
  ActionType2["NAVIGATE"] = "NAVIGATE";
358
+ ActionType2["OPEN_DIALOG"] = "OPEN_DIALOG";
359
+ ActionType2["CLOSE_DIALOG"] = "CLOSE_DIALOG";
360
+ ActionType2["UPDATE_CONTEXT"] = "UPDATE_CONTEXT";
408
361
  return ActionType2;
409
362
  })(ActionType || {});
410
- var ActionRouter = class {
363
+
364
+ // src/core/system-events.ts
365
+ var SystemEventType = /* @__PURE__ */ ((SystemEventType2) => {
366
+ SystemEventType2["PLAN_START"] = "PLAN_START";
367
+ SystemEventType2["PLAN_PROMPT_CREATED"] = "PLAN_PROMPT_CREATED";
368
+ SystemEventType2["PLAN_RESPONSE_CHUNK"] = "PLAN_RESPONSE_CHUNK";
369
+ SystemEventType2["PLAN_COMPLETE"] = "PLAN_COMPLETE";
370
+ SystemEventType2["PLAN_ERROR"] = "PLAN_ERROR";
371
+ SystemEventType2["BINDING_RESOLUTION_START"] = "BINDING_RESOLUTION_START";
372
+ SystemEventType2["BINDING_RESOLUTION_COMPLETE"] = "BINDING_RESOLUTION_COMPLETE";
373
+ SystemEventType2["DATA_FETCH_START"] = "DATA_FETCH_START";
374
+ SystemEventType2["DATA_FETCH_COMPLETE"] = "DATA_FETCH_COMPLETE";
375
+ SystemEventType2["RENDER_START"] = "RENDER_START";
376
+ SystemEventType2["RENDER_COMPLETE"] = "RENDER_COMPLETE";
377
+ SystemEventType2["PREFETCH_START"] = "PREFETCH_START";
378
+ SystemEventType2["PREFETCH_COMPLETE"] = "PREFETCH_COMPLETE";
379
+ return SystemEventType2;
380
+ })(SystemEventType || {});
381
+ var SystemEventManager = class {
411
382
  constructor() {
412
- this.routes = {};
413
- }
414
- /**
415
- * Register a new action route
416
- * @param eventType - UI event type to route
417
- * @param config - Route configuration
418
- */
419
- registerRoute(eventType, config) {
420
- if (!this.routes[eventType]) {
421
- this.routes[eventType] = [];
422
- }
423
- this.routes[eventType].push(config);
383
+ this.listeners = {};
424
384
  }
425
385
  /**
426
- * Find the appropriate route for an event
427
- * @param event - UI event
428
- * @param layout - Current UI layout
429
- * @param dataContext - Current data context
430
- * @returns Route resolution or null if no match
386
+ * Register a listener for a specific system event type
387
+ *
388
+ * @param eventType - The system event type to listen for
389
+ * @param listener - The listener function
390
+ * @returns Function to unregister the listener
431
391
  */
432
- resolveRoute(event, schema, layout, dataContext, goal, userContext) {
433
- console.log(
434
- `[ActionRouter Debug] resolveRoute called for event type: ${event.type}`
435
- );
436
- const routes = this.routes[event.type] || [];
437
- console.log(
438
- `[ActionRouter Debug] Found ${routes.length} routes for ${event.type}`
439
- );
440
- if (routes.length === 0) {
441
- console.log(
442
- `[ActionRouter Debug] No specific route found for ${event.type}, using default FULL_REFRESH.`
443
- );
444
- const defaultPlannerInput = {
445
- schema,
446
- goal,
447
- history: [event],
448
- userContext: userContext || null
449
- };
450
- const defaultPrompt = buildPrompt(
451
- defaultPlannerInput,
452
- void 0,
453
- void 0
454
- );
455
- const defaultResolution = {
456
- actionType: "FULL_REFRESH" /* FULL_REFRESH */,
457
- targetNodeId: layout?.id || "root",
458
- plannerInput: defaultPlannerInput,
459
- prompt: defaultPrompt
460
- };
461
- console.log(
462
- "[ActionRouter Debug] Default Resolution:",
463
- defaultResolution
464
- );
465
- return defaultResolution;
466
- }
467
- const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
468
- const nodeConfig = sourceNode?.events?.[event.type];
469
- let matchingRoute;
470
- if (nodeConfig) {
471
- matchingRoute = routes.find(
472
- (route) => route.actionType.toString() === nodeConfig.action
473
- );
474
- }
475
- if (!matchingRoute) {
476
- matchingRoute = routes[0];
477
- }
478
- console.log("[ActionRouter Debug] Matching Route Config:", matchingRoute);
479
- const targetNodeId = nodeConfig?.target || matchingRoute.targetNodeId || event.nodeId;
480
- const additionalContext = {};
481
- if (matchingRoute.contextKeys) {
482
- matchingRoute.contextKeys.forEach((key) => {
483
- additionalContext[key] = dataContext[key];
484
- });
485
- }
486
- if (sourceNode) {
487
- additionalContext.sourceNode = sourceNode;
488
- }
489
- if (layout) {
490
- const targetNode = findNodeById(layout, targetNodeId);
491
- if (targetNode) {
492
- additionalContext.targetNode = targetNode;
493
- }
494
- }
495
- if (event.payload) {
496
- additionalContext.eventPayload = event.payload;
497
- }
498
- if (nodeConfig?.payload) {
499
- Object.entries(nodeConfig.payload).forEach(([key, value]) => {
500
- additionalContext[key] = value;
501
- });
392
+ on(eventType, listener) {
393
+ if (!this.listeners[eventType]) {
394
+ this.listeners[eventType] = [];
502
395
  }
503
- const plannerInput2 = {
504
- schema,
505
- goal,
506
- history: [event],
507
- userContext: {
508
- ...userContext,
509
- ...additionalContext
396
+ this.listeners[eventType]?.push(listener);
397
+ return () => {
398
+ if (this.listeners[eventType]) {
399
+ this.listeners[eventType] = this.listeners[eventType]?.filter(
400
+ (l) => l !== listener
401
+ );
510
402
  }
511
403
  };
512
- const templateValues = {
513
- goal,
514
- eventType: event.type,
515
- nodeId: event.nodeId,
516
- targetNodeId,
517
- actionType: matchingRoute.actionType,
518
- ...userContext || {},
519
- // Spread the original userContext (passed to resolveRoute)
520
- ...additionalContext
521
- // Spread additionalContext afterwards (can override userContext keys)
522
- };
523
- console.log("[ActionRouter Debug] Template Values:", templateValues);
524
- const finalPrompt = buildPrompt(
525
- plannerInput2,
526
- matchingRoute.promptTemplate,
527
- // Pass template if it exists (can be undefined)
528
- templateValues
529
- // Pass templateValues (used only if promptTemplate exists)
530
- );
531
- console.log("[ActionRouter Debug] Generated Prompt:", finalPrompt);
532
- const finalResolution = {
533
- actionType: matchingRoute.actionType,
534
- targetNodeId,
535
- plannerInput: plannerInput2,
536
- prompt: finalPrompt
537
- // Use the generated prompt
538
- };
539
- console.log("[ActionRouter Debug] Final Resolution:", finalResolution);
540
- return finalResolution;
541
404
  }
542
405
  /**
543
- * Process a prompt template with variables
544
- * @param template - Template string with ${var} placeholders
545
- * @param values - Values to substitute
546
- * @returns Processed string
406
+ * Emit a system event to all registered listeners
407
+ *
408
+ * @param event - The system event to emit
547
409
  */
548
- processTemplate(template, values) {
549
- return template.replace(/\${(\w+)}/g, (_, key) => {
550
- return values[key] !== void 0 ? String(values[key]) : `\${${key}}`;
551
- });
410
+ async emit(event) {
411
+ const listeners = this.listeners[event.type] || [];
412
+ for (const listener of listeners) {
413
+ await listener(event);
414
+ }
552
415
  }
553
416
  };
554
- function createDefaultRouter() {
555
- const router = new ActionRouter();
556
- router.registerRoute("CLICK", {
557
- actionType: "FULL_REFRESH" /* FULL_REFRESH */,
558
- targetNodeId: "root"
559
- });
560
- router.registerRoute("INIT", {
561
- actionType: "FULL_REFRESH" /* FULL_REFRESH */,
562
- targetNodeId: "root"
563
- });
564
- router.registerRoute("CLICK", {
565
- actionType: "SHOW_DETAIL" /* SHOW_DETAIL */,
566
- targetNodeId: "${targetNodeId}",
567
- promptTemplate: "Update the UI to show details for the selected item. Current node: ${nodeId}, Target node: ${targetNodeId}",
568
- contextKeys: ["selected"]
569
- });
570
- router.registerRoute("CLICK", {
571
- actionType: "NAVIGATE" /* NAVIGATE */,
572
- targetNodeId: "root",
573
- promptTemplate: "Navigate to a new view based on the user clicking ${nodeId}. Current goal: ${goal}"
574
- });
575
- router.registerRoute("CLICK", {
576
- actionType: "ADD_DROPDOWN" /* ADD_DROPDOWN */,
577
- targetNodeId: "${targetNodeId}",
578
- promptTemplate: "Add a dropdown menu to node ${targetNodeId} with options relevant to the clicked element ${nodeId}"
579
- });
580
- router.registerRoute("CLICK", {
581
- actionType: "TOGGLE_STATE" /* TOGGLE_STATE */,
582
- targetNodeId: "${nodeId}",
583
- promptTemplate: "Toggle the state of node ${nodeId} (e.g., expanded/collapsed, selected/unselected)"
584
- });
585
- router.registerRoute("CHANGE", {
586
- actionType: "UPDATE_FORM" /* UPDATE_FORM */,
587
- targetNodeId: "${targetNodeId}",
588
- promptTemplate: "Update the form based on the user changing the value of ${nodeId}"
589
- });
590
- return router;
417
+ var systemEvents = new SystemEventManager();
418
+ function createSystemEvent(type, data) {
419
+ return {
420
+ type,
421
+ timestamp: Date.now(),
422
+ ...data
423
+ };
591
424
  }
592
425
  var componentType = zod.z.enum([
593
426
  // Layout components
@@ -606,30 +439,29 @@ var componentType = zod.z.enum([
606
439
  "Detail",
607
440
  "Tabs",
608
441
  "Dialog",
442
+ "Badge",
609
443
  // Typography
610
444
  "Heading",
611
445
  "Text"
612
446
  ]);
613
447
 
614
- // src/schema/ui.ts
615
- var uiEventType = zod.z.enum([
616
- "INIT",
617
- "CLICK",
618
- "CHANGE",
619
- "SUBMIT",
620
- "MOUSEOVER",
621
- "MOUSEOUT",
622
- "FOCUS",
623
- "BLUR"
448
+ // src/schema/openai-ui-spec.ts
449
+ var actionTypeEnum = zod.z.enum([
450
+ "FULL_REFRESH",
451
+ "UPDATE_NODE",
452
+ "UPDATE_DATA",
453
+ "ADD_DROPDOWN",
454
+ "SHOW_DETAIL",
455
+ "HIDE_DETAIL",
456
+ "HIDE_DIALOG",
457
+ "SAVE_TASK_CHANGES",
458
+ "TOGGLE_STATE",
459
+ "UPDATE_FORM",
460
+ "NAVIGATE",
461
+ "OPEN_DIALOG",
462
+ "CLOSE_DIALOG",
463
+ "UPDATE_CONTEXT"
624
464
  ]);
625
- var uiEvent = zod.z.object({
626
- type: uiEventType,
627
- nodeId: zod.z.string(),
628
- timestamp: zod.z.number().nullable(),
629
- payload: zod.z.record(zod.z.unknown()).nullable()
630
- });
631
- zod.z.enum(["AI_RESPONSE", "ERROR"]);
632
- var runtimeRecord = zod.z.record(zod.z.any()).nullable();
633
465
  var openAISimplifiedValue = zod.z.string().nullable();
634
466
  var openAIRecordSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
635
467
  var openAIEventPayloadSimplifiedNullable = zod.z.record(openAISimplifiedValue).nullable();
@@ -639,22 +471,26 @@ var openAIBaseNode = zod.z.object({
639
471
  "The type of UI component (e.g., Container, Text, Button, ListView)."
640
472
  ),
641
473
  props: openAIRecordSimplifiedNullable.describe(
642
- 'Component-specific properties (attributes). Values should be strings or null. E.g., for Header use { "title": "My Title" }; for Text use { "text": "My Text" }.'
474
+ 'Component-specific properties (attributes). Values should be strings or null. E.g., for Header use { "title": "My Title" }; for Text use { "text": "My Text" }; for Button use { "label": "My Button Label" }.'
643
475
  ),
644
476
  bindings: openAIRecordSimplifiedNullable.describe(
645
- 'Data bindings map context paths to component props. Values are paths (e.g., "user.name") or templates (e.g., "{{item.title}}"). **CRITICAL for ListView/Table:** the `data` key MUST point to the *exact array path* (e.g., { "data": "tasks.data" } or { "data": "userList" }), NOT the parent object.'
477
+ 'Data bindings map context paths to component props. Values are paths (e.g., "user.name") or templates (e.g., "{{item.title}}")...'
646
478
  ),
647
479
  events: zod.z.record(
648
480
  zod.z.string(),
649
481
  zod.z.object({
650
- action: zod.z.string(),
651
- target: zod.z.string(),
652
- payload: openAIEventPayloadSimplifiedNullable
482
+ action: actionTypeEnum.describe(
483
+ 'Action identifier (e.g., "UPDATE_DATA", "ADD_ITEM", "DELETE_ITEM", "VIEW_DETAIL", "HIDE_DETAIL"). Defines what operation to perform when the event occurs.'
484
+ ),
485
+ target: zod.z.string().describe("Target identifier."),
486
+ payload: openAIEventPayloadSimplifiedNullable.describe(
487
+ "Static payload to merge with the event's runtime payload."
488
+ )
653
489
  })
654
- ).nullable(),
655
- // Entire events object is nullable
490
+ ).nullable().describe(
491
+ 'Defines event handlers mapped from UIEventType (e.g., "CLICK", "CHANGE") to an action configuration.'
492
+ ),
656
493
  children: zod.z.null()
657
- // Base children are null. When extended, it will be an array or null.
658
494
  });
659
495
  var openAINodeL4 = openAIBaseNode;
660
496
  var openAINodeL3 = openAIBaseNode.extend({
@@ -666,395 +502,1014 @@ var openAINodeL2 = openAIBaseNode.extend({
666
502
  var openAIUISpec = openAIBaseNode.extend({
667
503
  children: zod.z.array(openAINodeL2).nullable()
668
504
  });
669
- var uiSpecNode = zod.z.object({
670
- id: zod.z.string(),
671
- node_type: zod.z.string(),
672
- props: runtimeRecord,
673
- bindings: runtimeRecord,
674
- events: zod.z.record(
675
- zod.z.string(),
676
- zod.z.object({
677
- action: zod.z.string(),
678
- target: zod.z.string(),
679
- payload: runtimeRecord
680
- })
681
- ).nullable(),
682
- children: zod.z.lazy(() => zod.z.array(uiSpecNode)).nullable()
683
- });
684
- zod.z.discriminatedUnion("type", [
685
- zod.z.object({
686
- type: zod.z.literal("UI_EVENT"),
687
- event: uiEvent
688
- }),
689
- zod.z.object({
690
- type: zod.z.literal("AI_RESPONSE"),
691
- node: uiSpecNode
692
- }),
693
- zod.z.object({
694
- type: zod.z.literal("PARTIAL_UPDATE"),
695
- nodeId: zod.z.string(),
696
- node: uiSpecNode
697
- }),
698
- zod.z.object({
699
- type: zod.z.literal("ADD_NODE"),
700
- parentId: zod.z.string(),
701
- node: uiSpecNode,
702
- index: zod.z.number().nullable()
703
- }),
704
- zod.z.object({
705
- type: zod.z.literal("REMOVE_NODE"),
706
- nodeId: zod.z.string()
707
- }),
708
- zod.z.object({
709
- type: zod.z.literal("ERROR"),
710
- message: zod.z.string()
711
- }),
712
- zod.z.object({
713
- type: zod.z.literal("LOADING"),
714
- isLoading: zod.z.boolean()
715
- })
716
- ]);
717
- zod.z.object({
718
- layout: uiSpecNode.nullable(),
719
- loading: zod.z.boolean(),
720
- history: zod.z.array(uiEvent),
721
- error: zod.z.string().nullable()
722
- });
723
- zod.z.object({
724
- schema: zod.z.record(zod.z.unknown()),
725
- goal: zod.z.string(),
726
- history: zod.z.array(uiEvent).nullable(),
727
- userContext: zod.z.record(zod.z.unknown()).nullable().optional()
728
- });
729
-
730
- // src/core/system-events.ts
731
- var SystemEventType = /* @__PURE__ */ ((SystemEventType2) => {
732
- SystemEventType2["PLAN_START"] = "PLAN_START";
733
- SystemEventType2["PLAN_PROMPT_CREATED"] = "PLAN_PROMPT_CREATED";
734
- SystemEventType2["PLAN_RESPONSE_CHUNK"] = "PLAN_RESPONSE_CHUNK";
735
- SystemEventType2["PLAN_COMPLETE"] = "PLAN_COMPLETE";
736
- SystemEventType2["PLAN_ERROR"] = "PLAN_ERROR";
737
- SystemEventType2["BINDING_RESOLUTION_START"] = "BINDING_RESOLUTION_START";
738
- SystemEventType2["BINDING_RESOLUTION_COMPLETE"] = "BINDING_RESOLUTION_COMPLETE";
739
- SystemEventType2["DATA_FETCH_START"] = "DATA_FETCH_START";
740
- SystemEventType2["DATA_FETCH_COMPLETE"] = "DATA_FETCH_COMPLETE";
741
- SystemEventType2["RENDER_START"] = "RENDER_START";
742
- SystemEventType2["RENDER_COMPLETE"] = "RENDER_COMPLETE";
743
- SystemEventType2["PREFETCH_START"] = "PREFETCH_START";
744
- SystemEventType2["PREFETCH_COMPLETE"] = "PREFETCH_COMPLETE";
745
- return SystemEventType2;
746
- })(SystemEventType || {});
747
- var SystemEventManager = class {
748
- constructor() {
749
- this.listeners = {};
750
- }
751
- /**
752
- * Register a listener for a specific system event type
753
- *
754
- * @param eventType - The system event type to listen for
755
- * @param listener - The listener function
756
- * @returns Function to unregister the listener
757
- */
758
- on(eventType, listener) {
759
- if (!this.listeners[eventType]) {
760
- this.listeners[eventType] = [];
761
- }
762
- this.listeners[eventType]?.push(listener);
763
- return () => {
764
- if (this.listeners[eventType]) {
765
- this.listeners[eventType] = this.listeners[eventType]?.filter(
766
- (l) => l !== listener
767
- );
768
- }
769
- };
770
- }
771
- /**
772
- * Emit a system event to all registered listeners
773
- *
774
- * @param event - The system event to emit
775
- */
776
- async emit(event) {
777
- const listeners = this.listeners[event.type] || [];
778
- for (const listener of listeners) {
779
- await listener(event);
780
- }
781
- }
782
- };
783
- var systemEvents = new SystemEventManager();
784
- function createSystemEvent(type, data) {
785
- return {
786
- type,
787
- timestamp: Date.now(),
788
- ...data
789
- };
790
- }
791
-
792
- // src/env.ts
793
- var rawApiKeyFromEnv = process.env.VITE_OPENAI_API_KEY;
794
- var env = {
795
- MOCK_PLANNER: process.env.VITE_MOCK_PLANNER || "0",
796
- // Simplified MOCK_PLANNER assignment
797
- NODE_ENV: process.env.VITE_NODE_ENV || "production",
798
- // Simplified NODE_ENV assignment
799
- OPENAI_API_KEY: rawApiKeyFromEnv || ""
800
- };
801
505
 
802
506
  // src/core/planner.ts
803
- var getOpenAIClient = (apiKey) => {
804
- return openai.createOpenAI({
805
- apiKey,
806
- // Use the provided key directly
807
- compatibility: "strict"
507
+ var getAnthropicClient = (apiKey) => {
508
+ return anthropic.createAnthropic({
509
+ apiKey
808
510
  });
809
511
  };
810
- function mockPlanner(input, targetNodeId, customPrompt) {
811
- if (customPrompt) {
812
- console.log("mockPlanner received customPrompt:", customPrompt);
813
- }
814
- const taskSchema = input.schema.tasks;
815
- const taskData = taskSchema?.sampleData || [
816
- {
817
- id: "1",
818
- title: "Example Task 1",
819
- description: "This is a sample task",
820
- status: "pending",
821
- priority: "medium"
822
- },
823
- {
824
- id: "2",
825
- title: "Example Task 2",
826
- description: "Another sample task",
827
- status: "completed",
828
- priority: "high"
829
- }
830
- ];
831
- const mockNode = {
832
- id: targetNodeId || "root",
512
+ function mockPlanner(_input) {
513
+ return {
514
+ id: "task-dashboard",
833
515
  node_type: "Container",
834
- props: {
835
- className: "p-4 space-y-6"
836
- },
516
+ props: { className: "p-4 space-y-4" },
837
517
  bindings: null,
838
518
  events: null,
839
519
  children: [
840
520
  {
841
- id: "header-1",
842
- node_type: "Header",
843
- props: {
844
- title: "Task Management Dashboard",
845
- className: "mb-4"
846
- },
521
+ id: "header",
522
+ node_type: "Container",
523
+ props: { className: "flex justify-between items-center mb-4" },
847
524
  bindings: null,
848
525
  events: null,
849
- children: null
526
+ children: [
527
+ {
528
+ id: "title",
529
+ node_type: "Text",
530
+ props: { text: "Task Dashboard", className: "text-2xl font-bold" },
531
+ bindings: null,
532
+ events: null,
533
+ children: null
534
+ },
535
+ {
536
+ id: "add-task-button",
537
+ node_type: "Button",
538
+ props: { label: "Add Task", variant: "default" },
539
+ bindings: null,
540
+ events: {
541
+ CLICK: { action: "SHOW_DETAIL", target: "new-task-form" }
542
+ },
543
+ children: null
544
+ }
545
+ ]
850
546
  },
851
547
  {
852
548
  id: "main-content",
853
549
  node_type: "Container",
854
- props: {
855
- className: "grid grid-cols-1 gap-6 md:grid-cols-3"
856
- },
550
+ props: { className: "flex gap-4" },
857
551
  bindings: null,
858
552
  events: null,
859
553
  children: [
860
554
  {
861
555
  id: "tasks-container",
862
556
  node_type: "Container",
863
- props: {
864
- className: "md:col-span-2"
865
- },
557
+ props: { className: "flex-1" },
866
558
  bindings: null,
867
559
  events: null,
868
560
  children: [
869
561
  {
870
- id: "list-heading",
871
- node_type: "Container",
872
- props: {
873
- className: "flex justify-between items-center mb-4"
874
- },
875
- bindings: null,
562
+ id: "task-list",
563
+ node_type: "ListView",
564
+ props: { className: "space-y-2" },
565
+ bindings: { data: "tasks.data" },
876
566
  events: null,
877
567
  children: [
878
568
  {
879
- id: "list-title",
880
- node_type: "Header",
881
- props: {
882
- title: "Tasks",
883
- className: "border-none p-0 m-0"
884
- },
569
+ id: "task-item-{{index}}",
570
+ node_type: "Card",
571
+ props: { className: "p-3 border rounded" },
885
572
  bindings: null,
886
573
  events: null,
887
- children: null
888
- },
889
- {
890
- id: "add-task-button",
891
- node_type: "Button",
892
- props: {
893
- label: "Add Task",
894
- variant: "default"
895
- },
896
- bindings: null,
897
- events: {
898
- onClick: {
899
- action: "ADD_TASK",
900
- target: "tasks-container",
901
- payload: {}
574
+ children: [
575
+ {
576
+ id: "task-title-{{index}}",
577
+ node_type: "Text",
578
+ props: { className: "font-medium" },
579
+ bindings: { text: "item.title" },
580
+ events: null,
581
+ children: null
582
+ },
583
+ {
584
+ id: "task-status-{{index}}",
585
+ node_type: "Badge",
586
+ props: {},
587
+ bindings: { text: "item.status" },
588
+ events: null,
589
+ children: null
590
+ },
591
+ {
592
+ id: "view-details-button-{{index}}",
593
+ node_type: "Button",
594
+ props: { label: "View Details", variant: "outline", size: "sm" },
595
+ bindings: null,
596
+ events: {
597
+ CLICK: { action: "SHOW_DETAIL", target: "task-detail" }
598
+ },
599
+ children: null
902
600
  }
903
- },
904
- children: null
601
+ ]
905
602
  }
906
603
  ]
604
+ }
605
+ ]
606
+ },
607
+ {
608
+ id: "task-detail",
609
+ node_type: "Container",
610
+ props: { className: "w-1/3 border-l pl-4", visible: false },
611
+ bindings: null,
612
+ events: null,
613
+ children: [
614
+ {
615
+ id: "detail-title",
616
+ node_type: "Text",
617
+ props: { text: "Task Details", className: "text-lg font-bold mb-2" },
618
+ bindings: null,
619
+ events: null,
620
+ children: null
907
621
  },
908
622
  {
909
- id: "task-list",
910
- node_type: "ListView",
911
- props: {
912
- selectable: "true"
913
- },
914
- bindings: {
915
- data: "tasks.data",
916
- fields: JSON.stringify([
917
- { key: "id", label: "ID" },
918
- { key: "title", label: "Title" },
919
- { key: "status", label: "Status" },
920
- { key: "priority", label: "Priority" }
921
- ])
922
- },
623
+ id: "detail-content",
624
+ node_type: "Text",
625
+ props: {},
626
+ bindings: { text: "tasks.selected.description" },
627
+ events: null,
628
+ children: null
629
+ },
630
+ {
631
+ id: "close-detail-button",
632
+ node_type: "Button",
633
+ props: { label: "Close", variant: "ghost" },
634
+ bindings: null,
923
635
  events: {
924
- onSelect: {
925
- action: "SELECT_TASK",
926
- target: "task-detail",
927
- payload: {
928
- source: "task-list"
929
- }
930
- }
636
+ CLICK: { action: "HIDE_DETAIL", target: "task-detail" }
931
637
  },
932
638
  children: null
933
639
  }
934
640
  ]
641
+ }
642
+ ]
643
+ }
644
+ ]
645
+ };
646
+ }
647
+ async function callPlannerLLM(input, apiKey, useMock) {
648
+ await systemEvents.emit(
649
+ createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
650
+ );
651
+ if (useMock || typeof window !== "undefined" && window.__USE_MOCK_PLANNER) {
652
+ console.log("[Mock Planner] Using mock planner for testing");
653
+ const mockLayout = mockPlanner();
654
+ await systemEvents.emit(
655
+ createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
656
+ layout: mockLayout,
657
+ executionTimeMs: 0
658
+ })
659
+ );
660
+ return mockLayout;
661
+ }
662
+ if (!apiKey) {
663
+ console.warn(
664
+ `Anthropic API key was not provided to callPlannerLLM. Returning a placeholder UI.`
665
+ );
666
+ return {
667
+ id: "root-no-api-key",
668
+ node_type: "Container",
669
+ props: {
670
+ className: "p-4 flex flex-col items-center justify-center h-full"
671
+ },
672
+ bindings: null,
673
+ events: null,
674
+ children: [
675
+ {
676
+ id: "no-api-key-message",
677
+ node_type: "Text",
678
+ props: {
679
+ text: "Anthropic API Key is required to generate the UI. Please provide one in your environment configuration.",
680
+ className: "text-red-500 text-center"
935
681
  },
936
- {
937
- id: "task-detail",
938
- node_type: "Detail",
939
- props: {
940
- title: "Task Details",
941
- visible: "true"
942
- },
943
- bindings: {
944
- data: JSON.stringify(taskData[0]),
945
- fields: JSON.stringify([
946
- { key: "title", label: "Title", type: "heading" },
947
- { key: "description", label: "Description", type: "content" },
948
- { key: "status", label: "Status" },
949
- { key: "priority", label: "Priority" },
950
- { key: "dueDate", label: "Due Date" }
951
- ])
952
- },
953
- events: {
954
- onBack: {
955
- action: "CLOSE_DETAIL",
956
- target: "task-detail",
957
- payload: {}
958
- }
959
- },
960
- children: null
682
+ bindings: null,
683
+ events: null,
684
+ children: null
685
+ }
686
+ ]
687
+ };
688
+ }
689
+ const startTime = Date.now();
690
+ const templateValuesForPrompt = input.userContext ? { ...input.userContext } : void 0;
691
+ const promptTemplateFromInput = typeof input.userContext?.promptTemplate === "string" ? input.userContext.promptTemplate : void 0;
692
+ const prompt = buildPrompt(
693
+ input,
694
+ promptTemplateFromInput,
695
+ // Use template from input.userContext
696
+ templateValuesForPrompt
697
+ // Use values from input.userContext
698
+ );
699
+ await systemEvents.emit(
700
+ createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
701
+ );
702
+ try {
703
+ let uiSpec;
704
+ if (typeof window !== "undefined") {
705
+ const response = await fetch("/api/generate-ui", {
706
+ method: "POST",
707
+ headers: {
708
+ "Content-Type": "application/json"
709
+ },
710
+ body: JSON.stringify({ prompt, apiKey })
711
+ });
712
+ if (!response.ok) {
713
+ const errorData = await response.json();
714
+ throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
715
+ }
716
+ const data = await response.json();
717
+ uiSpec = data.uiSpec;
718
+ } else {
719
+ const { object } = await ai.generateObject({
720
+ model: getAnthropicClient(apiKey)("claude-haiku-4-5"),
721
+ schema: openAIUISpec,
722
+ mode: "tool",
723
+ messages: [{ role: "user", content: prompt }],
724
+ temperature: 0.2,
725
+ maxTokens: 4e3
726
+ });
727
+ uiSpec = object;
728
+ }
729
+ await systemEvents.emit(
730
+ createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
731
+ layout: uiSpec,
732
+ executionTimeMs: Date.now() - startTime
733
+ })
734
+ );
735
+ return uiSpec;
736
+ } catch (error) {
737
+ console.error("Error calling LLM planner:", error);
738
+ await systemEvents.emit(
739
+ createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
740
+ error: error instanceof Error ? error : new Error(String(error))
741
+ })
742
+ );
743
+ throw error;
744
+ }
745
+ }
746
+
747
+ // src/core/bindings.ts
748
+ function getValueByPath(context, path) {
749
+ const parts = path.split(".");
750
+ let current = context;
751
+ for (const part of parts) {
752
+ if (current === null || current === void 0) {
753
+ return void 0;
754
+ }
755
+ if (typeof current !== "object") {
756
+ return void 0;
757
+ }
758
+ current = current[part];
759
+ }
760
+ return current;
761
+ }
762
+ function setValueByPath(context, path, value) {
763
+ if (!path) {
764
+ return context;
765
+ }
766
+ const result = { ...context };
767
+ const parts = path.split(".");
768
+ let current = result;
769
+ for (let i = 0; i < parts.length - 1; i++) {
770
+ const part = parts[i];
771
+ if (typeof current !== "object" || current === null) {
772
+ console.warn(
773
+ `setValueByPath: Cannot traverse path "${path}". Parent segment "${parts[i - 1] || "(root)"}" is not an object.`
774
+ );
775
+ return context;
776
+ }
777
+ const currentAsObject = current;
778
+ const nextPartValue = currentAsObject[part];
779
+ if (nextPartValue === void 0 || nextPartValue === null) {
780
+ currentAsObject[part] = {};
781
+ } else if (typeof nextPartValue !== "object") {
782
+ console.warn(
783
+ `setValueByPath: Cannot create nested path "${path}". Segment "${part}" is not an object.`
784
+ );
785
+ return context;
786
+ } else {
787
+ currentAsObject[part] = { ...nextPartValue };
788
+ }
789
+ current = currentAsObject[part];
790
+ }
791
+ const lastPart = parts[parts.length - 1];
792
+ if (typeof current === "object" && current !== null) {
793
+ current[lastPart] = value;
794
+ } else {
795
+ console.warn(
796
+ `setValueByPath: Could not set value for path "${path}". Final segment parent is not an object.`
797
+ );
798
+ return context;
799
+ }
800
+ return result;
801
+ }
802
+ function processBinding(binding, context, itemData) {
803
+ if (typeof binding === "string") {
804
+ const exactMatchArr = binding.match(/^{{(.*)}}$/);
805
+ const pathInsideExact = exactMatchArr ? exactMatchArr[1].trim() : null;
806
+ if (pathInsideExact !== null && !pathInsideExact.includes("{{") && !pathInsideExact.includes("}}")) {
807
+ const pathToResolve = pathInsideExact;
808
+ let resolvedValue = void 0;
809
+ if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
810
+ if (pathToResolve.startsWith("item.")) {
811
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
812
+ } else {
813
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
814
+ }
815
+ } else if (itemData && pathToResolve in itemData) {
816
+ resolvedValue = getValueByPath(itemData, pathToResolve);
817
+ }
818
+ if (resolvedValue === void 0) {
819
+ resolvedValue = getValueByPath(context, pathToResolve);
820
+ }
821
+ return resolvedValue;
822
+ } else if (binding.includes("{{") && binding.includes("}}")) {
823
+ const resolvedString = binding.replaceAll(
824
+ /{{(.*?)}}/g,
825
+ // Non-greedy match inside braces
826
+ (match, path) => {
827
+ const trimmedPath = path.trim();
828
+ let resolvedValue = void 0;
829
+ if ((trimmedPath.startsWith("item.") || trimmedPath.startsWith("row.")) && itemData) {
830
+ if (trimmedPath.startsWith("item.")) {
831
+ resolvedValue = getValueByPath(
832
+ itemData,
833
+ trimmedPath.substring(5)
834
+ );
835
+ } else {
836
+ resolvedValue = getValueByPath(
837
+ itemData,
838
+ trimmedPath.substring(4)
839
+ );
840
+ }
841
+ } else if (itemData && trimmedPath in itemData) {
842
+ resolvedValue = getValueByPath(itemData, trimmedPath);
843
+ }
844
+ if (resolvedValue === void 0) {
845
+ resolvedValue = getValueByPath(context, trimmedPath);
846
+ }
847
+ return resolvedValue === null || resolvedValue === void 0 ? "" : String(resolvedValue);
848
+ }
849
+ );
850
+ return resolvedString;
851
+ } else {
852
+ const pathToResolve = binding;
853
+ let resolvedValue = void 0;
854
+ if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
855
+ if (pathToResolve.startsWith("item.")) {
856
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
857
+ } else {
858
+ resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
859
+ }
860
+ if (resolvedValue !== void 0) {
861
+ return resolvedValue;
862
+ }
863
+ }
864
+ if (itemData && !pathToResolve.includes(".") && pathToResolve in itemData) {
865
+ resolvedValue = getValueByPath(itemData, pathToResolve);
866
+ }
867
+ if (resolvedValue === void 0) {
868
+ resolvedValue = getValueByPath(context, pathToResolve);
869
+ }
870
+ return resolvedValue;
871
+ }
872
+ }
873
+ if (Array.isArray(binding)) {
874
+ return binding.map((item) => processBinding(item, context, itemData));
875
+ }
876
+ if (binding !== null && typeof binding === "object") {
877
+ const result = {};
878
+ for (const [key, value] of Object.entries(binding)) {
879
+ result[key] = processBinding(value, context, itemData);
880
+ }
881
+ return result;
882
+ }
883
+ return binding;
884
+ }
885
+ async function resolveBindings(node, context, itemData) {
886
+ if (node.id === "task-detail") {
887
+ console.log(
888
+ `[resolveBindings task-detail SPECIFIC CHECK] Context for task-detail:`,
889
+ JSON.stringify(context)
890
+ );
891
+ console.log(
892
+ `[resolveBindings task-detail SPECIFIC CHECK] context.isTaskDetailDialogVisible = ${context.isTaskDetailDialogVisible}`
893
+ );
894
+ console.log(
895
+ `[resolveBindings task-detail SPECIFIC CHECK] context.selectedTask = ${JSON.stringify(
896
+ context.selectedTask
897
+ )}`
898
+ );
899
+ }
900
+ console.log(
901
+ `[resolveBindings ENTRY] Node ID: ${node.id}, Type: ${node.node_type}, Has itemData: ${!!itemData}, Context keys: ${Object.keys(
902
+ context || {}
903
+ // Ensure context is not null before Object.keys
904
+ ).join(", ")}`
905
+ );
906
+ const result = {
907
+ ...node,
908
+ props: node.props ? JSON.parse(JSON.stringify(node.props)) : null,
909
+ events: node.events ? JSON.parse(JSON.stringify(node.events)) : null,
910
+ bindings: node.bindings ? JSON.parse(JSON.stringify(node.bindings)) : null,
911
+ children: null
912
+ // Initialize children to null
913
+ };
914
+ let mergedProps = node.props ? { ...JSON.parse(JSON.stringify(node.props)) } : null;
915
+ const PROP_KEYS_TO_RESOLVE = /* @__PURE__ */ new Set([
916
+ "text",
917
+ "label",
918
+ "title",
919
+ "placeholder",
920
+ "value"
921
+ ]);
922
+ if (node.props) {
923
+ for (const [key, value] of Object.entries(node.props)) {
924
+ if (!mergedProps)
925
+ mergedProps = {};
926
+ if (PROP_KEYS_TO_RESOLVE.has(key)) {
927
+ const resolvedPropValue = processBinding(
928
+ value,
929
+ context,
930
+ itemData ?? void 0
931
+ );
932
+ if (resolvedPropValue !== void 0) {
933
+ mergedProps[key] = resolvedPropValue;
934
+ } else {
935
+ if (typeof value === "string" && (value.includes("{{") || value.includes("."))) {
936
+ mergedProps[key] = "";
937
+ } else {
938
+ mergedProps[key] = value;
939
+ }
940
+ }
941
+ } else if (value !== void 0) {
942
+ mergedProps[key] = value;
943
+ } else {
944
+ delete mergedProps[key];
945
+ }
946
+ }
947
+ }
948
+ result.props = mergedProps;
949
+ if (node.bindings) {
950
+ for (const [key, bindingValue] of Object.entries(node.bindings)) {
951
+ const resolvedValue = processBinding(
952
+ bindingValue,
953
+ context,
954
+ itemData ?? void 0
955
+ );
956
+ if (node.id === "task-detail") {
957
+ console.log(
958
+ `[resolveBindings - ${node.id}] Binding for '${key}': '${String(
959
+ bindingValue
960
+ )}' -> Resolved:`,
961
+ resolvedValue
962
+ );
963
+ }
964
+ if (resolvedValue !== void 0) {
965
+ if (!result.props)
966
+ result.props = {};
967
+ result.props[key] = resolvedValue;
968
+ if (node.id === "task-detail") {
969
+ console.log(
970
+ `[resolveBindings - ${node.id}] Set result.props.${key} =`,
971
+ resolvedValue
972
+ );
973
+ }
974
+ } else {
975
+ if (node.id === "task-detail") {
976
+ console.log(
977
+ `[resolveBindings - ${node.id}] Binding for '${key}' ('${String(
978
+ bindingValue
979
+ )}') resolved to undefined. Not setting prop.`
980
+ );
981
+ }
982
+ }
983
+ }
984
+ }
985
+ if (node.events) {
986
+ const processedEvents = {};
987
+ for (const eventType in node.events) {
988
+ const eventConfig = node.events[eventType];
989
+ processedEvents[eventType] = {
990
+ ...eventConfig,
991
+ payload: eventConfig.payload ? processBinding(
992
+ eventConfig.payload,
993
+ context,
994
+ itemData ?? void 0
995
+ ) : null
996
+ };
997
+ }
998
+ result.events = processedEvents;
999
+ } else {
1000
+ result.events = null;
1001
+ }
1002
+ const dataBindingValue = result.props?.data ?? result.props?.items;
1003
+ if ((node.node_type === "ListView" || node.node_type === "Table") && Array.isArray(dataBindingValue) && node.children && node.children.length > 0) {
1004
+ const templateChild = node.children[0];
1005
+ const looksLikeTemplate = node.children.length === 1 && (templateChild.id.includes("{{") || templateChild.id.includes("-template"));
1006
+ const isAlreadyExpanded = !looksLikeTemplate && node.children.length > 1;
1007
+ if (isAlreadyExpanded) {
1008
+ result.children = await Promise.all(
1009
+ node.children.map(
1010
+ (existingChild) => resolveBindings(existingChild, context, itemData)
1011
+ )
1012
+ // itemData is tricky here, should it be from parent or is it irrelevant?
1013
+ // For now, assume itemData is not applicable for re-resolving top-level list items this way.
1014
+ );
1015
+ } else {
1016
+ const mappedChildren = await Promise.all(
1017
+ dataBindingValue.map(async (currentItemData, index) => {
1018
+ try {
1019
+ if (typeof currentItemData !== "object" || currentItemData === null) {
1020
+ console.warn(
1021
+ `List item at index ${index} for node ${node.id} is not an object:`,
1022
+ currentItemData
1023
+ );
1024
+ return null;
1025
+ }
1026
+ const currentItemAsRecord = currentItemData;
1027
+ const itemId = currentItemAsRecord.id;
1028
+ const instanceId = `${templateChild.id}-${itemId || index}`;
1029
+ const childNodeInstance = JSON.parse(
1030
+ JSON.stringify(templateChild)
1031
+ );
1032
+ childNodeInstance.id = instanceId;
1033
+ makeChildIdsUniqueInInstance(
1034
+ childNodeInstance,
1035
+ instanceId,
1036
+ templateChild.id
1037
+ );
1038
+ const resolvedChild = await resolveBindings(
1039
+ childNodeInstance,
1040
+ context,
1041
+ currentItemAsRecord
1042
+ );
1043
+ if (resolvedChild && !resolvedChild.props)
1044
+ resolvedChild.props = {};
1045
+ if (resolvedChild && resolvedChild.props) {
1046
+ resolvedChild.props.key = itemId || `${node.id}-item-${index}`;
1047
+ }
1048
+ return resolvedChild;
1049
+ } catch (error) {
1050
+ console.error(
1051
+ `[resolveBindings Error] Error processing item at index ${index} for node ${node.id}:`,
1052
+ error,
1053
+ "Item Data:",
1054
+ currentItemData
1055
+ );
1056
+ return null;
1057
+ }
1058
+ })
1059
+ );
1060
+ result.children = mappedChildren.filter(
1061
+ (child) => child !== null
1062
+ );
1063
+ }
1064
+ } else if (node.children && node.children.length > 0) {
1065
+ result.children = await Promise.all(
1066
+ node.children.map((child) => resolveBindings(child, context, itemData))
1067
+ );
1068
+ } else {
1069
+ result.children = [];
1070
+ }
1071
+ return result;
1072
+ }
1073
+ function executeAction(action, target, payload, context = {}) {
1074
+ let newContext = { ...context };
1075
+ switch (action) {
1076
+ case "SHOW_DETAIL" /* SHOW_DETAIL */: {
1077
+ const taskId = payload?.taskId;
1078
+ const dialogNodeId = target;
1079
+ if (taskId && dialogNodeId) {
1080
+ const tasksData = getValueByPath(context, "tasks.data");
1081
+ if (Array.isArray(tasksData)) {
1082
+ const foundTask = tasksData.find(
1083
+ (t) => t.id === taskId
1084
+ );
1085
+ if (foundTask) {
1086
+ newContext = setValueByPath(newContext, "selectedTask", foundTask);
1087
+ } else {
1088
+ console.warn(
1089
+ `[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: Task with id "${taskId}" not found in tasks.data.`
1090
+ );
1091
+ newContext = setValueByPath(newContext, "selectedTask", null);
1092
+ }
1093
+ } else {
1094
+ console.warn(
1095
+ `[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: context.tasks.data is not an array or not found.`
1096
+ );
1097
+ newContext = setValueByPath(newContext, "selectedTask", null);
1098
+ }
1099
+ } else {
1100
+ console.warn(
1101
+ `[executeAction] ${"SHOW_DETAIL" /* SHOW_DETAIL */}: payload.taskId or target (dialogNodeId) was missing. Dialog will be shown without a selected task.`
1102
+ );
1103
+ newContext = setValueByPath(newContext, "selectedTask", null);
1104
+ }
1105
+ newContext = setValueByPath(
1106
+ newContext,
1107
+ // Use the potentially modified newContext (with selectedTask set or cleared)
1108
+ "isTaskDetailDialogVisible",
1109
+ true
1110
+ );
1111
+ break;
1112
+ }
1113
+ case "HIDE_DIALOG" /* HIDE_DIALOG */: {
1114
+ newContext = setValueByPath(newContext, "selectedTask", null);
1115
+ newContext = setValueByPath(
1116
+ newContext,
1117
+ "isTaskDetailDialogVisible",
1118
+ false
1119
+ );
1120
+ break;
1121
+ }
1122
+ case "HIDE_DETAIL" /* HIDE_DETAIL */: {
1123
+ newContext = setValueByPath(newContext, "selectedItemForDetail", null);
1124
+ newContext = setValueByPath(newContext, "isDetailViewOpen", false);
1125
+ break;
1126
+ }
1127
+ case "OPEN_DIALOG" /* OPEN_DIALOG */: {
1128
+ const dialogId = target || payload?.dialogId;
1129
+ if (dialogId === "taskDetailDialogNodeId" || !dialogId) {
1130
+ newContext = setValueByPath(
1131
+ newContext,
1132
+ "isTaskDetailDialogVisible",
1133
+ true
1134
+ );
1135
+ } else {
1136
+ console.warn(
1137
+ `[executeAction] ${"OPEN_DIALOG" /* OPEN_DIALOG */}: Unhandled dialogId: ${dialogId}.`
1138
+ );
1139
+ }
1140
+ break;
1141
+ }
1142
+ case "CLOSE_DIALOG" /* CLOSE_DIALOG */: {
1143
+ const dialogId = target || payload?.dialogId;
1144
+ if (dialogId === "taskDetailDialogNodeId" || !dialogId) {
1145
+ newContext = setValueByPath(
1146
+ newContext,
1147
+ "isTaskDetailDialogVisible",
1148
+ false
1149
+ );
1150
+ newContext = setValueByPath(newContext, "selectedTask", null);
1151
+ } else {
1152
+ console.warn(
1153
+ `[executeAction] ${"CLOSE_DIALOG" /* CLOSE_DIALOG */}: Unhandled dialogId: ${dialogId}.`
1154
+ );
1155
+ }
1156
+ break;
1157
+ }
1158
+ case "UPDATE_DATA" /* UPDATE_DATA */: {
1159
+ let updatedContext = context;
1160
+ if (target && payload && "value" in payload) {
1161
+ updatedContext = setValueByPath(context, target, payload.value);
1162
+ } else {
1163
+ console.warn(
1164
+ `[executeAction] ${"UPDATE_DATA" /* UPDATE_DATA */} requires targetPath (data path) and payload with 'value' property.`
1165
+ );
1166
+ }
1167
+ newContext = updatedContext;
1168
+ break;
1169
+ }
1170
+ case "ADD_ITEM" /* ADD_ITEM */: {
1171
+ if (!target) {
1172
+ console.warn(`[executeAction] ADD_ITEM requires target path.`);
1173
+ break;
1174
+ }
1175
+ if (!payload?.item) {
1176
+ console.warn(
1177
+ `[executeAction] ADD_ITEM requires payload with item property.`
1178
+ );
1179
+ break;
1180
+ }
1181
+ const list = getValueByPath(newContext, target);
1182
+ if (!Array.isArray(list)) {
1183
+ console.warn(
1184
+ `[executeAction] ADD_ITEM failed: target path "${target}" does not resolve to an array.`
1185
+ );
1186
+ break;
1187
+ }
1188
+ const newItem = payload.item;
1189
+ const position = payload.position;
1190
+ let newList;
1191
+ if (position === "start") {
1192
+ newList = [newItem, ...list];
1193
+ } else {
1194
+ newList = [...list, newItem];
1195
+ }
1196
+ newContext = setValueByPath(newContext, target, newList);
1197
+ break;
1198
+ }
1199
+ case "DELETE_ITEM" /* DELETE_ITEM */: {
1200
+ if (!target) {
1201
+ console.warn(`[executeAction] DELETE_ITEM requires target path.`);
1202
+ break;
1203
+ }
1204
+ const itemId = payload?.id;
1205
+ if (itemId === void 0 || itemId === null) {
1206
+ console.warn(
1207
+ `[executeAction] DELETE_ITEM requires payload with id property.`
1208
+ );
1209
+ break;
1210
+ }
1211
+ const list = getValueByPath(newContext, target);
1212
+ if (!Array.isArray(list)) {
1213
+ console.warn(
1214
+ `[executeAction] DELETE_ITEM failed: target path "${target}" does not resolve to an array.`
1215
+ );
1216
+ break;
1217
+ }
1218
+ const newList = list.filter(
1219
+ (item) => item?.id !== itemId
1220
+ );
1221
+ if (newList.length !== list.length) {
1222
+ newContext = setValueByPath(newContext, target, newList);
1223
+ } else {
1224
+ console.warn(
1225
+ `[executeAction] DELETE_ITEM: Item with id "${itemId}" not found in list at path "${target}".`
1226
+ );
1227
+ }
1228
+ break;
1229
+ }
1230
+ case "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */: {
1231
+ if (!target) {
1232
+ console.warn(
1233
+ "[executeAction] SAVE_TASK_CHANGES requires target (task ID)."
1234
+ );
1235
+ break;
1236
+ }
1237
+ const taskIdToSave = target;
1238
+ const currentTasks = getValueByPath(newContext, "tasks.data");
1239
+ const selectedTaskData = getValueByPath(newContext, "selectedTask");
1240
+ if (currentTasks && selectedTaskData && selectedTaskData.id === taskIdToSave) {
1241
+ const updatedTasks = currentTasks.map(
1242
+ (task) => task.id === taskIdToSave ? { ...task, ...selectedTaskData, ...payload } : task
1243
+ // Merge selectedTaskData and any direct payload changes
1244
+ );
1245
+ newContext = setValueByPath(newContext, "tasks.data", updatedTasks);
1246
+ newContext = setValueByPath(newContext, "selectedTask", null);
1247
+ newContext = setValueByPath(
1248
+ newContext,
1249
+ "isTaskDetailDialogVisible",
1250
+ false
1251
+ );
1252
+ } else {
1253
+ console.warn(
1254
+ "[executeAction] SAVE_TASK_CHANGES: Could not save. Task list, selected task, or ID mismatch.",
1255
+ {
1256
+ taskIdToSave,
1257
+ selectedTaskDataId: selectedTaskData?.id,
1258
+ currentTasksExists: !!currentTasks
961
1259
  }
962
- ]
1260
+ );
963
1261
  }
964
- ]
965
- };
966
- return mockNode;
967
- }
968
- async function callPlannerLLM(input, openaiApiKey, routeResolution) {
969
- await systemEvents.emit(
970
- createSystemEvent("PLAN_START" /* PLAN_START */, { plannerInput: input })
971
- );
972
- if (env.MOCK_PLANNER === "1") {
973
- console.warn(
974
- `Using mock planner because MOCK_PLANNER environment variable is set to "1".`
975
- );
976
- return mockPlanner(input);
977
- }
978
- if (!openaiApiKey) {
979
- console.warn(
980
- `OpenAI API key was not provided to callPlannerLLM. Falling back to mock planner.`
981
- );
982
- return mockPlanner(input);
983
- }
984
- const startTime = Date.now();
985
- const prompt = routeResolution?.prompt;
986
- if (!prompt) {
987
- throw new Error("ActionRouter did not provide a prompt to callPlannerLLM.");
1262
+ break;
1263
+ }
1264
+ default:
1265
+ console.warn(`[executeAction] Unhandled action type: ${action}`);
988
1266
  }
989
- await systemEvents.emit(
990
- createSystemEvent("PLAN_PROMPT_CREATED" /* PLAN_PROMPT_CREATED */, { prompt })
991
- );
992
- try {
993
- const { object: uiSpec } = await ai.generateObject({
994
- model: getOpenAIClient(openaiApiKey)("gpt-4o", {
995
- structuredOutputs: true
996
- }),
997
- schema: openAIUISpec,
998
- messages: [{ role: "user", content: prompt }],
999
- temperature: 0.2,
1000
- maxTokens: 4e3
1267
+ return newContext;
1268
+ }
1269
+ function makeChildIdsUniqueInInstance(parentNode, baseInstanceId, originalTemplateRootId) {
1270
+ if (parentNode.children) {
1271
+ parentNode.children = parentNode.children.map((child) => {
1272
+ let originalChildId = child.id;
1273
+ if (child.id.startsWith(originalTemplateRootId + "-")) {
1274
+ const parts = child.id.split("-");
1275
+ if (parts.length > 1) {
1276
+ originalChildId = parts[parts.length - 1];
1277
+ }
1278
+ }
1279
+ const newChildId = `${baseInstanceId}-${originalChildId}`;
1280
+ const newChild = {
1281
+ ...JSON.parse(JSON.stringify(child)),
1282
+ // Deep clone child
1283
+ id: newChildId
1284
+ };
1285
+ makeChildIdsUniqueInInstance(
1286
+ newChild,
1287
+ baseInstanceId,
1288
+ originalTemplateRootId
1289
+ );
1290
+ return newChild;
1001
1291
  });
1002
- await systemEvents.emit(
1003
- createSystemEvent("PLAN_COMPLETE" /* PLAN_COMPLETE */, {
1004
- layout: uiSpec,
1005
- executionTimeMs: Date.now() - startTime
1006
- })
1007
- );
1008
- return uiSpec;
1009
- } catch (error) {
1010
- console.error("Error calling LLM planner:", error);
1011
- await systemEvents.emit(
1012
- createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
1013
- error: error instanceof Error ? error : new Error(String(error))
1014
- })
1015
- );
1016
- throw error;
1017
1292
  }
1018
1293
  }
1019
- async function processEvent(event, router, schema, layout, dataContext, goal, userContext, openaiApiKey) {
1020
- const routeResolution = await router.resolveRoute(
1021
- event,
1022
- schema,
1023
- layout || null,
1024
- dataContext,
1025
- goal,
1026
- userContext
1027
- );
1028
- if (!routeResolution) {
1029
- throw new Error(
1030
- `No route found for event type: ${event.type}, node: ${event.nodeId}`
1031
- );
1032
- }
1033
- if (routeResolution.actionType.toString() === "NoOp") {
1034
- if (!layout)
1035
- throw new Error("Layout is undefined and action is NoOp");
1036
- return layout;
1037
- }
1038
- const plannerInputForLLM = routeResolution.plannerInput;
1039
- const newLayout = await callPlannerLLM(
1040
- plannerInputForLLM,
1041
- openaiApiKey || "",
1042
- routeResolution
1043
- );
1044
- return newLayout;
1294
+
1295
+ // src/core/action-router.ts
1296
+ var UI_GUIDANCE_BASE = `
1297
+ UI Guidance:
1298
+ 1. Create a focused interface that directly addresses the goal
1299
+ 2. Use appropriate UI patterns (lists, forms, details, etc.)
1300
+ 3. Include navigation between related views when needed
1301
+ 4. Keep the interface simple and intuitive
1302
+ 5. Bind to schema data where appropriate
1303
+ 6. **CRITICAL for Buttons:** All \`Button\` nodes **MUST** include a \`label\` property in their \`props\` (e.g., \`{ "props": { "label": "Click Me" } }\`).
1304
+ 7. Provide event handlers for user interactions - make sure to always include both action and target properties`;
1305
+ var LIST_BINDING_GUIDANCE = `8. **CRITICAL:** For \`ListView\` or \`Table\` nodes, the \`data\` binding key **MUST** point to the *exact path* of the data *array* within the context.`;
1306
+ var LIST_BINDING_EXAMPLE = `Example: If the context has \`{ tasks: { data: [...] } }\`, the binding **MUST** be \`{ "bindings": { "data": "tasks.data" } }\`. If the context has \`{ userList: [...] }\`, the binding **MUST** be \`{ "bindings": { "data": "userList" } }\`. **NEVER** bind to the parent object containing the array (e.g., DO NOT USE \`{ "bindings": { "data": "tasks" } }\`).`;
1307
+ var COMMON_UI_GUIDANCE = UI_GUIDANCE_BASE + "\n" + // Add a newline separator
1308
+ LIST_BINDING_GUIDANCE + // Add the specific list binding rule
1309
+ "\n" + // Add a newline separator
1310
+ LIST_BINDING_EXAMPLE;
1311
+ function processTemplate(template, values) {
1312
+ return template.replace(/\${(.*?)}/g, (match, key) => {
1313
+ const trimmedKey = key.trim();
1314
+ return trimmedKey in values ? String(values[trimmedKey]) : match;
1315
+ });
1045
1316
  }
1317
+ function buildPrompt(input, promptTemplate, templateValues) {
1318
+ const { schema, goal, history, userContext } = input;
1319
+ const schemaInfo = Object.entries(schema).map(([tableName, tableSchema]) => {
1320
+ const schemaString = typeof tableSchema === "object" && tableSchema !== null ? JSON.stringify(tableSchema) : String(tableSchema);
1321
+ return `Table: ${tableName}
1322
+ Schema: ${schemaString}`;
1323
+ }).join("\n\n");
1324
+ const recentEvents = history && history.length > 0 ? history.slice(-5).map(
1325
+ (event) => `Event: ${event.type} on node ${event.nodeId}${event.payload ? ` with payload ${JSON.stringify(event.payload)}` : ""}`
1326
+ ).join("\n") : "No recent events";
1327
+ const userContextSection = userContext ? `
1328
+
1329
+ User Context:
1330
+ ${JSON.stringify(userContext)}` : "";
1331
+ if (promptTemplate && templateValues) {
1332
+ const fullTemplateValues = {
1333
+ ...templateValues,
1334
+ schemaInfo,
1335
+ recentEvents,
1336
+ userContextString: userContextSection.trim(),
1337
+ // Use trimmed version
1338
+ commonUIGuidance: COMMON_UI_GUIDANCE,
1339
+ goal
1340
+ // Ensure goal is always available to templates
1341
+ };
1342
+ return processTemplate(promptTemplate, fullTemplateValues);
1343
+ }
1344
+ const interactionDescription = history && history.length > 0 ? `The user's last action was: ${history[history.length - 1].type} on node ${history[history.length - 1].nodeId}` : "The user initiated the session for the goal";
1345
+ return `
1346
+ You are an expert UI generator.
1347
+ Create a user interface that achieves the following goal: "${goal}".
1348
+ ${interactionDescription}.
1349
+
1350
+ Available data schema:
1351
+ ${schemaInfo}
1046
1352
 
1047
- // src/core/state.ts
1353
+ Recent user interactions:
1354
+ ${recentEvents}${userContextSection}
1355
+
1356
+ Generate a complete UI specification in JSON format that matches the following TypeScript type:
1357
+ type UISpecNode = { id: string; node_type: string; props?: Record<string, unknown>; bindings?: Record<string, unknown>; events?: Record<string, { action: string; target: string; payload?: Record<string, unknown>; }>; children?: UISpecNode[]; };
1358
+ ${COMMON_UI_GUIDANCE}
1359
+
1360
+ Respond ONLY with the JSON UI specification and no other text.
1361
+ `;
1362
+ }
1363
+ var ActionRouter = class {
1364
+ constructor(apiKey, planningConfig) {
1365
+ this.apiKey = apiKey;
1366
+ this.planningConfig = planningConfig || {
1367
+ prefetchDepth: 1,
1368
+ temperature: 0.5,
1369
+ streaming: false
1370
+ };
1371
+ }
1372
+ /**
1373
+ * Find the appropriate route for an event
1374
+ * @param event - UI event
1375
+ * @param layout - Current UI layout
1376
+ * @param dataContext - Current data context
1377
+ * @returns Route resolution or null if no match
1378
+ */
1379
+ async resolveRoute(event, schema, layout, dataContext, goal, apiKey, userContext) {
1380
+ console.log(
1381
+ `[ActionRouter Debug] resolveRoute called for event type: ${event.type}, nodeId: ${event.nodeId}`
1382
+ );
1383
+ const sourceNode = layout ? findNodeById(layout, event.nodeId) : void 0;
1384
+ const nodeConfig = sourceNode?.events?.[event.type];
1385
+ let actionType;
1386
+ let determinedTargetNodeId;
1387
+ let determinedNodeConfigPayload = null;
1388
+ if (nodeConfig?.action) {
1389
+ actionType = nodeConfig.action;
1390
+ determinedTargetNodeId = nodeConfig.target || sourceNode?.id || "root";
1391
+ determinedNodeConfigPayload = nodeConfig.payload ? { ...nodeConfig.payload } : null;
1392
+ } else {
1393
+ switch (event.type) {
1394
+ case "INIT":
1395
+ actionType = "FULL_REFRESH" /* FULL_REFRESH */;
1396
+ determinedTargetNodeId = "root";
1397
+ break;
1398
+ case "CLICK":
1399
+ actionType = "FULL_REFRESH" /* FULL_REFRESH */;
1400
+ determinedTargetNodeId = "root";
1401
+ break;
1402
+ case "CHANGE":
1403
+ if (sourceNode && ["Input", "Select", "Textarea", "Checkbox", "RadioGroup"].includes(sourceNode.node_type)) {
1404
+ actionType = "UPDATE_DATA" /* UPDATE_DATA */;
1405
+ determinedTargetNodeId = sourceNode.id;
1406
+ } else {
1407
+ actionType = "FULL_REFRESH" /* FULL_REFRESH */;
1408
+ determinedTargetNodeId = "root";
1409
+ }
1410
+ break;
1411
+ default:
1412
+ actionType = "FULL_REFRESH" /* FULL_REFRESH */;
1413
+ determinedTargetNodeId = "root";
1414
+ break;
1415
+ }
1416
+ }
1417
+ if (!Object.values(ActionType).includes(actionType)) {
1418
+ throw new Error(`Invalid action type: ${actionType}`);
1419
+ }
1420
+ const additionalContext = {};
1421
+ if (sourceNode) {
1422
+ additionalContext.sourceNode = sourceNode;
1423
+ }
1424
+ const targetNode = layout ? findNodeById(layout, determinedTargetNodeId) : void 0;
1425
+ if (targetNode) {
1426
+ additionalContext.targetNode = targetNode;
1427
+ }
1428
+ let finalEventPayload = event.payload && Object.keys(event.payload).length > 0 ? { ...event.payload } : null;
1429
+ if (determinedNodeConfigPayload) {
1430
+ finalEventPayload = { ...finalEventPayload || {}, ...determinedNodeConfigPayload };
1431
+ }
1432
+ if (finalEventPayload) {
1433
+ additionalContext.eventPayload = finalEventPayload;
1434
+ }
1435
+ const plannerInput2 = {
1436
+ schema,
1437
+ goal,
1438
+ history: [event],
1439
+ userContext: { ...userContext || {}, ...additionalContext }
1440
+ };
1441
+ if (actionType === "SHOW_DETAIL" /* SHOW_DETAIL */) {
1442
+ const itemData = dataContext.tasks?.data?.find(
1443
+ (task) => task.id === (event.payload?.itemId || event.payload?.taskId)
1444
+ );
1445
+ const updatedDataContext = {
1446
+ ...dataContext,
1447
+ selectedTask: itemData,
1448
+ isTaskDetailDialogVisible: true
1449
+ };
1450
+ const layoutFromLLM = await callPlannerLLM(plannerInput2, apiKey);
1451
+ return {
1452
+ actionType: "UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
1453
+ targetNodeId: determinedTargetNodeId,
1454
+ updatedDataContext,
1455
+ updatedNode: layoutFromLLM
1456
+ };
1457
+ }
1458
+ if (actionType === "HIDE_DIALOG" /* HIDE_DIALOG */) {
1459
+ if (!layout) {
1460
+ throw new Error("Layout cannot be null when handling HIDE_DIALOG");
1461
+ }
1462
+ return {
1463
+ actionType: "UPDATE_CONTEXT" /* UPDATE_CONTEXT */,
1464
+ targetNodeId: determinedTargetNodeId,
1465
+ updatedDataContext: {
1466
+ ...dataContext,
1467
+ selectedTask: null,
1468
+ isTaskDetailDialogVisible: false
1469
+ },
1470
+ updatedNode: layout
1471
+ };
1472
+ }
1473
+ if (["UPDATE_DATA" /* UPDATE_DATA */, "ADD_ITEM" /* ADD_ITEM */, "DELETE_ITEM" /* DELETE_ITEM */, "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */].includes(actionType)) {
1474
+ if (!layout) {
1475
+ throw new Error("Layout cannot be null when handling data update actions");
1476
+ }
1477
+ const targetPathOrId = actionType === "UPDATE_DATA" /* UPDATE_DATA */ ? sourceNode?.bindings?.value || determinedTargetNodeId : determinedTargetNodeId;
1478
+ return {
1479
+ actionType,
1480
+ targetNodeId: determinedTargetNodeId,
1481
+ updatedNode: layout,
1482
+ updatedDataContext: executeAction(actionType, targetPathOrId, finalEventPayload || {}, dataContext)
1483
+ };
1484
+ }
1485
+ if ([
1486
+ "FULL_REFRESH" /* FULL_REFRESH */,
1487
+ "UPDATE_NODE" /* UPDATE_NODE */,
1488
+ "ADD_DROPDOWN" /* ADD_DROPDOWN */,
1489
+ "TOGGLE_STATE" /* TOGGLE_STATE */,
1490
+ "UPDATE_FORM" /* UPDATE_FORM */,
1491
+ "NAVIGATE" /* NAVIGATE */,
1492
+ "UPDATE_CONTEXT" /* UPDATE_CONTEXT */
1493
+ ].includes(actionType)) {
1494
+ const layoutFromLLM = await callPlannerLLM(plannerInput2, apiKey);
1495
+ return {
1496
+ actionType,
1497
+ targetNodeId: determinedTargetNodeId,
1498
+ updatedNode: layoutFromLLM,
1499
+ updatedDataContext: dataContext
1500
+ };
1501
+ }
1502
+ throw new Error(`Unhandled action type: ${actionType}`);
1503
+ }
1504
+ };
1048
1505
  function useUIStateEngine({
1049
1506
  schema,
1050
1507
  goal,
1051
- openaiApiKey,
1508
+ apiKey = "",
1052
1509
  userContext,
1053
- mockMode = false,
1054
1510
  planningConfig,
1055
- router = createDefaultRouter(),
1056
1511
  dataContext = {},
1057
- enablePartialUpdates = false
1512
+ enablePartialUpdates = true
1058
1513
  }) {
1059
1514
  if (userContext === null) {
1060
1515
  console.warn(
@@ -1062,84 +1517,68 @@ function useUIStateEngine({
1062
1517
  );
1063
1518
  }
1064
1519
  const [state, dispatch] = React.useReducer(uiReducer, initialState);
1520
+ const stateRef = React.useRef(state);
1521
+ const router = new ActionRouter(apiKey, planningConfig);
1522
+ React.useEffect(() => {
1523
+ stateRef.current = state;
1524
+ }, [state]);
1065
1525
  const handleEvent = React.useCallback(
1066
- async (event) => {
1526
+ async (event, currentResolvedLayout, updatedDataContext) => {
1067
1527
  dispatch({ type: "UI_EVENT", event });
1068
1528
  dispatch({ type: "LOADING", isLoading: true });
1069
1529
  try {
1070
1530
  let resolvedNode;
1071
1531
  let actionTypeForDispatch = "FULL_REFRESH" /* FULL_REFRESH */;
1072
1532
  let targetNodeIdForDispatch = "root";
1073
- if (enablePartialUpdates) {
1074
- const route = router.resolveRoute(
1075
- event,
1076
- schema,
1077
- state.layout,
1078
- dataContext,
1079
- goal,
1080
- userContext
1081
- );
1082
- if (route) {
1083
- console.log("Resolved route:", route);
1084
- actionTypeForDispatch = route.actionType;
1085
- targetNodeIdForDispatch = route.targetNodeId;
1086
- systemEvents.emit(
1087
- createSystemEvent("PLAN_START" /* PLAN_START */, {
1088
- plannerInput: route.plannerInput
1089
- })
1090
- );
1091
- if (mockMode) {
1092
- resolvedNode = mockPlanner(
1093
- route.plannerInput,
1094
- route.targetNodeId,
1095
- route.prompt
1096
- );
1097
- } else {
1098
- resolvedNode = await callPlannerLLM(
1099
- route.plannerInput,
1100
- openaiApiKey || "",
1101
- route
1102
- );
1103
- }
1104
- } else {
1105
- const input = {
1106
- schema,
1107
- goal,
1108
- history: [...state.history, event],
1109
- userContext
1110
- };
1111
- if (mockMode) {
1112
- resolvedNode = mockPlanner(input);
1113
- } else {
1114
- resolvedNode = await callPlannerLLM(
1115
- input,
1116
- openaiApiKey || "",
1117
- void 0
1118
- );
1119
- }
1533
+ const layoutForRouting = currentResolvedLayout || stateRef.current.layout;
1534
+ const contextForRouting = updatedDataContext || dataContext;
1535
+ console.log(
1536
+ "[state.ts handleEvent] About to call router.resolveRoute. enablePartialUpdates:",
1537
+ enablePartialUpdates
1538
+ );
1539
+ console.log(
1540
+ "[state.ts handleEvent] layoutForRouting ID (if exists):",
1541
+ layoutForRouting?.id
1542
+ );
1543
+ console.log(
1544
+ "[state.ts handleEvent] contextForRouting:",
1545
+ JSON.stringify(contextForRouting, null, 2)
1546
+ );
1547
+ const route = await router.resolveRoute(
1548
+ event,
1549
+ schema,
1550
+ layoutForRouting,
1551
+ contextForRouting,
1552
+ goal,
1553
+ apiKey,
1554
+ userContext
1555
+ );
1556
+ console.log(
1557
+ "[state.ts handleEvent] router.resolveRoute returned:",
1558
+ route
1559
+ );
1560
+ if (route) {
1561
+ console.log("Resolved route:", route);
1562
+ actionTypeForDispatch = route.actionType;
1563
+ targetNodeIdForDispatch = route.targetNodeId;
1564
+ if (route.updatedDataContext) {
1565
+ dispatch({
1566
+ type: "SET_DATA_CONTEXT",
1567
+ payload: route.updatedDataContext
1568
+ });
1120
1569
  }
1121
- } else {
1122
- const input = {
1123
- schema,
1124
- goal,
1125
- history: [...state.history, event],
1126
- // event is already in history from UI_EVENT dispatch
1127
- userContext
1128
- };
1129
- if (mockMode) {
1130
- resolvedNode = mockPlanner(input);
1131
- } else {
1132
- resolvedNode = await callPlannerLLM(
1133
- input,
1134
- openaiApiKey || "",
1135
- void 0
1570
+ if (!route.updatedNode) {
1571
+ throw new Error(
1572
+ "No updatedNode returned from router.resolveRoute. This should not happen."
1136
1573
  );
1137
1574
  }
1575
+ resolvedNode = route.updatedNode;
1576
+ } else {
1577
+ throw new Error("No route returned from router.resolveRoute");
1138
1578
  }
1139
1579
  switch (actionTypeForDispatch) {
1140
1580
  case "UPDATE_NODE" /* UPDATE_NODE */:
1141
1581
  case "SHOW_DETAIL" /* SHOW_DETAIL */:
1142
- case "HIDE_DETAIL" /* HIDE_DETAIL */:
1143
1582
  case "TOGGLE_STATE" /* TOGGLE_STATE */:
1144
1583
  case "ADD_DROPDOWN" /* ADD_DROPDOWN */:
1145
1584
  case "UPDATE_FORM" /* UPDATE_FORM */:
@@ -1150,6 +1589,16 @@ function useUIStateEngine({
1150
1589
  node: resolvedNode
1151
1590
  });
1152
1591
  break;
1592
+ case "HIDE_DIALOG" /* HIDE_DIALOG */:
1593
+ dispatch({
1594
+ type: "PARTIAL_UPDATE",
1595
+ nodeId: targetNodeIdForDispatch,
1596
+ node: resolvedNode
1597
+ });
1598
+ break;
1599
+ case "SAVE_TASK_CHANGES" /* SAVE_TASK_CHANGES */:
1600
+ dispatch({ type: "AI_RESPONSE", node: resolvedNode });
1601
+ break;
1153
1602
  case "FULL_REFRESH" /* FULL_REFRESH */:
1154
1603
  default:
1155
1604
  dispatch({ type: "AI_RESPONSE", node: resolvedNode });
@@ -1168,80 +1617,57 @@ function useUIStateEngine({
1168
1617
  }
1169
1618
  },
1170
1619
  [
1171
- // append, // REMOVE
1172
1620
  goal,
1173
1621
  schema,
1174
- state.history,
1175
- // Keep state.history if input preparation needs it
1176
- state.layout,
1177
- // stop, // REMOVE
1178
1622
  userContext,
1179
- router,
1180
- mockMode,
1181
1623
  dataContext,
1182
- openaiApiKey,
1624
+ apiKey,
1183
1625
  enablePartialUpdates,
1184
1626
  dispatch
1185
- // Add dispatch
1186
1627
  ]
1187
1628
  );
1188
1629
  React.useEffect(() => {
1189
1630
  const initialFetch = async () => {
1190
1631
  dispatch({ type: "LOADING", isLoading: true });
1191
1632
  try {
1192
- const input = {
1633
+ const initEvent = {
1634
+ type: "INIT",
1635
+ nodeId: "system",
1636
+ timestamp: Date.now(),
1637
+ payload: null
1638
+ };
1639
+ const route = await router.resolveRoute(
1640
+ initEvent,
1193
1641
  schema,
1642
+ stateRef.current.layout,
1643
+ dataContext,
1194
1644
  goal,
1195
- history: [],
1196
- // Initial history is empty
1645
+ apiKey,
1197
1646
  userContext
1198
- };
1199
- let node;
1200
- if (mockMode) {
1201
- node = mockPlanner(input);
1202
- } else {
1203
- const initEvent = {
1204
- type: "INIT",
1205
- // Assuming "INIT" is your initial event type
1206
- nodeId: "system",
1207
- // Or some other appropriate initial nodeId
1208
- timestamp: Date.now(),
1209
- payload: null
1210
- };
1211
- const route = router.resolveRoute(
1212
- initEvent,
1213
- schema,
1214
- null,
1215
- // No existing layout on initial fetch
1216
- dataContext,
1217
- goal,
1218
- userContext
1219
- );
1220
- if (!route || !route.prompt) {
1221
- console.error(
1222
- "[UIStateEngine] Initial fetch: Failed to resolve route or get prompt for INIT event."
1223
- );
1224
- throw new Error("Failed to initialize UI due to routing error.");
1225
- }
1226
- systemEvents.emit(
1227
- createSystemEvent("PLAN_START" /* PLAN_START */, {
1228
- plannerInput: route.plannerInput
1229
- })
1647
+ );
1648
+ if (!route) {
1649
+ console.error(
1650
+ "[UIStateEngine] Initial fetch: Failed to resolve route for INIT event."
1230
1651
  );
1231
- node = await callPlannerLLM(
1232
- route.plannerInput,
1233
- // Use plannerInput from the resolved route
1234
- openaiApiKey || "",
1235
- route
1236
- // Pass the entire route object
1652
+ throw new Error("Failed to initialize UI due to routing error.");
1653
+ }
1654
+ if (route.updatedDataContext) {
1655
+ dispatch({
1656
+ type: "SET_DATA_CONTEXT",
1657
+ payload: route.updatedDataContext
1658
+ });
1659
+ }
1660
+ if (!route.updatedNode) {
1661
+ throw new Error(
1662
+ "No updatedNode returned from router.resolveRoute on initial fetch. This should not happen."
1237
1663
  );
1238
1664
  }
1665
+ const node = route.updatedNode;
1239
1666
  dispatch({ type: "AI_RESPONSE", node });
1240
1667
  } catch (e) {
1241
1668
  const errorMessage = e instanceof Error ? e.message : String(e);
1242
1669
  dispatch({ type: "ERROR", message: errorMessage });
1243
1670
  systemEvents.emit(
1244
- // Also emit system event for initial load error
1245
1671
  createSystemEvent("PLAN_ERROR" /* PLAN_ERROR */, {
1246
1672
  error: e instanceof Error ? e : new Error(String(e))
1247
1673
  })
@@ -1251,7 +1677,7 @@ function useUIStateEngine({
1251
1677
  }
1252
1678
  };
1253
1679
  initialFetch();
1254
- }, [goal, schema, userContext, mockMode, dispatch, openaiApiKey]);
1680
+ }, [goal, schema, dispatch]);
1255
1681
  return {
1256
1682
  state,
1257
1683
  dispatch,
@@ -1734,45 +2160,67 @@ var Button = ({ onClick, children, variant = "default" }) => /* @__PURE__ */ jsx
1734
2160
  }
1735
2161
  );
1736
2162
  var Detail = ({ data, fields = [], title, visible = true, onBack }) => {
1737
- if (!visible)
2163
+ if (!visible) {
2164
+ console.log(
2165
+ `[Detail Component Internal] Detail component for node id: ${data?.id || title || "Unknown Detail"} is NOT RENDERING because visible is ${visible}.`
2166
+ );
1738
2167
  return null;
2168
+ }
2169
+ console.log(
2170
+ `[Detail Component Internal] Detail component for node id: ${data?.id || title || "Unknown Detail"} IS RENDERING because visible is ${visible}.`
2171
+ );
1739
2172
  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: [
1740
2173
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center border-b border-gray-200 dark:border-gray-700 pb-3", children: [
1741
2174
  title && /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-medium text-gray-800 dark:text-white", children: title }),
1742
2175
  onBack && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", onClick: onBack, children: "Back" })
1743
2176
  ] }),
1744
2177
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: fields.map((field) => {
1745
- if (field.type === "heading") {
1746
- return /* @__PURE__ */ jsxRuntime.jsx(
1747
- "h3",
1748
- {
1749
- className: "text-xl font-semibold text-gray-800 dark:text-white",
1750
- children: data?.[field.key] ?? ""
1751
- },
1752
- field.key
1753
- );
1754
- }
1755
- if (field.type === "content") {
1756
- return /* @__PURE__ */ jsxRuntime.jsx(
2178
+ try {
2179
+ if (field.type === "heading") {
2180
+ return /* @__PURE__ */ jsxRuntime.jsx(
2181
+ "h3",
2182
+ {
2183
+ className: "text-xl font-semibold text-gray-800 dark:text-white",
2184
+ children: data?.[field.key] ?? ""
2185
+ },
2186
+ field.key
2187
+ );
2188
+ }
2189
+ if (field.type === "content") {
2190
+ return /* @__PURE__ */ jsxRuntime.jsx(
2191
+ "div",
2192
+ {
2193
+ className: "text-sm text-gray-700 dark:text-gray-300",
2194
+ children: data?.[field.key] ?? ""
2195
+ },
2196
+ field.key
2197
+ );
2198
+ }
2199
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1757
2200
  "div",
1758
2201
  {
1759
- className: "text-sm text-gray-700 dark:text-gray-300",
1760
- children: data?.[field.key] ?? ""
2202
+ className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
2203
+ children: [
2204
+ field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
2205
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
2206
+ ]
1761
2207
  },
1762
2208
  field.key
1763
2209
  );
2210
+ } catch (e) {
2211
+ console.error(
2212
+ `[Detail Component Internal] Error rendering field: ${field?.key}`,
2213
+ e,
2214
+ "Field data:",
2215
+ field,
2216
+ "Full data object:",
2217
+ data
2218
+ );
2219
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2220
+ "Error rendering field: ",
2221
+ field?.key
2222
+ ] }, field?.key || "error-field");
1764
2223
  }
1765
- return /* @__PURE__ */ jsxRuntime.jsxs(
1766
- "div",
1767
- {
1768
- className: "flex flex-col border-b border-gray-100 dark:border-gray-800 py-2",
1769
- children: [
1770
- field.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium", children: field.label }),
1771
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-800 dark:text-gray-200", children: data?.[field.key] ?? "" })
1772
- ]
1773
- },
1774
- field.key
1775
- );
1776
2224
  }) })
1777
2225
  ] });
1778
2226
  };
@@ -1813,9 +2261,9 @@ var isUISpecNode = (value) => {
1813
2261
  return isString(value.id) && isString(value.node_type);
1814
2262
  };
1815
2263
  var isDetailFieldObject = (item) => isObject(item) && isString(item.key) && isString(item.label) && (item.type === void 0 || isString(item.type));
1816
- var createEventHandler = (node, eventName, uiEventType2, processEvent2) => {
2264
+ var createEventHandler = (node, eventName, uiEventType2, processEvent) => {
1817
2265
  const eventConfig = node.events?.[uiEventType2];
1818
- if (!processEvent2 || !eventConfig)
2266
+ if (!processEvent || !eventConfig)
1819
2267
  return void 0;
1820
2268
  return (eventPayload) => {
1821
2269
  const fullEvent = {
@@ -1827,16 +2275,16 @@ var createEventHandler = (node, eventName, uiEventType2, processEvent2) => {
1827
2275
  ...eventPayload || {}
1828
2276
  }
1829
2277
  };
1830
- processEvent2(fullEvent);
2278
+ processEvent(fullEvent);
1831
2279
  };
1832
2280
  };
1833
2281
  var adapterMap = {
1834
- Container: (node, processEvent2) => {
2282
+ Container: (node, processEvent) => {
1835
2283
  const { className, style: styleProp, key, ...restProps } = node.props || {};
1836
2284
  const children = node.children?.map(
1837
2285
  (child) => (
1838
2286
  // Use React.cloneElement to add the key prop to the element returned by renderNode
1839
- React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
2287
+ React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
1840
2288
  )
1841
2289
  );
1842
2290
  const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
@@ -1868,23 +2316,31 @@ var adapterMap = {
1868
2316
  className: getSafeProp(node.props, "className", isString, "")
1869
2317
  }
1870
2318
  ),
1871
- Button: (node, processEvent2) => /* @__PURE__ */ jsxRuntime.jsx(
2319
+ Button: (node, processEvent) => /* @__PURE__ */ jsxRuntime.jsx(
1872
2320
  Button,
1873
2321
  {
1874
2322
  variant: getSafeProp(node.props, "variant", isButtonVariant, "default"),
1875
- onClick: createEventHandler(node, "onClick", "CLICK", processEvent2),
2323
+ onClick: createEventHandler(node, "onClick", "CLICK", processEvent),
1876
2324
  children: getSafeProp(node.props, "label", isString, "Button")
1877
2325
  }
1878
2326
  ),
1879
- ListView: (node, processEvent2) => {
1880
- const { className, style: styleProp, key, ...restProps } = node.props || {};
2327
+ ListView: (node, processEvent) => {
2328
+ const {
2329
+ className,
2330
+ style: styleProp,
2331
+ key,
2332
+ // Exclude data prop (array) from being spread onto the DOM element
2333
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2334
+ data: _data,
2335
+ ...restProps
2336
+ } = node.props || {};
1881
2337
  const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
1882
2338
  console.log(
1883
2339
  `[Adapter Debug] Rendering ListView: id=${node.id}, props=`,
1884
2340
  node.props
1885
2341
  );
1886
2342
  const children = node.children?.map(
1887
- (child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
2343
+ (child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
1888
2344
  );
1889
2345
  return /* @__PURE__ */ jsxRuntime.jsx(
1890
2346
  "div",
@@ -1901,21 +2357,28 @@ var adapterMap = {
1901
2357
  key
1902
2358
  );
1903
2359
  },
1904
- Detail: (node, processEvent2) => {
1905
- const data = getSafeBinding(
1906
- node.bindings,
2360
+ Detail: (node, processEvent) => {
2361
+ const data = getSafeProp(
2362
+ node.props,
1907
2363
  "data",
1908
2364
  isRecordWithReactNodeValues,
1909
2365
  {}
1910
2366
  );
1911
- const fields = getSafeBinding(
1912
- node.bindings,
2367
+ const fields = getSafeProp(
2368
+ node.props,
1913
2369
  "fields",
1914
2370
  isArrayOf(isDetailFieldObject),
1915
2371
  []
1916
2372
  );
1917
2373
  const title = getSafeProp(node.props, "title", isString, "");
1918
2374
  const visible = getSafeProp(node.props, "visible", isBoolean, true);
2375
+ console.log(
2376
+ `[Adapter Debug] Rendering Detail: id=${node.id}, props=`,
2377
+ JSON.stringify(node.props),
2378
+ `effective visible=${visible}`,
2379
+ `typeof fields=${typeof fields}`,
2380
+ `Array.isArray(fields)=${Array.isArray(fields)}`
2381
+ );
1919
2382
  return /* @__PURE__ */ jsxRuntime.jsx(
1920
2383
  Detail,
1921
2384
  {
@@ -1923,14 +2386,14 @@ var adapterMap = {
1923
2386
  fields,
1924
2387
  title,
1925
2388
  visible,
1926
- onBack: createEventHandler(node, "onBack", "CLICK", processEvent2)
2389
+ onBack: createEventHandler(node, "onBack", "CLICK", processEvent)
1927
2390
  }
1928
2391
  );
1929
2392
  },
1930
- Card: (node, processEvent2) => {
2393
+ Card: (node, processEvent) => {
1931
2394
  const { className, style: styleProp, key, ...restProps } = node.props || {};
1932
2395
  const children = node.children?.map(
1933
- (child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
2396
+ (child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
1934
2397
  );
1935
2398
  const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
1936
2399
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1948,7 +2411,7 @@ var adapterMap = {
1948
2411
  key
1949
2412
  );
1950
2413
  },
1951
- Input: (node, processEvent2) => {
2414
+ Input: (node, processEvent) => {
1952
2415
  const name = getSafeProp(node.props, "name", isString, "inputName");
1953
2416
  const label = getSafeProp(node.props, "label", isString, "");
1954
2417
  const value = getSafeBinding(node.bindings, "value", isString, "");
@@ -1960,7 +2423,7 @@ var adapterMap = {
1960
2423
  node,
1961
2424
  "onChange",
1962
2425
  "CHANGE",
1963
- processEvent2
2426
+ processEvent
1964
2427
  );
1965
2428
  if (handler)
1966
2429
  handler({ value: e.target.value });
@@ -1970,13 +2433,13 @@ var adapterMap = {
1970
2433
  node,
1971
2434
  "onFocus",
1972
2435
  "FOCUS",
1973
- processEvent2
2436
+ processEvent
1974
2437
  );
1975
2438
  if (handler)
1976
2439
  handler({});
1977
2440
  };
1978
2441
  const handleBlur = () => {
1979
- const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
2442
+ const handler = createEventHandler(node, "onBlur", "BLUR", processEvent);
1980
2443
  if (handler)
1981
2444
  handler({});
1982
2445
  };
@@ -1998,7 +2461,7 @@ var adapterMap = {
1998
2461
  )
1999
2462
  ] });
2000
2463
  },
2001
- Select: (node, processEvent2) => {
2464
+ Select: (node, processEvent) => {
2002
2465
  const name = getSafeProp(node.props, "name", isString, "selectName");
2003
2466
  const label = getSafeProp(node.props, "label", isString, "");
2004
2467
  const placeholder = getSafeProp(
@@ -2021,7 +2484,7 @@ var adapterMap = {
2021
2484
  node,
2022
2485
  "onValueChange",
2023
2486
  "CHANGE",
2024
- processEvent2
2487
+ processEvent
2025
2488
  );
2026
2489
  if (handler)
2027
2490
  handler({ value: selectedValue });
@@ -2049,7 +2512,7 @@ var adapterMap = {
2049
2512
  }
2050
2513
  );
2051
2514
  },
2052
- Textarea: (node, processEvent2) => {
2515
+ Textarea: (node, processEvent) => {
2053
2516
  const { key, ...propsWithoutKey } = node.props || {};
2054
2517
  const name = getSafeProp(propsWithoutKey, "name", isString, "textareaName");
2055
2518
  const label = getSafeProp(propsWithoutKey, "label", isString, "");
@@ -2073,7 +2536,7 @@ var adapterMap = {
2073
2536
  node,
2074
2537
  "onChange",
2075
2538
  "CHANGE",
2076
- processEvent2
2539
+ processEvent
2077
2540
  );
2078
2541
  if (handler)
2079
2542
  handler({ value: e.target.value });
@@ -2083,13 +2546,13 @@ var adapterMap = {
2083
2546
  node,
2084
2547
  "onFocus",
2085
2548
  "FOCUS",
2086
- processEvent2
2549
+ processEvent
2087
2550
  );
2088
2551
  if (handler)
2089
2552
  handler({});
2090
2553
  };
2091
2554
  const handleBlur = () => {
2092
- const handler = createEventHandler(node, "onBlur", "BLUR", processEvent2);
2555
+ const handler = createEventHandler(node, "onBlur", "BLUR", processEvent);
2093
2556
  if (handler)
2094
2557
  handler({});
2095
2558
  };
@@ -2112,7 +2575,7 @@ var adapterMap = {
2112
2575
  )
2113
2576
  ] }, key);
2114
2577
  },
2115
- Checkbox: (node, processEvent2) => {
2578
+ Checkbox: (node, processEvent) => {
2116
2579
  const { key, ...propsWithoutKey } = node.props || {};
2117
2580
  const name = getSafeProp(propsWithoutKey, "name", isString, "checkboxName");
2118
2581
  const label = getSafeProp(propsWithoutKey, "label", isString, "");
@@ -2125,7 +2588,7 @@ var adapterMap = {
2125
2588
  node,
2126
2589
  "onCheckedChange",
2127
2590
  "CHANGE",
2128
- processEvent2
2591
+ processEvent
2129
2592
  );
2130
2593
  if (handler)
2131
2594
  handler({ checked: isChecked });
@@ -2152,7 +2615,7 @@ var adapterMap = {
2152
2615
  key
2153
2616
  );
2154
2617
  },
2155
- RadioGroup: (node, processEvent2) => {
2618
+ RadioGroup: (node, processEvent) => {
2156
2619
  const { key, ...propsWithoutKey } = node.props || {};
2157
2620
  const name = getSafeProp(
2158
2621
  propsWithoutKey,
@@ -2175,7 +2638,7 @@ var adapterMap = {
2175
2638
  node,
2176
2639
  "onValueChange",
2177
2640
  "CHANGE",
2178
- processEvent2
2641
+ processEvent
2179
2642
  );
2180
2643
  if (handler)
2181
2644
  handler({ value: selectedValue });
@@ -2218,7 +2681,7 @@ var adapterMap = {
2218
2681
  key
2219
2682
  );
2220
2683
  },
2221
- Tabs: (node, processEvent2) => {
2684
+ Tabs: (node, processEvent) => {
2222
2685
  const { key, ...propsWithoutKey } = node.props || {};
2223
2686
  const rawTabs = getSafeBinding(
2224
2687
  node.bindings,
@@ -2238,7 +2701,7 @@ var adapterMap = {
2238
2701
  node,
2239
2702
  "onValueChange",
2240
2703
  "CHANGE",
2241
- processEvent2
2704
+ processEvent
2242
2705
  );
2243
2706
  if (handler)
2244
2707
  handler({ value });
@@ -2252,18 +2715,26 @@ var adapterMap = {
2252
2715
  "data-id": node.id,
2253
2716
  children: [
2254
2717
  /* @__PURE__ */ jsxRuntime.jsx(TabsList, { children: rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsTrigger, { value: tab.value, children: tab.label }, tab.value)) }),
2255
- rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: tab.value, children: tab.content ? renderNode(tab.content, processEvent2) : null }, tab.value))
2718
+ rawTabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(TabsContent, { value: tab.value, children: tab.content ? renderNode(tab.content, processEvent) : null }, tab.value))
2256
2719
  ]
2257
2720
  },
2258
2721
  key
2259
2722
  );
2260
2723
  },
2261
- Dialog: (node, processEvent2) => {
2724
+ Dialog: (node, processEvent) => {
2262
2725
  const isOpen = getSafeBinding(
2263
2726
  node.bindings,
2264
- "open",
2727
+ "visible",
2265
2728
  isBoolean,
2266
- getSafeProp(node.props, "open", isBoolean, false)
2729
+ // Check bindings.visible (planner output)
2730
+ getSafeProp(
2731
+ node.props,
2732
+ "open",
2733
+ isBoolean,
2734
+ // Then props.open (if binding resolution put it there)
2735
+ getSafeProp(node.props, "visible", isBoolean, false)
2736
+ // Then props.visible (if binding resolution put it there under 'visible')
2737
+ )
2267
2738
  );
2268
2739
  const {
2269
2740
  title,
@@ -2274,10 +2745,13 @@ var adapterMap = {
2274
2745
  key,
2275
2746
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2276
2747
  open: _openProp,
2748
+ // ADD 'visible' to destructuring to prevent it from going into restProps
2749
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2750
+ visible: _visibleProp,
2277
2751
  ...restProps
2278
2752
  } = node.props || {};
2279
2753
  const children = node.children?.map(
2280
- (child) => React__default.default.cloneElement(renderNode(child, processEvent2), { key: child.id })
2754
+ (child) => React__default.default.cloneElement(renderNode(child, processEvent), { key: child.id })
2281
2755
  );
2282
2756
  const handleOpenChange = (open) => {
2283
2757
  if (!open) {
@@ -2287,7 +2761,7 @@ var adapterMap = {
2287
2761
  // Assumed event name in UISpec
2288
2762
  "CLICK",
2289
2763
  // Use CLICK as the event type for closing dialogs
2290
- processEvent2
2764
+ processEvent
2291
2765
  );
2292
2766
  if (handler) {
2293
2767
  handler({});
@@ -2369,12 +2843,30 @@ var adapterMap = {
2369
2843
  },
2370
2844
  key
2371
2845
  );
2846
+ },
2847
+ Badge: (node) => {
2848
+ const { className, style: styleProp, key, ...restProps } = node.props || {};
2849
+ const text = getSafeProp(node.props, "text", isString, "");
2850
+ const style = typeof styleProp === "string" ? parseStyleString(styleProp) : styleProp;
2851
+ return /* @__PURE__ */ jsxRuntime.jsx(
2852
+ "span",
2853
+ {
2854
+ className: cn(
2855
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium bg-gray-100 text-gray-800",
2856
+ className
2857
+ ),
2858
+ style,
2859
+ ...restProps,
2860
+ children: text
2861
+ },
2862
+ key
2863
+ );
2372
2864
  }
2373
2865
  };
2374
- function renderNode(node, processEvent2) {
2866
+ function renderNode(node, processEvent) {
2375
2867
  const mappedComponent = adapterMap[node.node_type];
2376
2868
  if (mappedComponent) {
2377
- return mappedComponent(node, processEvent2);
2869
+ return mappedComponent(node, processEvent);
2378
2870
  }
2379
2871
  console.warn(`Unknown node type: ${node.node_type}`);
2380
2872
  return React__default.default.createElement(
@@ -2386,370 +2878,141 @@ function renderNode(node, processEvent2) {
2386
2878
  var renderedNodesCache = /* @__PURE__ */ new Map();
2387
2879
  var MAX_CACHE_SIZE = 10;
2388
2880
  var CACHE_TTL = 5e3;
2389
- async function renderNode2(node, adapter = "shadcn", processEvent2) {
2390
- const startTime = Date.now();
2391
- const nodeId = node.id;
2392
- const cachedItem = renderedNodesCache.get(nodeId);
2393
- if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
2394
- return cachedItem.element;
2395
- }
2396
- await systemEvents.emit(
2397
- createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
2398
- );
2399
- let result;
2400
- switch (adapter) {
2401
- case "shadcn":
2402
- result = renderNode(node, processEvent2);
2403
- break;
2404
- default:
2405
- console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
2406
- result = renderNode(node, processEvent2);
2407
- }
2408
- await systemEvents.emit(
2409
- createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
2410
- layout: node,
2411
- renderTimeMs: Date.now() - startTime
2412
- })
2413
- );
2414
- renderedNodesCache.set(nodeId, {
2415
- element: result,
2416
- timestamp: startTime
2417
- });
2418
- if (renderedNodesCache.size > MAX_CACHE_SIZE) {
2419
- const oldestKey = [...renderedNodesCache.entries()].sort(
2420
- ([, a], [, b]) => a.timestamp - b.timestamp
2421
- )[0][0];
2422
- renderedNodesCache.delete(oldestKey);
2423
- }
2424
- return result;
2425
- }
2426
- function renderShimmer(node, adapter = "shadcn") {
2427
- if (!node) {
2428
- return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
2429
- }
2430
- switch (node.node_type) {
2431
- case "ListView":
2432
- return /* @__PURE__ */ jsxRuntime.jsx(ShimmerTable, { rows: 3 });
2433
- case "Detail":
2434
- return /* @__PURE__ */ jsxRuntime.jsx(ShimmerCard, {});
2435
- case "Container":
2436
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: node.children?.map((child, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderShimmer(child, adapter) }, index)) });
2437
- default:
2438
- return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
2439
- }
2440
- }
2441
-
2442
- // src/core/bindings.ts
2443
- var bindingsCache = /* @__PURE__ */ new Map();
2444
- var MAX_CACHE_SIZE2 = 50;
2445
- var CACHE_TTL2 = 2e3;
2446
- var nodeCacheTimestamps = /* @__PURE__ */ new Map();
2447
- function hashDataContext(context) {
2448
- return JSON.stringify(context);
2449
- }
2450
- function createCacheKey(nodeId, context) {
2451
- return `${nodeId}:${hashDataContext(context)}`;
2452
- }
2453
- function getValueByPath(context, path) {
2454
- const parts = path.split(".");
2455
- let current = context;
2456
- for (const part of parts) {
2457
- if (current === null || current === void 0) {
2458
- return void 0;
2459
- }
2460
- if (typeof current !== "object") {
2461
- return void 0;
2462
- }
2463
- current = current[part];
2464
- }
2465
- return current;
2466
- }
2467
- function setValueByPath(context, path, value) {
2468
- if (!path) {
2469
- return context;
2470
- }
2471
- const result = { ...context };
2472
- const parts = path.split(".");
2473
- let current = result;
2474
- for (let i = 0; i < parts.length - 1; i++) {
2475
- const part = parts[i];
2476
- if (typeof current !== "object" || current === null) {
2477
- console.warn(
2478
- `setValueByPath: Cannot traverse path "${path}". Parent segment "${parts[i - 1] || "(root)"}" is not an object.`
2479
- );
2480
- return context;
2481
- }
2482
- const currentAsObject = current;
2483
- const nextPartValue = currentAsObject[part];
2484
- if (nextPartValue === void 0 || nextPartValue === null) {
2485
- currentAsObject[part] = {};
2486
- } else if (typeof nextPartValue !== "object") {
2487
- console.warn(
2488
- `setValueByPath: Cannot create nested path "${path}". Segment "${part}" is not an object.`
2489
- );
2490
- return context;
2491
- } else {
2492
- currentAsObject[part] = { ...nextPartValue };
2493
- }
2494
- current = currentAsObject[part];
2495
- }
2496
- const lastPart = parts[parts.length - 1];
2497
- if (typeof current === "object" && current !== null) {
2498
- current[lastPart] = value;
2499
- } else {
2500
- console.warn(
2501
- `setValueByPath: Could not set value for path "${path}". Final segment parent is not an object.`
2502
- );
2503
- return context;
2504
- }
2505
- return result;
2506
- }
2507
- function processBinding(binding, context, itemData) {
2508
- if (typeof binding === "string") {
2509
- const exactMatchArr = binding.match(/^{{(.*)}}$/);
2510
- const pathInsideExact = exactMatchArr ? exactMatchArr[1].trim() : null;
2511
- if (pathInsideExact !== null && !pathInsideExact.includes("{{") && !pathInsideExact.includes("}}")) {
2512
- const pathToResolve = pathInsideExact;
2513
- let resolvedValue = void 0;
2514
- console.log(
2515
- `[processBinding Debug] Processing EXACT template: "${binding}", Path: "${pathToResolve}", Has itemData: ${!!itemData}`
2516
- );
2517
- if (itemData) {
2518
- try {
2519
- console.log(
2520
- `[processBinding Debug] itemData content (EXACT):`,
2521
- JSON.parse(JSON.stringify(itemData))
2522
- );
2523
- } catch {
2524
- }
2525
- }
2526
- if ((pathToResolve.startsWith("item.") || pathToResolve.startsWith("row.")) && itemData) {
2527
- if (pathToResolve.startsWith("item.")) {
2528
- resolvedValue = getValueByPath(itemData, pathToResolve.substring(5));
2529
- } else {
2530
- resolvedValue = getValueByPath(itemData, pathToResolve.substring(4));
2531
- }
2532
- } else if (itemData && pathToResolve in itemData) {
2533
- resolvedValue = getValueByPath(itemData, pathToResolve);
2534
- }
2535
- if (resolvedValue === void 0) {
2536
- resolvedValue = getValueByPath(context, pathToResolve);
2537
- }
2538
- return resolvedValue;
2539
- } else if (binding.includes("{{") && binding.includes("}}")) {
2540
- console.log(
2541
- `[processBinding Debug] Processing EMBEDDED templates: "${binding}", Has itemData: ${!!itemData}`
2542
- );
2543
- if (itemData) {
2544
- try {
2545
- console.log(
2546
- `[processBinding Debug] itemData content (EMBEDDED):`,
2547
- JSON.parse(JSON.stringify(itemData))
2548
- );
2549
- } catch {
2550
- }
2551
- }
2552
- const resolvedString = binding.replaceAll(
2553
- /{{(.*?)}}/g,
2554
- // Non-greedy match inside braces
2555
- (match, path) => {
2556
- const trimmedPath = path.trim();
2557
- let resolvedValue = void 0;
2558
- if ((trimmedPath.startsWith("item.") || trimmedPath.startsWith("row.")) && itemData) {
2559
- if (trimmedPath.startsWith("item.")) {
2560
- resolvedValue = getValueByPath(
2561
- itemData,
2562
- trimmedPath.substring(5)
2563
- );
2564
- } else {
2565
- resolvedValue = getValueByPath(
2566
- itemData,
2567
- trimmedPath.substring(4)
2568
- );
2569
- }
2570
- } else if (itemData && trimmedPath in itemData) {
2571
- resolvedValue = getValueByPath(itemData, trimmedPath);
2572
- }
2573
- if (resolvedValue === void 0) {
2574
- resolvedValue = getValueByPath(context, trimmedPath);
2575
- }
2576
- return resolvedValue === null || resolvedValue === void 0 ? "" : String(resolvedValue);
2577
- }
2578
- );
2579
- return resolvedString;
2580
- } else {
2581
- const pathToResolve = binding;
2582
- let resolvedValue = void 0;
2583
- console.log(
2584
- `[processBinding Debug] Processing PATH string: "${pathToResolve}", Has itemData: ${!!itemData}`
2585
- );
2586
- if (itemData && !pathToResolve.includes(".") && pathToResolve in itemData) {
2587
- resolvedValue = getValueByPath(itemData, pathToResolve);
2588
- }
2589
- if (resolvedValue === void 0) {
2590
- resolvedValue = getValueByPath(context, pathToResolve);
2591
- }
2592
- return resolvedValue;
2593
- }
2594
- }
2595
- if (Array.isArray(binding)) {
2596
- return binding.map((item) => processBinding(item, context, itemData));
2597
- }
2598
- if (binding !== null && typeof binding === "object") {
2599
- const result = {};
2600
- for (const [key, value] of Object.entries(binding)) {
2601
- result[key] = processBinding(value, context, itemData);
2602
- }
2603
- return result;
2604
- }
2605
- return binding;
2606
- }
2607
- async function resolveBindings(node, context, itemData) {
2608
- const effectiveContext = itemData ? { ...context, item: itemData } : context;
2609
- const currentTime = Date.now();
2610
- const cacheKey = createCacheKey(node.id, effectiveContext);
2611
- const cachedNode = bindingsCache.get(cacheKey);
2612
- const cachedTimestamp = nodeCacheTimestamps.get(cacheKey);
2613
- if (cachedNode && cachedTimestamp && currentTime - cachedTimestamp < CACHE_TTL2) {
2614
- return cachedNode;
2615
- }
2616
- if (!itemData) {
2617
- await systemEvents.emit(
2618
- createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
2619
- layout: node
2620
- })
2621
- );
2622
- }
2623
- const result = {
2624
- ...node,
2625
- props: node.props ? JSON.parse(JSON.stringify(node.props)) : null,
2626
- events: node.events ? JSON.parse(JSON.stringify(node.events)) : null,
2627
- bindings: node.bindings ? JSON.parse(JSON.stringify(node.bindings)) : null,
2628
- children: null
2629
- // Initialize children to null
2630
- };
2631
- const resolvedBindings = {};
2632
- if (node.bindings) {
2633
- for (const [key, bindingValue] of Object.entries(node.bindings)) {
2634
- const resolvedValue = processBinding(bindingValue, context, itemData);
2635
- resolvedBindings[key] = resolvedValue;
2636
- if (resolvedValue !== void 0) {
2637
- if (!result.props)
2638
- result.props = {};
2639
- result.props[key] = resolvedValue;
2640
- }
2641
- }
2642
- }
2643
- result.bindings = null;
2644
- if (node.events) {
2645
- result.events = processBinding(
2646
- node.events,
2647
- context,
2648
- itemData
2649
- );
2650
- } else {
2651
- result.events = null;
2881
+ var clearRenderedNodeCacheEntry = (cacheKey) => {
2882
+ renderedNodesCache.delete(cacheKey);
2883
+ console.log(`[Renderer Cache] Cleared entry FOR REAL for key: ${cacheKey}`);
2884
+ };
2885
+ var getRendererCacheKey = (node) => {
2886
+ if (node.id === "task-detail") {
2887
+ const dataId = node.props?.data?.id;
2888
+ return `${node.id}:${node.props?.visible}:${dataId || "no-data-selected"}`;
2652
2889
  }
2653
- const dataBindingValue = resolvedBindings["data"] ?? resolvedBindings["items"];
2654
- if ((node.node_type === "ListView" || node.node_type === "Table") && Array.isArray(dataBindingValue) && node.children && node.children.length > 0) {
2655
- const templateChild = node.children[0];
2656
- const mappedChildren = await Promise.all(
2657
- dataBindingValue.map(async (currentItemData, index) => {
2658
- try {
2659
- if (typeof currentItemData !== "object" || currentItemData === null) {
2660
- console.warn(
2661
- `List item at index ${index} for node ${node.id} is not an object:`,
2662
- currentItemData
2663
- );
2664
- return null;
2665
- }
2666
- const currentItemAsRecord = currentItemData;
2667
- const itemId = currentItemAsRecord.id;
2668
- const instanceId = `${templateChild.id}-${itemId || index}`;
2669
- const childNodeInstance = JSON.parse(
2670
- JSON.stringify(templateChild)
2671
- );
2672
- childNodeInstance.id = instanceId;
2673
- const resolvedChild = await resolveBindings(
2674
- childNodeInstance,
2675
- context,
2676
- currentItemAsRecord
2677
- );
2678
- if (!resolvedChild.props)
2679
- resolvedChild.props = {};
2680
- resolvedChild.props.key = itemId || `${node.id}-item-${index}`;
2681
- return resolvedChild;
2682
- } catch (error) {
2683
- console.error(
2684
- `[resolveBindings Error] Error processing item at index ${index} for node ${node.id}:`,
2685
- error,
2686
- "Item Data:",
2687
- currentItemData
2688
- );
2689
- return null;
2690
- }
2691
- })
2692
- );
2693
- result.children = mappedChildren.filter(
2694
- (child) => child !== null
2695
- );
2696
- } else if (node.children && node.children.length > 0) {
2697
- result.children = await Promise.all(
2698
- node.children.map((child) => resolveBindings(child, context, itemData))
2890
+ let propsString = "null";
2891
+ try {
2892
+ propsString = JSON.stringify(node.props);
2893
+ } catch (_e) {
2894
+ console.warn(
2895
+ `[Renderer Cache Key] Error stringifying node.props for ID ${node.id}, using 'null' for props part of key.`
2699
2896
  );
2700
- } else {
2701
- result.children = [];
2702
2897
  }
2703
- if (!itemData) {
2704
- await systemEvents.emit(
2705
- createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
2706
- originalLayout: node,
2707
- resolvedLayout: result
2708
- })
2898
+ let bindingsString = "null";
2899
+ try {
2900
+ bindingsString = JSON.stringify(node.bindings);
2901
+ } catch (_e) {
2902
+ console.warn(
2903
+ `[Renderer Cache Key] Error stringifying node.bindings for ID ${node.id}, using 'null' for bindings part of key.`
2709
2904
  );
2710
2905
  }
2711
- bindingsCache.set(cacheKey, result);
2712
- nodeCacheTimestamps.set(cacheKey, currentTime);
2713
- if (bindingsCache.size > MAX_CACHE_SIZE2) {
2714
- let oldestKey = null;
2715
- let oldestTimestamp = currentTime;
2716
- for (const [key, timestamp] of nodeCacheTimestamps.entries()) {
2717
- if (timestamp < oldestTimestamp) {
2718
- oldestTimestamp = timestamp;
2719
- oldestKey = key;
2720
- }
2721
- }
2722
- if (oldestKey) {
2723
- bindingsCache.delete(oldestKey);
2724
- nodeCacheTimestamps.delete(oldestKey);
2725
- }
2906
+ return `${node.id}:${propsString}:${bindingsString}`;
2907
+ };
2908
+ async function renderNode2(node, adapter = "shadcn", processEvent) {
2909
+ const startTime = Date.now();
2910
+ const cacheKey = getRendererCacheKey(node);
2911
+ const cachedItem = renderedNodesCache.get(cacheKey);
2912
+ if (cachedItem && startTime - cachedItem.timestamp < CACHE_TTL) {
2913
+ return cachedItem.element;
2726
2914
  }
2727
- return result;
2728
- }
2729
- function executeAction(action, targetId, payload, context = {}, layoutTree) {
2730
- let newContext = { ...context };
2731
- switch (action) {
2732
- case "VIEW_DETAIL": {
2733
- if (payload?.item) {
2734
- newContext = setValueByPath(newContext, "selected", payload.item);
2915
+ if (node.id === "task-detail") {
2916
+ let safeNodeString = "Error stringifying node for log";
2917
+ let propsToLog = "{}";
2918
+ try {
2919
+ const clonedProps = node.props ? { ...node.props } : {};
2920
+ if (clonedProps && clonedProps.data) {
2921
+ clonedProps.data = `Data present (type: ${typeof clonedProps.data}, logging suppressed)`;
2735
2922
  }
2736
- break;
2923
+ if (clonedProps && Array.isArray(clonedProps.fields)) {
2924
+ clonedProps.fields = `Fields array (length: ${clonedProps.fields.length}, logging suppressed)`;
2925
+ } else if (clonedProps && clonedProps.fields) {
2926
+ clonedProps.fields = `Fields present (type: ${typeof clonedProps.fields}, logging suppressed)`;
2927
+ }
2928
+ propsToLog = JSON.stringify(clonedProps);
2929
+ const nodeToLog = {
2930
+ id: node.id,
2931
+ node_type: node.node_type,
2932
+ props: "See props above",
2933
+ bindings: node.bindings,
2934
+ events: node.events,
2935
+ children: node.children ? `Children array (length: ${node.children.length}, logging suppressed)` : null
2936
+ };
2937
+ safeNodeString = JSON.stringify(nodeToLog);
2938
+ } catch (e) {
2939
+ if (e instanceof Error) {
2940
+ safeNodeString = `Error stringifying node for log: ${e.message}`;
2941
+ } else {
2942
+ safeNodeString = `Error stringifying node for log: Unknown error`;
2943
+ }
2944
+ if (node.props === void 0)
2945
+ propsToLog = "undefined";
2946
+ else if (node.props === null)
2947
+ propsToLog = "null";
2737
2948
  }
2738
- case "HIDE_DETAIL": {
2739
- newContext = setValueByPath(newContext, "selected", null);
2949
+ console.log(
2950
+ `[Renderer renderNode BEFORE RENDER_START event] About to call adapter for task-detail. ID: ${node.id}, Visible: ${node.props?.visible}, Props (safe): ${propsToLog}, Bindings: ${JSON.stringify(
2951
+ node.bindings
2952
+ )}, Node (safe): ${safeNodeString}`
2953
+ );
2954
+ }
2955
+ await systemEvents.emit(
2956
+ createSystemEvent("RENDER_START" /* RENDER_START */, { layout: node })
2957
+ );
2958
+ let result;
2959
+ switch (adapter) {
2960
+ case "shadcn":
2961
+ result = renderNode(node, processEvent);
2740
2962
  break;
2741
- }
2742
- case "SET_VALUE": {
2743
- if (payload?.path && "value" in payload) {
2744
- const path = String(payload.path);
2745
- newContext = setValueByPath(newContext, path, payload.value);
2963
+ default:
2964
+ console.warn(`Unsupported adapter: ${adapter}, falling back to shadcn`);
2965
+ result = renderNode(node, processEvent);
2966
+ }
2967
+ if (node.id === "task-detail") {
2968
+ let elementType = void 0;
2969
+ if (result && typeof result.type === "function") {
2970
+ elementType = result.type.name;
2971
+ } else if (result && typeof result.type === "string") {
2972
+ elementType = result.type;
2973
+ } else if (result && typeof result.type === "object" && result.type !== null) {
2974
+ const componentType2 = result.type;
2975
+ if (typeof componentType2.displayName === "string") {
2976
+ elementType = componentType2.displayName;
2746
2977
  }
2747
- break;
2748
2978
  }
2979
+ console.log(
2980
+ `[Renderer renderNode] Adapter for task-detail returned element. Element type:`,
2981
+ elementType
2982
+ );
2983
+ }
2984
+ await systemEvents.emit(
2985
+ createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
2986
+ layout: node,
2987
+ renderTimeMs: Date.now() - startTime
2988
+ })
2989
+ );
2990
+ renderedNodesCache.set(cacheKey, {
2991
+ element: result,
2992
+ timestamp: startTime
2993
+ });
2994
+ if (renderedNodesCache.size > MAX_CACHE_SIZE) {
2995
+ const oldestEntry = renderedNodesCache.entries().next().value;
2996
+ if (oldestEntry) {
2997
+ renderedNodesCache.delete(oldestEntry[0]);
2998
+ }
2999
+ }
3000
+ return result;
3001
+ }
3002
+ function renderShimmer(node, adapter = "shadcn") {
3003
+ if (!node) {
3004
+ return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
3005
+ }
3006
+ switch (node.node_type) {
3007
+ case "ListView":
3008
+ return /* @__PURE__ */ jsxRuntime.jsx(ShimmerTable, { rows: 3 });
3009
+ case "Detail":
3010
+ return /* @__PURE__ */ jsxRuntime.jsx(ShimmerCard, {});
3011
+ case "Container":
3012
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: node.children?.map((child, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderShimmer(child, adapter) }, index)) });
2749
3013
  default:
2750
- console.warn(`Unknown action: ${action}`);
3014
+ return /* @__PURE__ */ jsxRuntime.jsx(ShimmerBlock, {});
2751
3015
  }
2752
- return newContext;
2753
3016
  }
2754
3017
 
2755
3018
  // src/core/events.ts
@@ -2886,18 +3149,26 @@ var AutoUI = ({
2886
3149
  eventHooks,
2887
3150
  systemEventHooks,
2888
3151
  debugMode = false,
2889
- mockMode = false,
2890
3152
  planningConfig,
2891
3153
  integration = {},
2892
- scope = {},
2893
- enablePartialUpdates = false,
2894
- openaiApiKey
3154
+ enablePartialUpdates = true,
3155
+ apiKey
2895
3156
  }) => {
3157
+ const stableGoal = React__default.default.useMemo(() => goal, [goal]);
3158
+ const schemaStringified = React__default.default.useMemo(() => JSON.stringify(schema), [schema]);
3159
+ const stableSchemaProp = React__default.default.useMemo(() => schema, [schemaStringified]);
2896
3160
  const [schemaAdapterInstance] = React.useState(null);
2897
3161
  const [dataContext, setDataContext] = React.useState({});
2898
3162
  const [componentsAvailable, setComponentsAvailable] = React.useState(true);
2899
- const effectiveSchema = schema;
2900
- const scopedGoal = goal;
3163
+ const [uiStatus, setUiStatus] = React.useState("initializing");
3164
+ const [currentResolvedLayoutForRender, setCurrentResolvedLayoutForRender] = React.useState(null);
3165
+ const [isResolvingBindings, setIsResolvingBindings] = React.useState(false);
3166
+ const [renderedNode, setRenderedNode] = React.useState(
3167
+ null
3168
+ );
3169
+ const currentResolvedLayoutRef = React.useRef(null);
3170
+ const effectiveSchema = stableSchemaProp;
3171
+ const scopedGoal = stableGoal;
2901
3172
  React.useEffect(() => {
2902
3173
  if (componentAdapter === "shadcn") {
2903
3174
  setComponentsAvailable(areShadcnComponentsAvailable());
@@ -2942,7 +3213,6 @@ var AutoUI = ({
2942
3213
  Object.entries(effectiveSchema).forEach(([key, tableSchema]) => {
2943
3214
  initialData[key] = {
2944
3215
  schema: tableSchema,
2945
- // For development, add sample data if available
2946
3216
  data: tableSchema?.sampleData || [],
2947
3217
  selected: null
2948
3218
  };
@@ -2959,12 +3229,11 @@ var AutoUI = ({
2959
3229
  schema: effectiveSchema,
2960
3230
  goal: scopedGoal,
2961
3231
  userContext,
2962
- mockMode,
2963
3232
  planningConfig,
2964
- router: void 0,
2965
3233
  dataContext,
3234
+ // Pass the local dataContext here, engine will use it if its own is empty initially
2966
3235
  enablePartialUpdates,
2967
- openaiApiKey
3236
+ apiKey
2968
3237
  });
2969
3238
  const eventManagerRef = React.useRef(new EventManager());
2970
3239
  React.useEffect(() => {
@@ -3003,161 +3272,344 @@ var AutoUI = ({
3003
3272
  unregisters.forEach((unregister) => unregister());
3004
3273
  };
3005
3274
  }, [eventHooks]);
3006
- const processEvent2 = React.useCallback(
3275
+ React.useEffect(() => {
3276
+ currentResolvedLayoutRef.current = currentResolvedLayoutForRender;
3277
+ }, [currentResolvedLayoutForRender]);
3278
+ const processEvent = React.useCallback(
3007
3279
  async (event) => {
3280
+ setUiStatus("event_processing");
3281
+ const layoutAtEventTime = currentResolvedLayoutRef.current;
3008
3282
  const shouldProceed = await eventManagerRef.current.processEvent(event);
3009
- if (onEvent) {
3283
+ if (onEvent)
3010
3284
  onEvent(event);
3011
- }
3012
3285
  if (!shouldProceed) {
3013
- console.info("Event processing was prevented by hooks", event);
3014
- return;
3015
- }
3016
- const findNodeById2 = (node, id) => {
3017
- if (!node)
3018
- return void 0;
3019
- if (node.id === id)
3020
- return node;
3021
- if (node.children) {
3022
- for (const child of node.children) {
3023
- const found = findNodeById2(child, id);
3024
- if (found)
3025
- return found;
3026
- }
3027
- }
3028
- return void 0;
3029
- };
3030
- const sourceNode = findNodeById2(state.layout, event.nodeId);
3031
- if (!sourceNode) {
3032
- console.warn(`Node not found for event: ${event.nodeId}`);
3033
- handleEvent(event);
3286
+ console.info(
3287
+ "[AutoUI.processEvent] Event processing stopped by local hooks",
3288
+ event
3289
+ );
3290
+ setUiStatus("idle");
3034
3291
  return;
3035
3292
  }
3036
- const eventConfig = sourceNode.events?.[event.type];
3037
- if (!eventConfig) {
3038
- console.warn(
3039
- `No event config found for ${event.type} on node ${event.nodeId}`
3293
+ if (event.type === "CLICK" && layoutAtEventTime && event.nodeId.includes("view-details-button")) {
3294
+ const mainContent = layoutAtEventTime.children?.find(
3295
+ (c) => c.id === "main-content"
3040
3296
  );
3041
- handleEvent(event);
3042
- return;
3297
+ const taskList = mainContent?.children?.find((c) => c.id === "tasks-container")?.children?.find((c) => c.id === "task-list");
3298
+ const eventSourceListItemCard = taskList?.children?.find(
3299
+ (item) => item.children?.some((btn) => btn.id === event.nodeId)
3300
+ );
3301
+ const buttonNode = eventSourceListItemCard?.children?.find(
3302
+ (btn) => btn.id === event.nodeId
3303
+ );
3304
+ if (buttonNode?.events?.CLICK?.action === "SHOW_DETAIL" /* SHOW_DETAIL */ && buttonNode?.events?.CLICK?.target === "task-detail") {
3305
+ const cacheKeyToClear = `task-detail:false:no-data-selected`;
3306
+ clearRenderedNodeCacheEntry(cacheKeyToClear);
3307
+ console.log(
3308
+ `[AutoUI.processEvent] Attempted to clear cache for task-detail using simplified key: ${cacheKeyToClear}`
3309
+ );
3310
+ }
3043
3311
  }
3044
- const newContext = executeAction(
3045
- eventConfig.action,
3046
- eventConfig.target || "",
3047
- // Provide empty string as fallback if target is null
3048
- {
3049
- ...eventConfig.payload,
3050
- ...event.payload
3051
- },
3052
- dataContext,
3053
- state.layout || void 0
3054
- );
3055
- setDataContext(newContext);
3056
- handleEvent(event);
3057
- },
3058
- [dataContext, handleEvent, onEvent, state.layout]
3059
- );
3060
- const [resolvedLayout, setResolvedLayout] = React.useState(
3061
- void 0
3062
- );
3063
- const [renderedNode, setRenderedNode] = React.useState(
3064
- null
3065
- );
3066
- const resolveLayoutBindings = React.useCallback(async () => {
3067
- if (state.layout && dataContext) {
3068
- console.log(
3069
- "[AutoUI Debug] DataContext before resolving bindings:",
3070
- JSON.stringify(dataContext, null, 2)
3071
- );
3072
3312
  console.log(
3073
- "[AutoUI Debug] Raw layout before resolving (from planner):",
3074
- JSON.stringify(state.layout, null, 2)
3075
- );
3076
- const correctedLayout = correctListBindingsRecursive(
3077
- state.layout,
3078
- dataContext
3313
+ "[AutoUI.processEvent] Forwarding event to engine:",
3314
+ JSON.stringify(event, null, 2)
3079
3315
  );
3080
3316
  console.log(
3081
- "[AutoUI Debug] Layout after binding correction (before resolving):",
3082
- JSON.stringify(correctedLayout, null, 2)
3317
+ "[AutoUI.processEvent] Passing currentLayout ID (at event time):",
3318
+ layoutAtEventTime?.id
3083
3319
  );
3084
- const resolved = await resolveBindings(correctedLayout, dataContext);
3085
- setResolvedLayout(resolved);
3086
3320
  console.log(
3087
- "[AutoUI Debug] Resolved layout after bindings:",
3088
- JSON.stringify(resolved, null, 2)
3321
+ "[AutoUI.processEvent] Passing dataContext keys to engine:",
3322
+ Object.keys(dataContext)
3089
3323
  );
3090
- } else {
3091
- setResolvedLayout(void 0);
3324
+ handleEvent(event, layoutAtEventTime, dataContext);
3325
+ },
3326
+ [
3327
+ dataContext,
3328
+ handleEvent,
3329
+ onEvent,
3330
+ eventManagerRef
3331
+ // currentResolvedLayoutForRender removed - using ref instead to prevent infinite loops
3332
+ // setUiStatus is implicitly available
3333
+ ]
3334
+ );
3335
+ React.useEffect(() => {
3336
+ if (state.dataContext && Object.keys(state.dataContext).length > 0) {
3337
+ if (JSON.stringify(dataContext) !== JSON.stringify(state.dataContext)) {
3338
+ console.log(
3339
+ "[AutoUI] Syncing local dataContext from engine state. New keys:",
3340
+ Object.keys(state.dataContext),
3341
+ "Old keys:",
3342
+ Object.keys(dataContext)
3343
+ );
3344
+ setDataContext(state.dataContext);
3345
+ }
3092
3346
  }
3347
+ }, [state.dataContext, dataContext]);
3348
+ React.useEffect(() => {
3349
+ const resolveAndSetLayout = async () => {
3350
+ const hasMeaningfulDataContext = dataContext && Object.keys(dataContext).some(
3351
+ (key) => key !== "user" || Object.keys(dataContext["user"]).length > 0
3352
+ );
3353
+ if (state.layout && hasMeaningfulDataContext) {
3354
+ console.log(
3355
+ `[AutoUI resolveAndSetLayout] Calling core resolveBindings for layout ID: ${state.layout.id}. DataContext keys: ${Object.keys(dataContext).join(", ")}`
3356
+ );
3357
+ setIsResolvingBindings(true);
3358
+ setUiStatus("resolving_bindings");
3359
+ try {
3360
+ const correctedLayout = correctListBindingsRecursive(
3361
+ state.layout,
3362
+ dataContext
3363
+ );
3364
+ systemEvents.emit(
3365
+ createSystemEvent("BINDING_RESOLUTION_START" /* BINDING_RESOLUTION_START */, {
3366
+ layout: correctedLayout
3367
+ // Log corrected layout before resolving
3368
+ })
3369
+ );
3370
+ const resolved = await resolveBindings(correctedLayout, dataContext);
3371
+ setCurrentResolvedLayoutForRender(resolved);
3372
+ systemEvents.emit(
3373
+ createSystemEvent("BINDING_RESOLUTION_COMPLETE" /* BINDING_RESOLUTION_COMPLETE */, {
3374
+ originalLayout: correctedLayout,
3375
+ // Log corrected layout
3376
+ resolvedLayout: resolved
3377
+ })
3378
+ );
3379
+ } catch (err) {
3380
+ console.error("Error resolving bindings:", err);
3381
+ setUiStatus("error");
3382
+ setCurrentResolvedLayoutForRender(null);
3383
+ } finally {
3384
+ setIsResolvingBindings(false);
3385
+ }
3386
+ } else {
3387
+ if (!state.layout)
3388
+ console.log(
3389
+ "[AutoUI] Skipping resolveBindings: state.layout is null/undefined"
3390
+ );
3391
+ if (!hasMeaningfulDataContext)
3392
+ console.log(
3393
+ "[AutoUI] Skipping resolveBindings: dataContext is not meaningfully populated yet"
3394
+ );
3395
+ setCurrentResolvedLayoutForRender(null);
3396
+ if (!state.loading && uiStatus !== "initializing" && !isResolvingBindings)
3397
+ setUiStatus("idle");
3398
+ }
3399
+ };
3400
+ resolveAndSetLayout();
3093
3401
  }, [state.layout, dataContext]);
3094
3402
  React.useEffect(() => {
3095
- resolveLayoutBindings();
3096
- }, [resolveLayoutBindings]);
3097
- const renderResolvedLayout = React.useCallback(async () => {
3098
- if (resolvedLayout) {
3099
- try {
3100
- const rendered = await renderNode2(
3101
- resolvedLayout,
3102
- componentAdapter,
3103
- processEvent2
3403
+ const renderLayout = async () => {
3404
+ if (currentResolvedLayoutForRender && !isResolvingBindings) {
3405
+ setUiStatus("rendering");
3406
+ console.log(
3407
+ "[AutoUI Debug] Rendering with currentResolvedLayoutForRender:",
3408
+ JSON.stringify(currentResolvedLayoutForRender, null, 2)
3104
3409
  );
3105
- setRenderedNode(rendered);
3106
- } catch (err) {
3107
- console.error("Error rendering node:", err);
3410
+ try {
3411
+ systemEvents.emit(
3412
+ createSystemEvent("RENDER_START" /* RENDER_START */, {
3413
+ layout: currentResolvedLayoutForRender
3414
+ })
3415
+ );
3416
+ const rendered = await renderNode2(
3417
+ currentResolvedLayoutForRender,
3418
+ componentAdapter,
3419
+ processEvent
3420
+ );
3421
+ setRenderedNode(rendered);
3422
+ systemEvents.emit(
3423
+ createSystemEvent("RENDER_COMPLETE" /* RENDER_COMPLETE */, {
3424
+ layout: currentResolvedLayoutForRender,
3425
+ renderTimeMs: 0
3426
+ // Placeholder, actual timing would be more complex
3427
+ })
3428
+ );
3429
+ setUiStatus("idle");
3430
+ } catch (err) {
3431
+ console.error("Error rendering node:", err);
3432
+ setUiStatus("error");
3433
+ }
3434
+ } else if (!currentResolvedLayoutForRender && !state.loading && !isResolvingBindings && uiStatus !== "initializing") {
3435
+ setRenderedNode(null);
3436
+ setUiStatus("idle");
3437
+ }
3438
+ };
3439
+ renderLayout();
3440
+ }, [
3441
+ currentResolvedLayoutForRender,
3442
+ componentAdapter,
3443
+ processEvent,
3444
+ isResolvingBindings,
3445
+ state.loading
3446
+ ]);
3447
+ React.useEffect(() => {
3448
+ if (uiStatus !== "error") {
3449
+ setUiStatus("initializing");
3450
+ }
3451
+ }, []);
3452
+ React.useEffect(() => {
3453
+ if (uiStatus === "initializing") {
3454
+ if (state.loading) ; else if (state.layout && !isResolvingBindings) ; else if (!state.layout && !state.loading && !isResolvingBindings) {
3455
+ if (state.error) {
3456
+ setUiStatus("error");
3457
+ } else {
3458
+ setUiStatus("idle");
3459
+ }
3108
3460
  }
3109
- } else {
3110
- setRenderedNode(null);
3111
3461
  }
3112
- }, [resolvedLayout, componentAdapter, processEvent2]);
3462
+ }, [state.loading, state.layout, state.error, uiStatus, isResolvingBindings]);
3113
3463
  React.useEffect(() => {
3114
- renderResolvedLayout();
3115
- }, [renderResolvedLayout]);
3464
+ if (!debugMode)
3465
+ return;
3466
+ const pipelineState = {
3467
+ uiStatus,
3468
+ hasLayout: !!state.layout,
3469
+ layoutId: state.layout?.id || null,
3470
+ hasResolvedLayout: !!currentResolvedLayoutForRender,
3471
+ resolvedLayoutId: currentResolvedLayoutForRender?.id || null,
3472
+ hasRenderedNode: !!renderedNode,
3473
+ isResolvingBindings,
3474
+ isLoading: state.loading,
3475
+ hasError: !!state.error,
3476
+ error: state.error || null,
3477
+ dataContextKeys: Object.keys(dataContext),
3478
+ timestamp: Date.now()
3479
+ };
3480
+ if (typeof window !== "undefined") {
3481
+ window.__autoui_pipeline_state = pipelineState;
3482
+ window.__autoui_pipeline_history = window.__autoui_pipeline_history || [];
3483
+ window.__autoui_pipeline_history.push(pipelineState);
3484
+ console.log("[PIPELINE_STATE]", JSON.stringify(pipelineState));
3485
+ }
3486
+ }, [
3487
+ debugMode,
3488
+ uiStatus,
3489
+ state.layout,
3490
+ state.loading,
3491
+ state.error,
3492
+ currentResolvedLayoutForRender,
3493
+ renderedNode,
3494
+ isResolvingBindings,
3495
+ dataContext
3496
+ ]);
3116
3497
  if (!componentsAvailable) {
3117
3498
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error p-4 border border-red-300 bg-red-50 text-red-700 rounded", children: [
3118
3499
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: "Component Library Not Found" }),
3119
3500
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-line", children: getMissingComponentsMessage() })
3120
3501
  ] });
3121
3502
  }
3503
+ const showShimmer = (uiStatus === "initializing" || state.loading || isResolvingBindings) && !state.error;
3122
3504
  return /* @__PURE__ */ jsxRuntime.jsxs(
3123
3505
  "div",
3124
3506
  {
3125
3507
  className: `autoui-root ${integration.className || ""}`,
3126
3508
  id: integration.id,
3127
- "data-mode": integration.mode,
3128
- "data-scope": scope?.type || "full",
3129
3509
  children: [
3130
- state.loading || !resolvedLayout ? (
3131
- // Render shimmer loading state
3132
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-loading", children: state.layout ? renderShimmer(state.layout, componentAdapter) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-shimmer-container", children: [
3133
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-shimmer-header" }),
3134
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-shimmer-content" })
3135
- ] }) })
3136
- ) : (
3137
- // Render the resolved layout
3138
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderedNode })
3139
- ),
3140
- 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: [
3141
- /* @__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" }),
3142
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "autoui-error-message text-sm text-red-600 dark:text-red-300", children: state.error }),
3143
- !mockMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 text-sm text-red-600 dark:text-red-300", children: [
3144
- /* @__PURE__ */ jsxRuntime.jsx("p", { children: "This could be because:" }),
3145
- /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "list-disc pl-5 mt-2", children: [
3146
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your OpenAI API key is missing or invalid" }),
3147
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "The OpenAI service is experiencing issues" }),
3148
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "Your API rate limit has been exceeded" })
3149
- ] }),
3150
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-2", children: [
3151
- "Try setting ",
3152
- /* @__PURE__ */ jsxRuntime.jsx("code", { children: "mockMode=true" }),
3153
- " to use sample data instead."
3154
- ] })
3155
- ] })
3510
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
3511
+ "Current Status: ",
3512
+ uiStatus
3513
+ ] }),
3514
+ uiStatus === "idle" && !isResolvingBindings && renderedNode && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-content", children: renderedNode }),
3515
+ state.error && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-error", children: [
3516
+ "Error: ",
3517
+ state.error
3518
+ ] }),
3519
+ showShimmer && state.layout && // Show shimmer if we have a layout to base it on
3520
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "autoui-loading", children: renderShimmer(state.layout, componentAdapter) }),
3521
+ showShimmer && !state.layout && // Fallback shimmer if no layout yet
3522
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "autoui-loading p-4", children: [
3523
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-20 bg-gray-200 animate-pulse rounded mb-4" }),
3524
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-40 bg-gray-200 animate-pulse rounded" })
3156
3525
  ] })
3157
3526
  ]
3158
3527
  }
3159
3528
  );
3160
3529
  };
3530
+ var uiEventType = zod.z.enum([
3531
+ "INIT",
3532
+ "CLICK",
3533
+ "CHANGE",
3534
+ "SUBMIT",
3535
+ "MOUSEOVER",
3536
+ "MOUSEOUT",
3537
+ "FOCUS",
3538
+ "BLUR"
3539
+ ]);
3540
+ var uiEvent = zod.z.object({
3541
+ type: uiEventType,
3542
+ nodeId: zod.z.string(),
3543
+ timestamp: zod.z.number().nullable(),
3544
+ payload: zod.z.record(zod.z.unknown()).nullable()
3545
+ });
3546
+ zod.z.enum(["AI_RESPONSE", "ERROR"]);
3547
+ var runtimeRecord = zod.z.record(zod.z.unknown()).nullable();
3548
+ var uiSpecNode = zod.z.object({
3549
+ id: zod.z.string(),
3550
+ node_type: zod.z.string(),
3551
+ props: runtimeRecord,
3552
+ bindings: runtimeRecord,
3553
+ events: zod.z.record(
3554
+ zod.z.string(),
3555
+ zod.z.object({
3556
+ action: zod.z.string(),
3557
+ target: zod.z.string(),
3558
+ payload: runtimeRecord
3559
+ })
3560
+ ).nullable(),
3561
+ children: zod.z.lazy(() => zod.z.array(uiSpecNode)).nullable()
3562
+ });
3563
+ zod.z.discriminatedUnion("type", [
3564
+ zod.z.object({
3565
+ type: zod.z.literal("UI_EVENT"),
3566
+ event: uiEvent
3567
+ }),
3568
+ zod.z.object({
3569
+ type: zod.z.literal("AI_RESPONSE"),
3570
+ node: uiSpecNode
3571
+ }),
3572
+ zod.z.object({
3573
+ type: zod.z.literal("PARTIAL_UPDATE"),
3574
+ nodeId: zod.z.string(),
3575
+ node: uiSpecNode
3576
+ }),
3577
+ zod.z.object({
3578
+ type: zod.z.literal("ADD_NODE"),
3579
+ parentId: zod.z.string(),
3580
+ node: uiSpecNode,
3581
+ index: zod.z.number().nullable()
3582
+ }),
3583
+ zod.z.object({
3584
+ type: zod.z.literal("REMOVE_NODE"),
3585
+ nodeId: zod.z.string()
3586
+ }),
3587
+ zod.z.object({
3588
+ type: zod.z.literal("ERROR"),
3589
+ message: zod.z.string()
3590
+ }),
3591
+ zod.z.object({
3592
+ type: zod.z.literal("LOADING"),
3593
+ isLoading: zod.z.boolean()
3594
+ }),
3595
+ zod.z.object({
3596
+ type: zod.z.literal("SET_DATA_CONTEXT"),
3597
+ payload: zod.z.record(zod.z.string(), zod.z.unknown())
3598
+ })
3599
+ ]);
3600
+ zod.z.object({
3601
+ layout: uiSpecNode.nullable(),
3602
+ loading: zod.z.boolean(),
3603
+ history: zod.z.array(uiEvent),
3604
+ error: zod.z.string().nullable(),
3605
+ dataContext: zod.z.record(zod.z.string(), zod.z.unknown())
3606
+ });
3607
+ zod.z.object({
3608
+ schema: zod.z.record(zod.z.unknown()),
3609
+ goal: zod.z.string(),
3610
+ history: zod.z.array(uiEvent).nullable(),
3611
+ userContext: zod.z.record(zod.z.unknown()).nullable().optional()
3612
+ });
3161
3613
 
3162
3614
  // src/adapters/schema/drizzle.ts
3163
3615
  var DrizzleAdapter = class {
@@ -3292,113 +3744,23 @@ var generateUIComponent = async (prompt) => {
3292
3744
  );
3293
3745
  return `<div>Generated UI Component for: ${prompt}</div>`;
3294
3746
  };
3295
- function usePlanner(options) {
3296
- const {
3297
- schema,
3298
- goal,
3299
- openaiApiKey,
3300
- userContext,
3301
- initialLayout,
3302
- mockMode: optionsMockMode
3303
- } = options;
3304
- const [layout, setLayout] = React.useState(
3305
- initialLayout || void 0
3306
- );
3307
- const [loading, setLoading] = React.useState(false);
3308
- const [error, setError] = React.useState(null);
3309
- const router = options.router || createDefaultRouter();
3310
- const dataContext = {};
3311
- const initialFetchAttempted = React.useRef(false);
3312
- const mockMode = optionsMockMode || !openaiApiKey;
3313
- const generateInitialLayout = React.useCallback(async () => {
3314
- setLoading(true);
3315
- setError(null);
3316
- try {
3317
- const plannerInput2 = {
3318
- schema,
3319
- goal,
3320
- userContext: userContext || null,
3321
- history: null
3322
- };
3323
- let generatedLayout;
3324
- if (mockMode) {
3325
- console.warn(
3326
- "Using mock planner in usePlanner hook (mockMode enabled or API key missing)."
3327
- );
3328
- generatedLayout = mockPlanner(plannerInput2);
3329
- } else {
3330
- generatedLayout = await callPlannerLLM(
3331
- plannerInput2,
3332
- openaiApiKey,
3333
- void 0
3334
- );
3335
- }
3336
- setLayout(generatedLayout);
3337
- } catch (err) {
3338
- setError(err instanceof Error ? err : new Error(String(err)));
3339
- } finally {
3340
- setLoading(false);
3341
- }
3342
- }, [schema, goal, userContext, openaiApiKey, mockMode]);
3343
- const handleEvent = React.useCallback(
3344
- async (event) => {
3345
- if (!layout) {
3346
- setError(new Error("Cannot handle event - no layout exists"));
3347
- return;
3348
- }
3349
- setLoading(true);
3350
- setError(null);
3351
- try {
3352
- const updatedLayout = await processEvent(
3353
- event,
3354
- router,
3355
- schema,
3356
- layout,
3357
- dataContext,
3358
- goal,
3359
- userContext,
3360
- openaiApiKey
3361
- );
3362
- setLayout(updatedLayout);
3363
- } catch (err) {
3364
- setError(err instanceof Error ? err : new Error(String(err)));
3365
- } finally {
3366
- setLoading(false);
3367
- }
3368
- },
3369
- [layout, router, schema, dataContext, goal, userContext, openaiApiKey]
3370
- );
3371
- React.useEffect(() => {
3372
- if (options.initialLayout === void 0 && layout === void 0 && !initialFetchAttempted.current) {
3373
- initialFetchAttempted.current = true;
3374
- generateInitialLayout();
3375
- }
3376
- }, [options.initialLayout, layout, generateInitialLayout]);
3377
- return {
3378
- layout,
3379
- loading,
3380
- error,
3381
- handleEvent,
3382
- generateInitialLayout
3383
- };
3384
- }
3385
3747
 
3386
3748
  exports.ActionRouter = ActionRouter;
3387
3749
  exports.ActionType = ActionType;
3388
3750
  exports.AutoUI = AutoUI;
3389
3751
  exports.DrizzleAdapter = DrizzleAdapter;
3390
3752
  exports.SystemEventType = SystemEventType;
3391
- exports.createDefaultRouter = createDefaultRouter;
3753
+ exports.componentType = componentType;
3392
3754
  exports.createEventHook = createEventHook;
3393
3755
  exports.createSchemaAdapter = createSchemaAdapter;
3394
3756
  exports.createSystemEvent = createSystemEvent;
3395
3757
  exports.generateComponent = generateComponent;
3396
3758
  exports.generateUIComponent = generateUIComponent;
3397
3759
  exports.generateUIDescription = generateUIDescription;
3760
+ exports.openAIUISpec = openAIUISpec;
3398
3761
  exports.systemEvents = systemEvents;
3399
3762
  exports.uiEvent = uiEvent;
3400
3763
  exports.uiEventType = uiEventType;
3401
3764
  exports.uiSpecNode = uiSpecNode;
3402
- exports.usePlanner = usePlanner;
3403
3765
  //# sourceMappingURL=out.js.map
3404
3766
  //# sourceMappingURL=index.js.map