@agent-native/core 0.84.9 → 0.84.12

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.
Files changed (80) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +43 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/run-store.ts +29 -6
  5. package/corpus/core/src/cli/skills.ts +6 -3
  6. package/corpus/core/src/client/agent-chat-adapter.ts +31 -11
  7. package/corpus/core/src/client/require-session.tsx +20 -1
  8. package/corpus/core/src/client/sharing/ShareButton.tsx +9 -3
  9. package/corpus/core/src/collab/storage.ts +40 -5
  10. package/corpus/core/src/server/auth.ts +6 -0
  11. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +7 -6
  12. package/corpus/templates/assets/AGENTS.md +15 -0
  13. package/corpus/templates/assets/actions/_helpers.ts +3 -1
  14. package/corpus/templates/assets/actions/create-generation-preset.ts +12 -1
  15. package/corpus/templates/assets/actions/generate-asset.ts +5 -1
  16. package/corpus/templates/assets/actions/generate-image-batch.ts +6 -1
  17. package/corpus/templates/assets/actions/generate-image.ts +18 -11
  18. package/corpus/templates/assets/actions/open-asset-picker.ts +4 -2
  19. package/corpus/templates/assets/actions/update-generation-preset.ts +16 -3
  20. package/corpus/templates/assets/app/i18n/zh-TW.ts +3 -0
  21. package/corpus/templates/assets/app/i18n-data.ts +30 -0
  22. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +42 -7
  23. package/corpus/templates/assets/app/routes/library.tsx +4 -2
  24. package/corpus/templates/assets/changelog/2026-07-01-generation-presets-can-now-composite-your-brand-s-canonical-.md +6 -0
  25. package/corpus/templates/assets/changelog/2026-07-01-tagging-a-preset-now-briefs-the-agent-on-that-preset-s-aesth.md +6 -0
  26. package/corpus/templates/assets/server/lib/preset-chat-context.ts +118 -0
  27. package/corpus/templates/assets/server/plugins/agent-chat.ts +5 -0
  28. package/corpus/templates/assets/shared/api.ts +1 -0
  29. package/corpus/templates/design/actions/export-html.ts +4 -2
  30. package/corpus/templates/design/actions/export-pdf.ts +3 -1
  31. package/corpus/templates/design/actions/export-svg.ts +4 -2
  32. package/corpus/templates/design/actions/export-zip.ts +10 -3
  33. package/corpus/templates/design/actions/get-design-snapshot.ts +41 -4
  34. package/corpus/templates/design/actions/present-design-variants.ts +21 -6
  35. package/corpus/templates/design/actions/update-file.ts +68 -17
  36. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +3 -2
  37. package/corpus/templates/design/app/components/design/EditPanel.tsx +35 -3
  38. package/corpus/templates/design/app/components/design/MotionDock.tsx +46 -11
  39. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +80 -30
  40. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +60 -10
  41. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +2 -0
  42. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +4 -0
  43. package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
  44. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +2 -2
  45. package/corpus/templates/design/app/i18n-data.ts +1 -1
  46. package/corpus/templates/design/app/pages/DesignEditor.tsx +493 -109
  47. package/corpus/templates/design/app/pages/VisualEdit.tsx +4 -16
  48. package/corpus/templates/design/changelog/2026-07-01-improved-variant-pick-follow-ups-so-selected-directions-cont.md +6 -0
  49. package/corpus/templates/design/e2e/helpers.ts +2 -2
  50. package/dist/agent/run-store.d.ts.map +1 -1
  51. package/dist/agent/run-store.js +28 -6
  52. package/dist/agent/run-store.js.map +1 -1
  53. package/dist/cli/skills.d.ts.map +1 -1
  54. package/dist/cli/skills.js +6 -3
  55. package/dist/cli/skills.js.map +1 -1
  56. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  57. package/dist/client/agent-chat-adapter.js +26 -11
  58. package/dist/client/agent-chat-adapter.js.map +1 -1
  59. package/dist/client/require-session.d.ts +14 -0
  60. package/dist/client/require-session.d.ts.map +1 -1
  61. package/dist/client/require-session.js +19 -1
  62. package/dist/client/require-session.js.map +1 -1
  63. package/dist/client/sharing/ShareButton.d.ts +4 -2
  64. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  65. package/dist/client/sharing/ShareButton.js +3 -1
  66. package/dist/client/sharing/ShareButton.js.map +1 -1
  67. package/dist/collab/routes.d.ts +1 -1
  68. package/dist/collab/storage.d.ts.map +1 -1
  69. package/dist/collab/storage.js +31 -5
  70. package/dist/collab/storage.js.map +1 -1
  71. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  72. package/dist/notifications/routes.d.ts +2 -2
  73. package/dist/observability/routes.d.ts +6 -6
  74. package/dist/resources/handlers.d.ts +2 -2
  75. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  76. package/dist/server/auth.d.ts.map +1 -1
  77. package/dist/server/auth.js +7 -0
  78. package/dist/server/auth.js.map +1 -1
  79. package/dist/server/transcribe-voice.d.ts +1 -1
  80. package/package.json +1 -1
@@ -186,7 +186,11 @@ interface MultiScreenCanvasProps {
186
186
  screenId: string,
187
187
  primitive: CanvasPrimitiveInsert,
188
188
  ) => boolean | string;
189
- onPrimitiveCreated?: (screenId: string, nodeId: string) => void;
189
+ onPrimitiveCreated?: (
190
+ screenId: string,
191
+ nodeId: string,
192
+ options?: { nextTool?: "move" | "pen" },
193
+ ) => void;
190
194
  onPrimitiveReparent?: (args: {
191
195
  sourceNodeId: string;
192
196
  sourceScreenId: string;
@@ -1505,7 +1509,7 @@ export function MultiScreenCanvas({
1505
1509
  setTransformBadge(null);
1506
1510
  }, [clearSelectionRequest, updateSelectedDraftIds, updateSelectedIds]);
1507
1511
 
1508
- // Center the lineup on first mount so the user sees screens, not whitespace.
1512
+ // Center the lineup when the screen footprint changes so new frames stay reachable.
1509
1513
  useEffect(() => {
1510
1514
  if (!surfaceRef.current || screens.length === 0) return;
1511
1515
  const rect = surfaceRef.current.getBoundingClientRect();
@@ -1529,11 +1533,26 @@ export function MultiScreenCanvas({
1529
1533
  );
1530
1534
  const totalWidth = bounds?.width ?? SCREEN_WIDTH;
1531
1535
  const totalHeight = bounds?.height ?? SCREEN_CARD_HEIGHT;
1532
- const visualLeft = Math.max(24, (rect.width - totalWidth * scale) / 2);
1533
- const visualTop = Math.max(24, (rect.height - totalHeight * scale) / 2);
1536
+ // Leave a Figma-like board gutter beside the last frame for quick drops/draws,
1537
+ // and fit tall single frames so lower canvas interactions remain reachable.
1538
+ const widthFitScale =
1539
+ screens.length > 1 && totalWidth > 0
1540
+ ? Math.max(0.1, (rect.width - 180) / totalWidth)
1541
+ : scale;
1542
+ const heightFitScale =
1543
+ totalHeight > 0 ? Math.max(0.1, (rect.height - 96) / totalHeight) : scale;
1544
+ const nextScale = Math.min(scale, widthFitScale, heightFitScale);
1545
+ if (nextScale < scale) {
1546
+ const nextZoom = nextScale * 100;
1547
+ zoomRef.current = nextZoom;
1548
+ setCanvasZoom(nextZoom);
1549
+ onZoomChange?.(nextZoom);
1550
+ }
1551
+ const visualLeft = Math.max(24, (rect.width - totalWidth * nextScale) / 2);
1552
+ const visualTop = Math.max(24, (rect.height - totalHeight * nextScale) / 2);
1534
1553
  const nextPan = {
1535
- x: visualLeft - SURFACE_PADDING * scale,
1536
- y: visualTop - SURFACE_PADDING * scale,
1554
+ x: visualLeft - SURFACE_PADDING * nextScale,
1555
+ y: visualTop - SURFACE_PADDING * nextScale,
1537
1556
  };
1538
1557
  panRef.current = nextPan;
1539
1558
  setPan(nextPan);
@@ -2517,9 +2536,11 @@ export function MultiScreenCanvas({
2517
2536
  updateSelectedIds(() => state.baseSelectedIds);
2518
2537
  updateSelectedDraftIds(() => state.baseSelectedDraftIds);
2519
2538
  } else if (state.type === "pen-node") {
2520
- setActivePenPath(
2521
- state.pathBefore ? clonePenPath(state.pathBefore) : null,
2522
- );
2539
+ const restoredPath = state.pathBefore
2540
+ ? clonePenPath(state.pathBefore)
2541
+ : null;
2542
+ activePenPathRef.current = restoredPath;
2543
+ setActivePenPath(restoredPath);
2523
2544
  setPenGesturePreview(null);
2524
2545
  setPenPointer(null);
2525
2546
  setPenCloseHover(false);
@@ -2792,10 +2813,10 @@ export function MultiScreenCanvas({
2792
2813
  });
2793
2814
  const persisted = handler(boardPrimitive);
2794
2815
  if (!persisted) return null;
2795
- // Return a sentinel PersistedDraftPrimitive so the caller can remove
2796
- // the draft. The sentinel frameId "__board__" is detected downstream.
2816
+ // Return the board file id so the caller can run the same selection
2817
+ // and text-edit activation path used by regular screen primitives.
2797
2818
  return {
2798
- frameId: "__board__",
2819
+ frameId: boardFileId ?? "__board__",
2799
2820
  nodeId:
2800
2821
  (typeof persisted === "string"
2801
2822
  ? persisted
@@ -2836,6 +2857,7 @@ export function MultiScreenCanvas({
2836
2857
  };
2837
2858
  },
2838
2859
  [
2860
+ boardFileId,
2839
2861
  getScreenMetadata,
2840
2862
  getTargetFrameForDraft,
2841
2863
  metadataById,
@@ -2845,7 +2867,11 @@ export function MultiScreenCanvas({
2845
2867
  );
2846
2868
 
2847
2869
  const commitDraftPrimitive = useCallback(
2848
- (nextDraft: DraftPrimitive, preferredFrameId?: string) => {
2870
+ (
2871
+ nextDraft: DraftPrimitive,
2872
+ preferredFrameId?: string,
2873
+ options?: { nextTool?: "move" | "pen" },
2874
+ ) => {
2849
2875
  const persisted = persistDraftPrimitive(nextDraft, preferredFrameId);
2850
2876
  if (persisted) {
2851
2877
  updateDraftPrimitives((current) =>
@@ -2853,9 +2879,9 @@ export function MultiScreenCanvas({
2853
2879
  );
2854
2880
  updateSelectedDraftIds(() => []);
2855
2881
  updateSelectedIds(() => []);
2856
- if (persisted.frameId !== "__board__") {
2857
- onPrimitiveCreated?.(persisted.frameId, persisted.nodeId);
2858
- }
2882
+ onPrimitiveCreated?.(persisted.frameId, persisted.nodeId, {
2883
+ nextTool: options?.nextTool,
2884
+ });
2859
2885
  return;
2860
2886
  }
2861
2887
 
@@ -2937,9 +2963,10 @@ export function MultiScreenCanvas({
2937
2963
  stroke: toolProps?.stroke,
2938
2964
  strokeWidth: toolProps?.strokeWidth,
2939
2965
  }),
2966
+ undefined,
2967
+ { nextTool: "pen" },
2940
2968
  );
2941
2969
  clearActivePenPath();
2942
- onActiveToolChange?.("pen");
2943
2970
  },
2944
2971
  [clearActivePenPath, commitDraftPrimitive, onActiveToolChange, toolProps],
2945
2972
  );
@@ -3041,9 +3068,10 @@ export function MultiScreenCanvas({
3041
3068
  pathBefore: pathSnapshot,
3042
3069
  hasMoved: false,
3043
3070
  };
3044
- setPenGesturePreview(
3045
- appendPenNode(pathSnapshot, createCornerNode(anchor)),
3046
- );
3071
+ const initialPath = appendPenNode(pathSnapshot, createCornerNode(anchor));
3072
+ activePenPathRef.current = initialPath;
3073
+ setActivePenPath(initialPath);
3074
+ setPenGesturePreview(initialPath);
3047
3075
  setPenPointer(null);
3048
3076
  setPenCloseHover(false);
3049
3077
  setIsDragging(true);
@@ -3071,7 +3099,10 @@ export function MultiScreenCanvas({
3071
3099
  : handlePoint,
3072
3100
  )
3073
3101
  : createCornerNode(state.anchor);
3074
- setPenGesturePreview(appendPenNode(state.pathBefore, node));
3102
+ const nextPath = appendPenNode(state.pathBefore, node);
3103
+ activePenPathRef.current = nextPath;
3104
+ setActivePenPath(nextPath);
3105
+ setPenGesturePreview(nextPath);
3075
3106
  };
3076
3107
 
3077
3108
  const handleMouseUp = (ev: MouseEvent) => {
@@ -3090,7 +3121,9 @@ export function MultiScreenCanvas({
3090
3121
  : handlePoint,
3091
3122
  )
3092
3123
  : createCornerNode(state.anchor);
3093
- setActivePenPath(appendPenNode(state.pathBefore, node));
3124
+ const nextPath = appendPenNode(state.pathBefore, node);
3125
+ activePenPathRef.current = nextPath;
3126
+ setActivePenPath(nextPath);
3094
3127
  setPenGesturePreview(null);
3095
3128
  setPenPointer(null);
3096
3129
  setPenCloseHover(false);
@@ -3101,6 +3134,7 @@ export function MultiScreenCanvas({
3101
3134
  const cancelPenGesture = () => {
3102
3135
  const state = dragState.current;
3103
3136
  if (state?.type === "pen-node") {
3137
+ activePenPathRef.current = state.pathBefore;
3104
3138
  setActivePenPath(state.pathBefore);
3105
3139
  }
3106
3140
  setPenGesturePreview(null);
@@ -4730,6 +4764,20 @@ export function MultiScreenCanvas({
4730
4764
  frameGeometry[screen.id] ?? getInitialFrameGeometry(index, metadata),
4731
4765
  };
4732
4766
  });
4767
+ const topScreenId =
4768
+ selectedIds.find((id) =>
4769
+ canvasFrames.some(({ screen }) => screen.id === id),
4770
+ ) ??
4771
+ (activeId && canvasFrames.some(({ screen }) => screen.id === activeId)
4772
+ ? activeId
4773
+ : canvasFrames[0]?.screen.id);
4774
+ const renderCanvasFrames =
4775
+ topScreenId && canvasFrames.some(({ screen }) => screen.id === topScreenId)
4776
+ ? [
4777
+ ...canvasFrames.filter(({ screen }) => screen.id !== topScreenId),
4778
+ ...canvasFrames.filter(({ screen }) => screen.id === topScreenId),
4779
+ ]
4780
+ : canvasFrames;
4733
4781
  const screenContentById = useMemo(() => {
4734
4782
  if (!renderScreenContent) return new Map<string, ReactNode>();
4735
4783
  return new Map(
@@ -4910,7 +4958,7 @@ export function MultiScreenCanvas({
4910
4958
  );
4911
4959
  })()}
4912
4960
 
4913
- {canvasFrames.map(({ screen, metadata, geometry }) => {
4961
+ {renderCanvasFrames.map(({ screen, metadata, geometry }) => {
4914
4962
  return (
4915
4963
  <Screen
4916
4964
  key={screen.id}
@@ -7031,13 +7079,6 @@ function draftPrimitiveToInsert(
7031
7079
  x: Math.round((point.x - frameGeometry.x) * scaleX),
7032
7080
  y: Math.round((point.y - frameGeometry.y) * scaleY),
7033
7081
  });
7034
- const localGeometry = {
7035
- ...draft.geometry,
7036
- x: Math.round((draft.geometry.x - frameGeometry.x) * scaleX),
7037
- y: Math.round((draft.geometry.y - frameGeometry.y) * scaleY),
7038
- width: Math.max(1, Math.round(draft.geometry.width * scaleX)),
7039
- height: Math.max(1, Math.round(draft.geometry.height * scaleY)),
7040
- };
7041
7082
  const scaledPenPath = draft.penPath
7042
7083
  ? scalePenPathToGeometry(draft.penPath, frameGeometry, {
7043
7084
  x: 0,
@@ -7046,6 +7087,15 @@ function draftPrimitiveToInsert(
7046
7087
  height: metadata?.height ?? frameGeometry.height,
7047
7088
  })
7048
7089
  : undefined;
7090
+ const localGeometry = scaledPenPath
7091
+ ? getPenPathGeometry(scaledPenPath)
7092
+ : {
7093
+ ...draft.geometry,
7094
+ x: Math.round((draft.geometry.x - frameGeometry.x) * scaleX),
7095
+ y: Math.round((draft.geometry.y - frameGeometry.y) * scaleY),
7096
+ width: Math.max(1, Math.round(draft.geometry.width * scaleX)),
7097
+ height: Math.max(1, Math.round(draft.geometry.height * scaleY)),
7098
+ };
7049
7099
  return {
7050
7100
  kind: draft.kind,
7051
7101
  nodeId: draft.id,
@@ -58,6 +58,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
58
58
  );
59
59
  chromeTransitionStyle.textContent =
60
60
  '[data-agent-native-edit-overlay="selection"]{transition:border-width 150ms ease-out}' +
61
+ '[data-agent-native-empty-text-editing="true"] [data-agent-native-edit-overlay="selection"]{display:none!important}' +
62
+ "[data-agent-native-text-editing]{outline:none!important;outline-offset:0!important}" +
61
63
  "[data-agent-native-edge-handle],[data-agent-native-edit-handle],[data-agent-native-rotate-handle]{transition:width 150ms ease-out,height 150ms ease-out,border-width 150ms ease-out,top 150ms ease-out,bottom 150ms ease-out,left 150ms ease-out,right 150ms ease-out}" +
62
64
  "[data-agent-native-spacing-line]{position:absolute;display:none;pointer-events:none;border-radius:999px}" +
63
65
  "[data-agent-native-spacing-region]{position:absolute;display:none;box-sizing:border-box;pointer-events:auto;background-size:6px 6px}" +
@@ -2402,12 +2404,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2402
2404
  }
2403
2405
 
2404
2406
  function refreshOverlays(): void {
2407
+ var textEditingEl =
2408
+ activeTextEditEl ||
2409
+ (document.querySelector(
2410
+ "[data-agent-native-text-editing]",
2411
+ ) as HTMLElement | null);
2405
2412
  if (hoveredEl && hoveredEl !== selectedEl) {
2406
2413
  positionOverlay(highlightOverlay, hoveredEl);
2407
2414
  } else {
2408
2415
  highlightOverlay.style.display = "none";
2409
2416
  }
2410
- if (selectedEl) {
2417
+ if (textEditingEl) {
2418
+ if (activeTextEditEl === textEditingEl) {
2419
+ updateTextEditingChrome(textEditingEl, "", "");
2420
+ }
2421
+ if (!hasTextCharacters(textEditingEl)) {
2422
+ hideSelectionOverlay();
2423
+ }
2424
+ } else if (selectedEl) {
2411
2425
  positionOverlay(selectionOverlay, selectedEl);
2412
2426
  } else {
2413
2427
  hideParentAutoLayoutOverlay();
@@ -2819,9 +2833,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2819
2833
  originalMinWidth: string,
2820
2834
  originalMinHeight: string,
2821
2835
  ): void {
2822
- target.style.outline = "";
2823
- target.style.outlineOffset = "";
2836
+ target.style.outline = "none";
2837
+ target.style.outlineStyle = "none";
2838
+ target.style.outlineWidth = "0px";
2839
+ target.style.outlineColor = "transparent";
2840
+ target.style.outlineOffset = "0px";
2824
2841
  if (hasTextCharacters(target)) {
2842
+ document.documentElement.removeAttribute(
2843
+ "data-agent-native-empty-text-editing",
2844
+ );
2825
2845
  target.style.minWidth = originalMinWidth;
2826
2846
  target.style.minHeight = originalMinHeight;
2827
2847
  positionOverlay(selectionOverlay, target);
@@ -2830,6 +2850,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2830
2850
  }
2831
2851
  target.style.minWidth = originalMinWidth || "1px";
2832
2852
  target.style.minHeight = originalMinHeight || "1em";
2853
+ document.documentElement.setAttribute(
2854
+ "data-agent-native-empty-text-editing",
2855
+ "true",
2856
+ );
2833
2857
  hideSelectionOverlay();
2834
2858
  setSelectionOverlayResizeChromeVisible(false);
2835
2859
  }
@@ -5021,24 +5045,37 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5021
5045
  // code-layer node rather than a runtime-only descendant (which would emit a
5022
5046
  // brittle body > div:nth-of-type(...) selector that never resolves).
5023
5047
  selectedEl = selectionTargetForHit(target) || target;
5048
+ var programmaticTextEdit =
5049
+ !!e &&
5050
+ (e as unknown as { agentNativeProgrammaticTextEdit?: boolean })
5051
+ .agentNativeProgrammaticTextEdit === true;
5024
5052
  var originalText = target.textContent || "";
5025
5053
  var originalHtml = target.innerHTML || "";
5026
5054
  var originalMinWidth = target.style.minWidth;
5027
5055
  var originalMinHeight = target.style.minHeight;
5028
5056
  var originalBorderColor = target.style.borderColor;
5057
+ var originalOutline = target.style.outline;
5058
+ var originalOutlineOffset = target.style.outlineOffset;
5029
5059
  var committed = false;
5030
5060
  activeTextEditEl = target;
5031
5061
  target.setAttribute("contenteditable", "true");
5032
5062
  target.setAttribute("data-agent-native-text-editing", "true");
5033
5063
  target.style.cursor = "text";
5034
5064
  target.style.borderColor = "transparent";
5065
+ target.style.outline = "none";
5066
+ target.style.outlineStyle = "none";
5067
+ target.style.outlineWidth = "0px";
5068
+ target.style.outlineColor = "transparent";
5069
+ target.style.outlineOffset = "0px";
5035
5070
  setTextEditingPointerPassthrough(true);
5036
5071
  updateTextEditingChrome(target, originalMinWidth, originalMinHeight);
5037
- postElementSelect(target, e);
5038
- (window.parent as Window).postMessage(
5039
- { type: "element-dblclick-text", payload: getElementInfo(target) },
5040
- "*",
5041
- );
5072
+ if (!programmaticTextEdit) {
5073
+ postElementSelect(target, e);
5074
+ (window.parent as Window).postMessage(
5075
+ { type: "element-dblclick-text", payload: getElementInfo(target) },
5076
+ "*",
5077
+ );
5078
+ }
5042
5079
  postTextEditingState(target, true);
5043
5080
 
5044
5081
  function finish(commit) {
@@ -5053,9 +5090,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5053
5090
  document.removeEventListener("selectionchange", onSelectionChange);
5054
5091
  target.removeAttribute("contenteditable");
5055
5092
  target.removeAttribute("data-agent-native-text-editing");
5093
+ document.documentElement.removeAttribute(
5094
+ "data-agent-native-empty-text-editing",
5095
+ );
5056
5096
  target.style.cursor = "";
5057
- target.style.outline = "";
5058
- target.style.outlineOffset = "";
5097
+ target.style.outline = originalOutline;
5098
+ target.style.outlineOffset = originalOutlineOffset;
5059
5099
  target.style.minWidth = originalMinWidth;
5060
5100
  target.style.minHeight = originalMinHeight;
5061
5101
  target.style.borderColor = originalBorderColor;
@@ -5077,6 +5117,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5077
5117
  }
5078
5118
 
5079
5119
  function onBlur() {
5120
+ if (programmaticTextEdit && !(target.textContent || "").trim()) {
5121
+ window.setTimeout(function () {
5122
+ if (committed || (target.textContent || "").trim()) return;
5123
+ target.focus();
5124
+ updateTextEditingChrome(target, originalMinWidth, originalMinHeight);
5125
+ postTextEditingState(target, true);
5126
+ }, 0);
5127
+ return;
5128
+ }
5080
5129
  finish(true);
5081
5130
  }
5082
5131
 
@@ -5302,6 +5351,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5302
5351
  clientX: bteCenterX,
5303
5352
  clientY: bteCenterY,
5304
5353
  target: textTarget,
5354
+ agentNativeProgrammaticTextEdit: true,
5305
5355
  preventDefault: function () {},
5306
5356
  stopPropagation: function () {},
5307
5357
  stopImmediatePropagation: function () {},
@@ -248,6 +248,8 @@ export function canvasPrimitiveReactStyle(
248
248
  // For text kind, clear fill-as-background since text uses `color`
249
249
  if (kind === "text") {
250
250
  style.background = "transparent";
251
+ style.outline = "none";
252
+ style.outlineOffset = 0;
251
253
  }
252
254
 
253
255
  return style;
@@ -25,6 +25,8 @@ export interface InspectorAiActionsProps {
25
25
  fileId?: string;
26
26
  /** Active file name (e.g. "index.html"). */
27
27
  filename?: string;
28
+ /** Localhost-backed source file, when the selected screen comes from a local app. */
29
+ routeSourceFile?: string;
28
30
  /** The design id. */
29
31
  designId?: string;
30
32
  /** When false, the controls are disabled. */
@@ -43,6 +45,7 @@ export function InspectorAiActions({
43
45
  sourceId,
44
46
  fileId,
45
47
  filename,
48
+ routeSourceFile,
46
49
  designId,
47
50
  canEdit,
48
51
  }: InspectorAiActionsProps) {
@@ -57,6 +60,7 @@ export function InspectorAiActions({
57
60
  sourceId,
58
61
  fileId,
59
62
  filename,
63
+ routeSourceFile,
60
64
  designId,
61
65
  };
62
66
  const disabled = !canEdit || !request.trim();
@@ -41,7 +41,7 @@ export function useQuestionFlow(
41
41
  formattedAnswers,
42
42
  "",
43
43
  designId
44
- ? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, then continue from the kept screen; otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
44
+ ? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, call get-design-snapshot with fileId for the kept screen, then continue from that single file in a bounded pass. Prefer edit-design; if generate-design is needed, update only the kept file compactly and stop after the first successful save. Otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
45
45
  : "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
46
46
  ]
47
47
  .filter(Boolean)
@@ -79,7 +79,7 @@ export function useQuestionFlow(
79
79
  formattedAnswers,
80
80
  "",
81
81
  designId
82
- ? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, then continue from the kept screen; otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
82
+ ? "Now continue the design. Honor any answer about variations: if the user asked to explore options, call present-design-variants with 2-5 concise directions using label, description, accentColor, and feature bullets; omit large content HTML when needed because the action can render compact representative screens - wait for their chat pick, delete the unchosen variant screens, call get-design-snapshot with fileId for the kept screen, then continue from that single file in a bounded pass. Prefer edit-design; if generate-design is needed, update only the kept file compactly and stop after the first successful save. Otherwise call generate-design with one complete, renderable index.html first. Do not ask another question unless a required decision is still genuinely missing."
83
83
  : "Now continue the design. Honor any answer about variations: use variants only if requested; otherwise generate one polished direction.",
84
84
  ]
85
85
  .filter(Boolean)
@@ -26,7 +26,7 @@ export interface AgentEditRequestArgs {
26
26
  * same pattern used by the tweaks panel (DesignEditor.tsx ~5293) and the
27
27
  * extensions panel context builder (DesignExtensionsPanel.tsx ~120-131).
28
28
  */
29
- function buildEditContext(args: AgentEditRequestArgs): string {
29
+ export function buildEditContext(args: AgentEditRequestArgs): string {
30
30
  const lines: string[] = [];
31
31
 
32
32
  if (args.designId) {
@@ -77,7 +77,7 @@ function buildEditContext(args: AgentEditRequestArgs): string {
77
77
  * Builds the full pasteable prompt string (visible message + context block)
78
78
  * for use in the "Copy prompt" flow.
79
79
  */
80
- function buildFullPrompt(args: AgentEditRequestArgs): string {
80
+ export function buildFullPrompt(args: AgentEditRequestArgs): string {
81
81
  const context = buildEditContext(args);
82
82
  return `${args.message}\n\n---\n${context}`;
83
83
  }
@@ -291,7 +291,7 @@ const enUS = {
291
291
  clickToRename: "Click to rename",
292
292
  collaborators: "Collaborators",
293
293
  share: "Share",
294
- signUpToSave: "Sign up to save",
294
+ signUpToSave: "Sign up free to save",
295
295
  signUpToSaveDescription:
296
296
  "Sign up for a free account to save designs, screen layouts, and generate new ones.",
297
297
  signUpToShare: "Sign up to share",