@agent-native/core 0.106.0 → 0.106.2

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 (104) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/artifact-response.ts +84 -0
  5. package/corpus/core/src/agent/thread-data-builder.ts +1 -1
  6. package/corpus/core/src/application-state/index.ts +2 -0
  7. package/corpus/core/src/application-state/script-helpers.ts +12 -0
  8. package/corpus/core/src/application-state/store.ts +38 -0
  9. package/corpus/core/src/client/review/ReviewCommentComposer.tsx +10 -3
  10. package/corpus/core/src/integrations/webhook-handler.ts +41 -5
  11. package/corpus/core/src/server/agent-chat-plugin.ts +7 -2
  12. package/corpus/templates/clips/app/components/capture-install-options.tsx +60 -5
  13. package/corpus/templates/clips/app/components/library/library-layout.tsx +9 -1
  14. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +4 -4
  15. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  16. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  17. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  18. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  19. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  20. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  21. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  22. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  23. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  24. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  25. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  26. package/corpus/templates/clips/app/lib/capture-install-options.ts +89 -4
  27. package/corpus/templates/clips/app/routes/_app.dictate.tsx +10 -1
  28. package/corpus/templates/clips/app/routes/record.tsx +1 -0
  29. package/corpus/templates/clips/app/routes/share.$shareId.tsx +7 -0
  30. package/corpus/templates/clips/changelog/2026-07-16-the-desktop-app-button-now-shows-open-desktop-app-and-launch.md +6 -0
  31. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +5 -0
  32. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +1 -0
  33. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +41 -6
  34. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +5 -0
  35. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  36. package/corpus/templates/content/changelog/2026-07-16-slack-corrections-now-preserve-newer-content-values-unless-y.md +6 -0
  37. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +22 -0
  38. package/corpus/templates/design/AGENTS.md +16 -0
  39. package/corpus/templates/design/actions/cancel-node-rewrite-request.ts +54 -0
  40. package/corpus/templates/design/actions/propose-node-rewrite.ts +290 -0
  41. package/corpus/templates/design/actions/resolve-node-rewrite.ts +257 -0
  42. package/corpus/templates/design/actions/view-screen.ts +98 -2
  43. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +71 -1
  44. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +19 -0
  45. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +33 -0
  46. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +194 -0
  47. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +21 -4
  48. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +18 -0
  49. package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -0
  50. package/corpus/templates/design/app/components/visual-editor/NodeRewriteProposal.tsx +638 -0
  51. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +535 -54
  52. package/corpus/templates/design/app/components/visual-editor/index.ts +5 -1
  53. package/corpus/templates/design/app/components/visual-editor/review-canvas-state.ts +21 -3
  54. package/corpus/templates/design/app/i18n/ar-SA.ts +35 -0
  55. package/corpus/templates/design/app/i18n/de-DE.ts +35 -0
  56. package/corpus/templates/design/app/i18n/es-ES.ts +35 -0
  57. package/corpus/templates/design/app/i18n/fr-FR.ts +35 -0
  58. package/corpus/templates/design/app/i18n/hi-IN.ts +35 -0
  59. package/corpus/templates/design/app/i18n/ja-JP.ts +35 -0
  60. package/corpus/templates/design/app/i18n/ko-KR.ts +35 -0
  61. package/corpus/templates/design/app/i18n/pt-BR.ts +35 -0
  62. package/corpus/templates/design/app/i18n/zh-CN.ts +35 -0
  63. package/corpus/templates/design/app/i18n/zh-TW.ts +32 -0
  64. package/corpus/templates/design/app/i18n-data.ts +32 -0
  65. package/corpus/templates/design/app/lib/node-reprompt.ts +110 -0
  66. package/corpus/templates/design/changelog/2026-07-16-design-editing-and-review-are-now-more-predictable.md +6 -0
  67. package/corpus/templates/design/server/lib/reprompt-action-guard.ts +107 -0
  68. package/corpus/templates/design/server/plugins/agent-chat.ts +9 -1
  69. package/corpus/templates/design/shared/code-layer.ts +15 -0
  70. package/corpus/templates/design/shared/node-rewrite.ts +213 -0
  71. package/corpus/templates/design/shared/review-anchor.ts +26 -3
  72. package/dist/a2a/artifact-response.d.ts +9 -0
  73. package/dist/a2a/artifact-response.d.ts.map +1 -1
  74. package/dist/a2a/artifact-response.js +68 -0
  75. package/dist/a2a/artifact-response.js.map +1 -1
  76. package/dist/agent/thread-data-builder.js +1 -1
  77. package/dist/agent/thread-data-builder.js.map +1 -1
  78. package/dist/application-state/index.d.ts +2 -2
  79. package/dist/application-state/index.d.ts.map +1 -1
  80. package/dist/application-state/index.js +2 -2
  81. package/dist/application-state/index.js.map +1 -1
  82. package/dist/application-state/script-helpers.d.ts +1 -0
  83. package/dist/application-state/script-helpers.d.ts.map +1 -1
  84. package/dist/application-state/script-helpers.js +7 -1
  85. package/dist/application-state/script-helpers.js.map +1 -1
  86. package/dist/application-state/store.d.ts +1 -0
  87. package/dist/application-state/store.d.ts.map +1 -1
  88. package/dist/application-state/store.js +28 -0
  89. package/dist/application-state/store.js.map +1 -1
  90. package/dist/client/review/ReviewCommentComposer.d.ts +4 -1
  91. package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -1
  92. package/dist/client/review/ReviewCommentComposer.js +3 -3
  93. package/dist/client/review/ReviewCommentComposer.js.map +1 -1
  94. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  95. package/dist/integrations/webhook-handler.js +24 -5
  96. package/dist/integrations/webhook-handler.js.map +1 -1
  97. package/dist/notifications/routes.d.ts +2 -2
  98. package/dist/resources/handlers.d.ts +1 -1
  99. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  100. package/dist/server/agent-chat-plugin.js +3 -1
  101. package/dist/server/agent-chat-plugin.js.map +1 -1
  102. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  103. package/dist/server/transcribe-voice.d.ts +1 -1
  104. package/package.json +1 -1
@@ -92,6 +92,7 @@ export type CanvasContextMenuAction =
92
92
  | "add-auto-layout"
93
93
  | "suggest-auto-layout"
94
94
  | "create-component"
95
+ | "reprompt"
95
96
  | "go-to-main-component"
96
97
  | "swap-instance"
97
98
  | "detach-instance"
@@ -135,6 +136,7 @@ export type CanvasContextMenuActionHandler = (
135
136
 
136
137
  export interface CanvasContextMenuLabels {
137
138
  selectLayer: string;
139
+ reprompt: string;
138
140
  pasteHere: string;
139
141
  selectAll: string;
140
142
  zoomToFit: string;
@@ -231,6 +233,10 @@ export interface CanvasContextMenuProps {
231
233
  selectedCount?: number;
232
234
  layerCandidates?: readonly CanvasLayerHitCandidate[];
233
235
  onSelectLayer?: (candidate: CanvasLayerHitCandidate) => void;
236
+ onRepromptLayer?: (
237
+ candidate: CanvasLayerHitCandidate,
238
+ details: CanvasContextMenuActionDetails,
239
+ ) => void;
234
240
  hasClipboard?: boolean;
235
241
  hasPropsClipboard?: boolean;
236
242
  hasAnimationClipboard?: boolean;
@@ -261,6 +267,7 @@ export interface CanvasContextMenuProps {
261
267
  canAddAutoLayout?: boolean;
262
268
  canSuggestAutoLayout?: boolean;
263
269
  canCreateComponent?: boolean;
270
+ canReprompt?: boolean;
264
271
  // Whether the current selection IS a component instance — gates the
265
272
  // whole Go to main component / Swap instance / Detach instance cluster on
266
273
  // (rather than showing them permanently disabled for non-instance
@@ -330,6 +337,7 @@ export interface CanvasContextMenuProps {
330
337
  onAddAutoLayout?: CanvasContextMenuActionHandler;
331
338
  onSuggestAutoLayout?: CanvasContextMenuActionHandler;
332
339
  onCreateComponent?: CanvasContextMenuActionHandler;
340
+ onReprompt?: CanvasContextMenuActionHandler;
333
341
  onGoToMainComponent?: CanvasContextMenuActionHandler;
334
342
  onSwapInstance?: CanvasContextMenuActionHandler;
335
343
  onDetachInstance?: CanvasContextMenuActionHandler;
@@ -361,6 +369,7 @@ export interface CanvasContextMenuProps {
361
369
 
362
370
  const DEFAULT_LABELS: CanvasContextMenuLabels = {
363
371
  selectLayer: "Select layer",
372
+ reprompt: "Regenerate…",
364
373
  pasteHere: "Paste here",
365
374
  selectAll: "Select all",
366
375
  zoomToFit: "Zoom to fit",
@@ -481,6 +490,7 @@ export const CanvasContextMenu = forwardRef<
481
490
  selectedCount = 0,
482
491
  layerCandidates = [],
483
492
  onSelectLayer,
493
+ onRepromptLayer,
484
494
  hasClipboard = false,
485
495
  hasPropsClipboard = false,
486
496
  hasAnimationClipboard = false,
@@ -500,6 +510,7 @@ export const CanvasContextMenu = forwardRef<
500
510
  canAddAutoLayout = selectedCount > 0,
501
511
  canSuggestAutoLayout = false,
502
512
  canCreateComponent = selectedCount > 0,
513
+ canReprompt = selectedCount > 0 || layerCandidates.length > 0,
503
514
  isComponentInstance = false,
504
515
  canGoToMainComponent = isComponentInstance,
505
516
  canSwapInstance = isComponentInstance,
@@ -540,6 +551,7 @@ export const CanvasContextMenu = forwardRef<
540
551
  onAddAutoLayout,
541
552
  onSuggestAutoLayout,
542
553
  onCreateComponent,
554
+ onReprompt,
543
555
  onGoToMainComponent,
544
556
  onSwapInstance,
545
557
  onDetachInstance,
@@ -624,6 +636,7 @@ export const CanvasContextMenu = forwardRef<
624
636
  "add-auto-layout": onAddAutoLayout,
625
637
  "suggest-auto-layout": onSuggestAutoLayout,
626
638
  "create-component": onCreateComponent,
639
+ reprompt: onReprompt,
627
640
  "go-to-main-component": onGoToMainComponent,
628
641
  "swap-instance": onSwapInstance,
629
642
  "detach-instance": onDetachInstance,
@@ -654,6 +667,7 @@ export const CanvasContextMenu = forwardRef<
654
667
  onCopyAsSvg,
655
668
  onCopyProps,
656
669
  onCreateComponent,
670
+ onReprompt,
657
671
  onDetachInstance,
658
672
  onFlipHorizontal,
659
673
  onFlipVertical,
@@ -772,6 +786,62 @@ export const CanvasContextMenu = forwardRef<
772
786
  <CanvasMenuSeparator />
773
787
  </>
774
788
  ) : null}
789
+ {canReprompt && (onReprompt || onRepromptLayer) ? (
790
+ <>
791
+ <ContextMenuGroup>
792
+ {layerCandidates.length > 1 && onRepromptLayer ? (
793
+ <ContextMenuSub>
794
+ <ContextMenuSubTrigger className={MENU_SUB_TRIGGER_CLASS}>
795
+ {labels.reprompt}
796
+ </ContextMenuSubTrigger>
797
+ <ContextMenuSubContent
798
+ className={cn(MENU_CONTENT_CLASS, "w-56")}
799
+ >
800
+ {layerCandidates.map((candidate) => (
801
+ <CanvasLayerCandidateItem
802
+ key={`reprompt:${candidate.key}`}
803
+ candidate={candidate}
804
+ onSelect={(event) => {
805
+ onRepromptLayer(candidate, {
806
+ action: "reprompt",
807
+ point,
808
+ selectedCount,
809
+ originalEvent: event,
810
+ });
811
+ handleOpenChange(false);
812
+ }}
813
+ />
814
+ ))}
815
+ </ContextMenuSubContent>
816
+ </ContextMenuSub>
817
+ ) : (
818
+ <CanvasMenuItem
819
+ hidden={isHiddenAction("reprompt")}
820
+ disabled={
821
+ !canReprompt ||
822
+ (!onReprompt &&
823
+ !(onRepromptLayer && layerCandidates.length === 1))
824
+ }
825
+ label={labels.reprompt}
826
+ onSelect={(event) => {
827
+ const candidate = layerCandidates[0];
828
+ if (!onReprompt && onRepromptLayer && candidate) {
829
+ onRepromptLayer(candidate, {
830
+ action: "reprompt",
831
+ point,
832
+ selectedCount,
833
+ originalEvent: event,
834
+ });
835
+ return;
836
+ }
837
+ runAction("reprompt", event);
838
+ }}
839
+ />
840
+ )}
841
+ </ContextMenuGroup>
842
+ <CanvasMenuSeparator />
843
+ </>
844
+ ) : null}
775
845
  {hasSelection ? (
776
846
  <>
777
847
  {/* LIVE-VERIFIED Figma "with selection" canvas menu. */}
@@ -1083,7 +1153,7 @@ function CanvasLayerCandidateItem({
1083
1153
  onSelect,
1084
1154
  }: {
1085
1155
  candidate: CanvasLayerHitCandidate;
1086
- onSelect: () => void;
1156
+ onSelect: (event: Event) => void;
1087
1157
  }) {
1088
1158
  const tag = candidate.info.tagName.toLowerCase();
1089
1159
  const Icon = candidate.info.componentName
@@ -24,6 +24,7 @@ import {
24
24
  type PenPath,
25
25
  type PenPoint,
26
26
  } from "@shared/pen-path";
27
+ import { sourceContentHash } from "@shared/source-workspace";
27
28
  import { IconPlugConnectedX, IconRefresh } from "@tabler/icons-react";
28
29
  import {
29
30
  useCallback,
@@ -43,6 +44,7 @@ import { Button } from "@/components/ui/button";
43
44
  import {
44
45
  DrawOverlay as SharedDrawOverlay,
45
46
  ReviewCanvasPins,
47
+ type RepromptDraftRequest,
46
48
  type ReviewFocusRequest,
47
49
  } from "@/components/visual-editor";
48
50
  import { sendToDesignAgentChatAndConfirm } from "@/lib/agent-chat";
@@ -556,6 +558,11 @@ interface DesignCanvasProps {
556
558
  previewFrameId?: string;
557
559
  /** Human-readable label for comment-pin prompts. */
558
560
  commentContextLabel?: string;
561
+ /** Opens an ephemeral, pre-anchored regenerate composer for this screen. */
562
+ repromptDraftRequest?: RepromptDraftRequest | null;
563
+ onRepromptDraftConsumed?: (nonce: number) => void;
564
+ /** Marks the one base canvas used for pending rewrite previews. */
565
+ nodeRewriteCanvasTarget?: boolean;
559
566
  /**
560
567
  * Called when a link inside the prototype points to another screen (a
561
568
  * relative href or `data-screen`). Lets the editor switch the active screen
@@ -1053,6 +1060,9 @@ export function DesignCanvas({
1053
1060
  commentContextId,
1054
1061
  screenId,
1055
1062
  previewFrameId,
1063
+ repromptDraftRequest,
1064
+ onRepromptDraftConsumed,
1065
+ nodeRewriteCanvasTarget = false,
1056
1066
  onPrototypeNavigate,
1057
1067
  motionTracks,
1058
1068
  motionDefaultEase,
@@ -4027,6 +4037,9 @@ export function DesignCanvas({
4027
4037
  const iframeElement = (
4028
4038
  <div
4029
4039
  data-review-canvas-id={reviewCanvasId}
4040
+ data-node-rewrite-canvas-target={
4041
+ nodeRewriteCanvasTarget ? "true" : undefined
4042
+ }
4030
4043
  className="design-canvas-iframe-wrapper relative inline-block ring-1 ring-border/60 shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.45)]"
4031
4044
  onDragEnter={handleWrapperDragEnter}
4032
4045
  onDragOver={handleWrapperDragOver}
@@ -4548,6 +4561,12 @@ export function DesignCanvas({
4548
4561
  onDispatchCommentToAgent={onDispatchCommentToAgent}
4549
4562
  onSendThreadToAgent={onSendThreadToAgent}
4550
4563
  sendingThreadId={reviewSendingThreadId}
4564
+ sourceType={
4565
+ sourceType ?? (externalPreviewUrl ? "localhost" : "inline")
4566
+ }
4567
+ sourceVersionHash={sourceContentHash(content)}
4568
+ repromptDraftRequest={repromptDraftRequest}
4569
+ onRepromptDraftConsumed={onRepromptDraftConsumed}
4551
4570
  />
4552
4571
  ) : null}
4553
4572
  </div>
@@ -398,6 +398,8 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
398
398
  hiddenScreenIds = EMPTY_SCREEN_IDS,
399
399
  lockedScreenIds = EMPTY_SCREEN_IDS,
400
400
  fullViewScreenIds,
401
+ pendingReviewScreenIds = EMPTY_SCREEN_IDS,
402
+ onReviewPendingScreen,
401
403
  interactMode = false,
402
404
  activeScreenHasHoveredChild = false,
403
405
  hoveredChildScreenId,
@@ -525,6 +527,10 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
525
527
  () => new Set(lockedScreenIds),
526
528
  [lockedScreenIds],
527
529
  );
530
+ const pendingReviewScreenIdSet = useMemo(
531
+ () => new Set(pendingReviewScreenIds),
532
+ [pendingReviewScreenIds],
533
+ );
528
534
  const renderedScreens = useMemo(
529
535
  () => screens.filter((screen) => !hiddenScreenIdSet.has(screen.id)),
530
536
  [hiddenScreenIdSet, screens],
@@ -7609,6 +7615,8 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
7609
7615
  !isBreakpointSelectionTarget(screen)
7610
7616
  }
7611
7617
  showFullView={fullViewIdSet.has(screen.id)}
7618
+ pendingReview={pendingReviewScreenIdSet.has(screen.id)}
7619
+ onReviewPendingScreen={onReviewPendingScreen}
7612
7620
  interactMode={interactMode}
7613
7621
  isDirectlyHovered={screen.id === directlyHoveredScreenId}
7614
7622
  isFileDragOver={
@@ -8898,6 +8906,8 @@ interface ScreenProps {
8898
8906
  isSelected: boolean;
8899
8907
  isTopScreen: boolean;
8900
8908
  showFullView: boolean;
8909
+ pendingReview: boolean;
8910
+ onReviewPendingScreen?: (screenId: string) => void;
8901
8911
  interactMode: boolean;
8902
8912
  isDirectlyHovered: boolean;
8903
8913
  /** True while a native OS file drag is hovering this frame (Figma parity §1). */
@@ -8966,6 +8976,8 @@ const Screen = memo(function Screen({
8966
8976
  isSelected,
8967
8977
  isTopScreen,
8968
8978
  showFullView,
8979
+ pendingReview,
8980
+ onReviewPendingScreen,
8969
8981
  interactMode,
8970
8982
  isDirectlyHovered,
8971
8983
  isFileDragOver,
@@ -9174,6 +9186,27 @@ const Screen = memo(function Screen({
9174
9186
  >
9175
9187
  {display}
9176
9188
  </span>
9189
+ {pendingReview && onReviewPendingScreen ? (
9190
+ <button
9191
+ type="button"
9192
+ data-node-rewrite-review-badge
9193
+ className="flex h-5 shrink-0 items-center gap-1 rounded-full border border-border bg-background/95 px-1.5 !text-[9px] font-medium text-foreground shadow-sm hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
9194
+ title={t("designEditor.nodeRewrite.reviewCandidate")}
9195
+ aria-label={t("designEditor.nodeRewrite.reviewCandidate")}
9196
+ onClick={(event) => {
9197
+ event.preventDefault();
9198
+ event.stopPropagation();
9199
+ onReviewPendingScreen(screen.id);
9200
+ }}
9201
+ onMouseDown={(event) => {
9202
+ event.preventDefault();
9203
+ event.stopPropagation();
9204
+ }}
9205
+ >
9206
+ <span className="size-1.5 rounded-full bg-primary" />
9207
+ {t("designEditor.nodeRewrite.reviewCandidate")}
9208
+ </button>
9209
+ ) : null}
9177
9210
  {metadata.source === "fusion" ? (
9178
9211
  <span
9179
9212
  data-frame-source-badge="fusion"
@@ -2168,6 +2168,17 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
2168
2168
 
2169
2169
  var selectedEl: Element | null = null;
2170
2170
  var hoveredEl: Element | null = null;
2171
+ type NodeHtmlPreviewSession = {
2172
+ proposalId: string;
2173
+ originalElement: Element;
2174
+ originalOuterHTML: string;
2175
+ startMarker: Comment;
2176
+ endMarker: Comment;
2177
+ currentElement: Element;
2178
+ selectedWasInside: boolean;
2179
+ hoveredWasInside: boolean;
2180
+ };
2181
+ var activeNodeHtmlPreview: NodeHtmlPreviewSession | null = null;
2171
2182
  // Last element an "element-hover" message was actually posted for. Lets
2172
2183
  // the shield's pointermove handler skip getLightElementInfo's two
2173
2184
  // getComputedStyle calls plus the postMessage on every one of the dozens
@@ -5120,6 +5131,178 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
5120
5131
  return null;
5121
5132
  }
5122
5133
 
5134
+ function parseNodeHtmlPreviewElement(html: string): Element | null {
5135
+ var trimmed = html.trim();
5136
+ if (/^<body(?:\s|>)/i.test(trimmed)) {
5137
+ var parsedDocument = new DOMParser().parseFromString(
5138
+ "<!doctype html><html><head></head>" + trimmed + "</html>", // i18n-ignore parser scaffold
5139
+ "text/html",
5140
+ );
5141
+ return parsedDocument.body;
5142
+ }
5143
+ var template = document.createElement("template");
5144
+ template.innerHTML = html;
5145
+ var element = template.content.firstElementChild;
5146
+ if (!element || template.content.childElementCount !== 1) return null;
5147
+ for (
5148
+ var child = template.content.firstChild;
5149
+ child;
5150
+ child = child.nextSibling
5151
+ ) {
5152
+ if (child === element) continue;
5153
+ if (child.nodeType !== 3 || String(child.textContent || "").trim()) {
5154
+ return null;
5155
+ }
5156
+ }
5157
+ return element;
5158
+ }
5159
+
5160
+ function resolveNodeHtmlPreviewTarget(target: unknown): Element | null {
5161
+ if (!target || typeof target !== "object") return null;
5162
+ var nodeId = (target as { nodeId?: unknown }).nodeId;
5163
+ if (typeof nodeId === "string" && nodeId) {
5164
+ try {
5165
+ var nodeMatch = document.querySelector(
5166
+ '[data-agent-native-node-id="' + escapeAttribute(nodeId) + '"]',
5167
+ );
5168
+ if (nodeMatch && !isLayerInteractionBlocked(nodeMatch)) {
5169
+ return nodeMatch;
5170
+ }
5171
+ } catch (_err) {}
5172
+ }
5173
+ var selector = (target as { selector?: unknown }).selector;
5174
+ if (typeof selector !== "string" || !selector) return null;
5175
+ try {
5176
+ var selectorMatch = document.querySelector(selector);
5177
+ return selectorMatch && !isLayerInteractionBlocked(selectorMatch)
5178
+ ? selectorMatch
5179
+ : null;
5180
+ } catch (_err) {
5181
+ return null;
5182
+ }
5183
+ }
5184
+
5185
+ function postNodeHtmlPreviewApplied(proposalId: string): void {
5186
+ (window.parent as Window).postMessage(
5187
+ {
5188
+ type: "agent-native:node-html-preview-applied",
5189
+ proposalId: proposalId,
5190
+ },
5191
+ "*",
5192
+ );
5193
+ }
5194
+
5195
+ function replaceNodeHtmlPreviewElement(
5196
+ session: NodeHtmlPreviewSession,
5197
+ nextElement: Element,
5198
+ ): boolean {
5199
+ var parent = session.startMarker.parentNode;
5200
+ if (!parent || session.endMarker.parentNode !== parent) return false;
5201
+ var cursor = session.startMarker.nextSibling;
5202
+ while (cursor && cursor !== session.endMarker) {
5203
+ var next = cursor.nextSibling;
5204
+ parent.removeChild(cursor);
5205
+ cursor = next;
5206
+ }
5207
+ if (cursor !== session.endMarker) return false;
5208
+ nextElement.setAttribute(
5209
+ "data-agent-native-node-rewrite-proposal",
5210
+ session.proposalId,
5211
+ );
5212
+ parent.insertBefore(nextElement, session.endMarker);
5213
+ session.currentElement = nextElement;
5214
+ if (session.selectedWasInside) selectedEl = nextElement;
5215
+ if (session.hoveredWasInside) hoveredEl = nextElement;
5216
+ refreshOverlays();
5217
+ return true;
5218
+ }
5219
+
5220
+ function restoreActiveNodeHtmlPreview(proposalId?: string): boolean {
5221
+ var session = activeNodeHtmlPreview;
5222
+ if (!session || (proposalId && session.proposalId !== proposalId)) {
5223
+ return false;
5224
+ }
5225
+ activeNodeHtmlPreview = null;
5226
+ var parent = session.startMarker.parentNode;
5227
+ if (!parent || session.endMarker.parentNode !== parent) return false;
5228
+ var cursor = session.startMarker.nextSibling;
5229
+ while (cursor && cursor !== session.endMarker) {
5230
+ var next = cursor.nextSibling;
5231
+ parent.removeChild(cursor);
5232
+ cursor = next;
5233
+ }
5234
+ if (cursor !== session.endMarker) return false;
5235
+ parent.insertBefore(session.originalElement, session.endMarker);
5236
+ parent.removeChild(session.startMarker);
5237
+ parent.removeChild(session.endMarker);
5238
+ if (session.selectedWasInside) selectedEl = session.originalElement;
5239
+ if (session.hoveredWasInside) hoveredEl = session.originalElement;
5240
+ refreshOverlays();
5241
+ return session.originalElement.outerHTML === session.originalOuterHTML;
5242
+ }
5243
+
5244
+ function applyNodeHtmlPreview(data: {
5245
+ proposalId?: unknown;
5246
+ target?: unknown;
5247
+ html?: unknown;
5248
+ }): void {
5249
+ var proposalId = data.proposalId;
5250
+ if (
5251
+ typeof proposalId !== "string" ||
5252
+ !proposalId ||
5253
+ typeof data.html !== "string"
5254
+ ) {
5255
+ return;
5256
+ }
5257
+ var nextElement = parseNodeHtmlPreviewElement(data.html);
5258
+ if (!nextElement) return;
5259
+ if (
5260
+ activeNodeHtmlPreview &&
5261
+ activeNodeHtmlPreview.proposalId === proposalId
5262
+ ) {
5263
+ if (replaceNodeHtmlPreviewElement(activeNodeHtmlPreview, nextElement)) {
5264
+ postNodeHtmlPreviewApplied(proposalId);
5265
+ }
5266
+ return;
5267
+ }
5268
+ if (activeNodeHtmlPreview) restoreActiveNodeHtmlPreview();
5269
+ var target = resolveNodeHtmlPreviewTarget(data.target);
5270
+ if (!target || !target.parentNode || target === document.documentElement) {
5271
+ return;
5272
+ }
5273
+ var originalOuterHTML = target.outerHTML;
5274
+ nextElement.setAttribute(
5275
+ "data-agent-native-node-rewrite-proposal",
5276
+ proposalId,
5277
+ );
5278
+ var startMarker = document.createComment(
5279
+ "agent-native:node-html-preview:start",
5280
+ );
5281
+ var endMarker = document.createComment(
5282
+ "agent-native:node-html-preview:end",
5283
+ );
5284
+ var parent = target.parentNode;
5285
+ var selectedWasInside = !!selectedEl && target.contains(selectedEl);
5286
+ var hoveredWasInside = !!hoveredEl && target.contains(hoveredEl);
5287
+ parent.insertBefore(startMarker, target);
5288
+ parent.insertBefore(endMarker, target.nextSibling);
5289
+ parent.replaceChild(nextElement, target);
5290
+ activeNodeHtmlPreview = {
5291
+ proposalId: proposalId,
5292
+ originalElement: target,
5293
+ originalOuterHTML: originalOuterHTML,
5294
+ startMarker: startMarker,
5295
+ endMarker: endMarker,
5296
+ currentElement: nextElement,
5297
+ selectedWasInside: selectedWasInside,
5298
+ hoveredWasInside: hoveredWasInside,
5299
+ };
5300
+ if (selectedWasInside) selectedEl = nextElement;
5301
+ if (hoveredWasInside) hoveredEl = nextElement;
5302
+ refreshOverlays();
5303
+ postNodeHtmlPreviewApplied(proposalId);
5304
+ }
5305
+
5123
5306
  // Host-driven Layers-panel moves must never inherit findRuntimeTarget's
5124
5307
  // selected-element shortcut or first-match querySelector behavior. Runtime
5125
5308
  // React trees routinely repeat classes and component markup, so a selector
@@ -11070,6 +11253,7 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
11070
11253
  return;
11071
11254
  }
11072
11255
  if (e.data.type === "replace-document-content") {
11256
+ activeNodeHtmlPreview = null;
11073
11257
  replaceRuntimeDocument(
11074
11258
  e.data.content,
11075
11259
  e.data.forceFullDocument ? "" : e.data.selectedSelector,
@@ -11079,6 +11263,16 @@ declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
11079
11263
  );
11080
11264
  return;
11081
11265
  }
11266
+ if (e.data.type === "node-html-preview") {
11267
+ if (e.data.operation === "restore") {
11268
+ if (typeof e.data.proposalId === "string") {
11269
+ restoreActiveNodeHtmlPreview(e.data.proposalId);
11270
+ }
11271
+ } else if (e.data.operation === "preview") {
11272
+ applyNodeHtmlPreview(e.data);
11273
+ }
11274
+ return;
11275
+ }
11082
11276
  if (e.data.type === "delete-element") {
11083
11277
  removeRuntimeTarget(e.data.selector, e.data.selectorCandidates);
11084
11278
  return;
@@ -739,9 +739,20 @@
739
739
  ): Element | null {
740
740
  var element = elementFromEditorPoint(clientX, clientY);
741
741
  if (!element) return null;
742
- return element.closest(
742
+ var identifiedAncestor = element.closest(
743
743
  "[data-agent-native-node-id],[data-code-layer-id],[data-layer-id],[data-builder-id],[id]",
744
744
  );
745
+ if (
746
+ identifiedAncestor &&
747
+ identifiedAncestor !== document.body &&
748
+ identifiedAncestor !== document.documentElement
749
+ ) {
750
+ return identifiedAncestor;
751
+ }
752
+ if (element === document.body || element === document.documentElement) {
753
+ return null;
754
+ }
755
+ return element;
745
756
  }
746
757
 
747
758
  function reviewNodeElements(nodeIds: string[]): Record<string, Element> {
@@ -808,14 +819,20 @@
808
819
  reviewPointX,
809
820
  reviewPointY,
810
821
  );
822
+ var reviewPointNodeId = reviewPointElement
823
+ ? getNodeId(reviewPointElement)
824
+ : "";
825
+ var reviewPointSelector =
826
+ reviewPointElement && !reviewPointNodeId
827
+ ? buildSourceEquivalentSelector(reviewPointElement)
828
+ : "";
811
829
  try {
812
830
  (window.parent as Window).postMessage(
813
831
  {
814
832
  type: "agent-native:review-anchor-at-point-result",
815
833
  correlationId: reviewPointCorrelationId,
816
- nodeId: reviewPointElement
817
- ? getNodeId(reviewPointElement) || undefined
818
- : undefined,
834
+ nodeId: reviewPointNodeId || undefined,
835
+ targetSelector: reviewPointSelector || undefined,
819
836
  layerName:
820
837
  reviewPointElement?.getAttribute(
821
838
  "data-agent-native-layer-name",
@@ -23,3 +23,21 @@ export interface IframeContextMenuPayload {
23
23
  info?: ElementInfo | null;
24
24
  layerCandidates?: CanvasLayerHitCandidate[];
25
25
  }
26
+
27
+ export interface IframeNodeHtmlPreviewTarget {
28
+ nodeId?: string;
29
+ selector?: string;
30
+ }
31
+
32
+ export interface IframeNodeHtmlPreviewMessage {
33
+ type: "node-html-preview";
34
+ proposalId: string;
35
+ target: IframeNodeHtmlPreviewTarget;
36
+ operation: "preview" | "restore";
37
+ html?: string;
38
+ }
39
+
40
+ export interface IframeNodeHtmlPreviewAppliedPayload {
41
+ type: "agent-native:node-html-preview-applied";
42
+ proposalId: string;
43
+ }
@@ -130,6 +130,10 @@ export interface MultiScreenCanvasProps {
130
130
  * transformed directly from the overview canvas. */
131
131
  lockedScreenIds?: ReadonlySet<string> | readonly string[];
132
132
  fullViewScreenIds?: string[];
133
+ /** Screens with generated candidates waiting for explicit review. */
134
+ pendingReviewScreenIds?: ReadonlySet<string> | readonly string[];
135
+ /** Opens candidate review for one pending screen. */
136
+ onReviewPendingScreen?: (screenId: string) => void;
133
137
  /** Lets every live frame receive native pointer interaction while the
134
138
  * overview camera and frame chrome remain available. */
135
139
  interactMode?: boolean;