@agent-native/core 0.80.11 → 0.81.0

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 (225) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +47 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/design-connect.ts +419 -19
  5. package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
  6. package/corpus/core/src/client/chat/index.ts +1 -0
  7. package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
  8. package/corpus/core/src/client/chat-view-transition.ts +33 -2
  9. package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
  10. package/corpus/core/src/client/index.ts +1 -0
  11. package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
  12. package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
  13. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
  14. package/corpus/core/src/db/client.ts +29 -2
  15. package/corpus/core/src/provider-api/index.ts +29 -0
  16. package/corpus/core/src/server/core-routes-plugin.ts +58 -20
  17. package/corpus/core/src/styles/agent-native.css +54 -0
  18. package/corpus/core/src/usage/store.ts +2 -2
  19. package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
  20. package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
  21. package/corpus/templates/analytics/app/global.css +8 -0
  22. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
  23. package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
  24. package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
  25. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
  26. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
  27. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
  28. package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
  29. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
  30. package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
  31. package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
  32. package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
  33. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
  34. package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
  35. package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
  36. package/corpus/templates/assets/app/global.css +15 -0
  37. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
  38. package/corpus/templates/assets/app/routes/library.tsx +90 -16
  39. package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
  40. package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
  41. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
  42. package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
  43. package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
  44. package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
  45. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  46. package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
  47. package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
  48. package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
  49. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
  50. package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
  51. package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
  52. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
  53. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
  54. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
  55. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
  56. package/corpus/templates/design/AGENTS.md +27 -0
  57. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
  58. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
  59. package/corpus/templates/design/actions/apply-component-prop-edit.ts +63 -20
  60. package/corpus/templates/design/actions/apply-motion-edit.ts +67 -69
  61. package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
  62. package/corpus/templates/design/actions/capture-design-state.ts +27 -4
  63. package/corpus/templates/design/actions/connect-localhost.ts +59 -22
  64. package/corpus/templates/design/actions/export-zip.ts +23 -8
  65. package/corpus/templates/design/actions/generate-design.ts +53 -1
  66. package/corpus/templates/design/actions/get-component-details.ts +5 -19
  67. package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
  68. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
  69. package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
  70. package/corpus/templates/design/actions/index-design-tokens.ts +10 -1
  71. package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
  72. package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
  73. package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
  74. package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
  75. package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
  76. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
  77. package/corpus/templates/design/actions/navigate.ts +18 -3
  78. package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
  79. package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
  80. package/corpus/templates/design/actions/provider-api-request.ts +3 -3
  81. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
  82. package/corpus/templates/design/actions/run-design-audit.ts +53 -4
  83. package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
  84. package/corpus/templates/design/actions/view-screen.ts +1 -1
  85. package/corpus/templates/design/actions/write-local-file.ts +213 -0
  86. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +230 -3308
  87. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
  88. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +776 -287
  89. package/corpus/templates/design/app/components/design/EditPanel.tsx +212 -208
  90. package/corpus/templates/design/app/components/design/LayersPanel.tsx +14 -2
  91. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
  92. package/corpus/templates/design/app/components/design/MotionDock.tsx +235 -266
  93. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +490 -55
  94. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +43 -62
  95. package/corpus/templates/design/app/components/design/StatesPanel.tsx +34 -61
  96. package/corpus/templates/design/app/components/design/TokensPanel.tsx +251 -0
  97. package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
  98. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
  99. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
  100. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
  101. package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
  102. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
  103. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
  104. package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
  105. package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
  106. package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
  107. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
  108. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
  109. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
  110. package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
  111. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  112. package/corpus/templates/design/app/global.css +7 -0
  113. package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
  114. package/corpus/templates/design/app/i18n/zh-TW.ts +21 -0
  115. package/corpus/templates/design/app/i18n-data.ts +358 -0
  116. package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
  117. package/corpus/templates/design/app/pages/DesignEditor.tsx +2402 -499
  118. package/corpus/templates/design/app/pages/Index.tsx +5 -1
  119. package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
  120. package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
  121. package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
  122. package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
  123. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
  124. package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
  125. package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
  126. package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
  127. package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
  128. package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
  129. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
  130. package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
  131. package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
  132. package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
  133. package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
  134. package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
  135. package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
  136. package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
  137. package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
  138. package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
  139. package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
  140. package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
  141. package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
  142. package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
  143. package/corpus/templates/design/e2e/helpers.ts +168 -65
  144. package/corpus/templates/design/package.json +3 -1
  145. package/corpus/templates/design/server/db/schema.ts +23 -0
  146. package/corpus/templates/design/server/handlers/uploads.ts +22 -1
  147. package/corpus/templates/design/server/lib/provider-api.ts +7 -4
  148. package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
  149. package/corpus/templates/design/server/plugins/db.ts +39 -0
  150. package/corpus/templates/design/server/register-secrets.ts +38 -0
  151. package/corpus/templates/design/shared/board-file.ts +228 -0
  152. package/corpus/templates/design/shared/board-objects.ts +288 -0
  153. package/corpus/templates/design/shared/code-layer.ts +52 -0
  154. package/corpus/templates/design/shared/motion-compiler.ts +58 -7
  155. package/corpus/templates/design/shared/motion-timeline.ts +2 -2
  156. package/corpus/templates/design/tsconfig.json +2 -1
  157. package/corpus/templates/slides/AGENTS.md +9 -0
  158. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
  159. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
  160. package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
  161. package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
  162. package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
  163. package/corpus/templates/slides/app/vite-env.d.ts +18 -0
  164. package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
  165. package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
  166. package/corpus/templates/slides/package.json +1 -0
  167. package/corpus/templates/slides/vite.config.ts +1 -0
  168. package/dist/cli/design-connect.d.ts +25 -0
  169. package/dist/cli/design-connect.d.ts.map +1 -1
  170. package/dist/cli/design-connect.js +357 -18
  171. package/dist/cli/design-connect.js.map +1 -1
  172. package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
  173. package/dist/client/ConnectBuilderCard.js +1 -0
  174. package/dist/client/ConnectBuilderCard.js.map +1 -1
  175. package/dist/client/chat/index.d.ts +1 -1
  176. package/dist/client/chat/index.d.ts.map +1 -1
  177. package/dist/client/chat/index.js +1 -1
  178. package/dist/client/chat/index.js.map +1 -1
  179. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  180. package/dist/client/chat/run-recovery.js +5 -5
  181. package/dist/client/chat/run-recovery.js.map +1 -1
  182. package/dist/client/chat-view-transition.d.ts +6 -0
  183. package/dist/client/chat-view-transition.d.ts.map +1 -1
  184. package/dist/client/chat-view-transition.js +23 -2
  185. package/dist/client/chat-view-transition.js.map +1 -1
  186. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  187. package/dist/client/composer/PromptComposer.js +3 -2
  188. package/dist/client/composer/PromptComposer.js.map +1 -1
  189. package/dist/client/index.d.ts +1 -1
  190. package/dist/client/index.d.ts.map +1 -1
  191. package/dist/client/index.js +1 -1
  192. package/dist/client/index.js.map +1 -1
  193. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  194. package/dist/client/org/OrgSwitcher.js +11 -4
  195. package/dist/client/org/OrgSwitcher.js.map +1 -1
  196. package/dist/client/sharing/ShareButton.js +3 -3
  197. package/dist/client/sharing/ShareButton.js.map +1 -1
  198. package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
  199. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  200. package/dist/client/use-agent-chat-home-handoff.js +20 -6
  201. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  202. package/dist/collab/awareness.d.ts +2 -2
  203. package/dist/collab/awareness.d.ts.map +1 -1
  204. package/dist/collab/routes.d.ts +2 -2
  205. package/dist/db/client.d.ts +1 -0
  206. package/dist/db/client.d.ts.map +1 -1
  207. package/dist/db/client.js +28 -2
  208. package/dist/db/client.js.map +1 -1
  209. package/dist/notifications/routes.d.ts +3 -3
  210. package/dist/observability/routes.d.ts +2 -2
  211. package/dist/progress/routes.d.ts +1 -1
  212. package/dist/provider-api/index.d.ts +5 -4
  213. package/dist/provider-api/index.d.ts.map +1 -1
  214. package/dist/provider-api/index.js +28 -0
  215. package/dist/provider-api/index.js.map +1 -1
  216. package/dist/resources/handlers.d.ts +3 -3
  217. package/dist/server/core-routes-plugin.d.ts +25 -0
  218. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  219. package/dist/server/core-routes-plugin.js +43 -18
  220. package/dist/server/core-routes-plugin.js.map +1 -1
  221. package/dist/server/transcribe-voice.d.ts +1 -1
  222. package/dist/styles/agent-native.css +54 -0
  223. package/dist/usage/store.js +2 -2
  224. package/dist/usage/store.js.map +1 -1
  225. package/package.json +1 -1
@@ -0,0 +1,4037 @@
1
+ // @ts-nocheck -- extracted from the inline editor bridge; typed cleanup follows after the migration lands.
2
+ /**
3
+ * Editor chrome bridge — injected into the live-edit canvas iframe.
4
+ *
5
+ * This file is the TypeScript source for the editor chrome bridge that was
6
+ * previously inlined as a template literal in DesignCanvas.tsx. It compiles
7
+ * to a self-contained IIFE string via bridge/codegen.ts.
8
+ *
9
+ * Runtime placeholders (replaced by DesignCanvas.tsx before injection):
10
+ * __READ_ONLY__ — boolean literal "true"/"false"
11
+ * __TEXT_EDITING_ENABLED__ — boolean literal "true"/"false"
12
+ * __EDITOR_CHROME_SCALE_X__ — number string, e.g. "1.5"
13
+ * __EDITOR_CHROME_SCALE_Y__ — number string, e.g. "1.5"
14
+ *
15
+ * Rules:
16
+ * • No import/require of any module (DOM globals only).
17
+ * • No references to outer/module scope (the code runs inside an iframe).
18
+ * • Wrap everything in a self-executing IIFE.
19
+ *
20
+ * keep in sync with hit-test.bridge.ts for the shared container/axis/placement
21
+ * helpers (search for "// keep in sync" comments).
22
+ */
23
+ declare var __READ_ONLY__: boolean;
24
+ declare var __TEXT_EDITING_ENABLED__: boolean;
25
+ declare var __EDITOR_CHROME_SCALE_X__: string;
26
+ declare var __EDITOR_CHROME_SCALE_Y__: string;
27
+
28
+ (function () {
29
+ var readOnly = __READ_ONLY__;
30
+ var textEditingEnabled = !readOnly && __TEXT_EDITING_ENABLED__;
31
+ var scaleToolEnabled = false;
32
+ var editorChromeScaleX = Math.max(
33
+ 0.05,
34
+ Number(__EDITOR_CHROME_SCALE_X__) || 1,
35
+ );
36
+ var editorChromeScaleY = Math.max(
37
+ 0.05,
38
+ Number(__EDITOR_CHROME_SCALE_Y__) || editorChromeScaleX,
39
+ );
40
+
41
+ // Ease the constant-size selection chrome to its new size when overview zoom
42
+ // settles (parent posts set-editor-chrome-scale), matching the canvas chrome.
43
+ // Only chrome-scale-driven props animate; the overlay's live position is excluded.
44
+ (function () {
45
+ var chromeTransitionStyle = document.createElement("style");
46
+ chromeTransitionStyle.textContent =
47
+ '[data-agent-native-edit-overlay="selection"]{transition:border-width 150ms ease-out}' +
48
+ "[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}" +
49
+ "[data-agent-native-spacing-line]{position:absolute;display:none;pointer-events:none;border-radius:999px}" +
50
+ "[data-agent-native-spacing-region]{position:absolute;display:none;box-sizing:border-box;pointer-events:auto;background-size:6px 6px}" +
51
+ '[data-agent-native-spacing-region][data-orientation="vertical"]{cursor:ew-resize}' +
52
+ '[data-agent-native-spacing-region][data-orientation="horizontal"]{cursor:ns-resize}';
53
+ (document.head || document.documentElement).appendChild(
54
+ chromeTransitionStyle,
55
+ );
56
+ })();
57
+
58
+ function chromeScaleX(): number {
59
+ return 1 / Math.max(0.05, editorChromeScaleX);
60
+ }
61
+
62
+ function chromeScaleY(): number {
63
+ return 1 / Math.max(0.05, editorChromeScaleY);
64
+ }
65
+
66
+ function chromeLineScale(): number {
67
+ return 1 / Math.max(0.05, Math.max(editorChromeScaleX, editorChromeScaleY));
68
+ }
69
+
70
+ function syncEditorChromeScaleVars(): void {
71
+ document.documentElement.style.setProperty(
72
+ "--agent-native-editor-chrome-scale-x",
73
+ String(chromeScaleX()),
74
+ );
75
+ document.documentElement.style.setProperty(
76
+ "--agent-native-editor-chrome-scale-y",
77
+ String(chromeScaleY()),
78
+ );
79
+ document.documentElement.style.setProperty(
80
+ "--agent-native-editor-chrome-line-scale",
81
+ String(chromeLineScale()),
82
+ );
83
+ }
84
+
85
+ function escapeIdent(value: unknown): string {
86
+ if (window.CSS && typeof window.CSS.escape === "function") {
87
+ return window.CSS.escape(String(value));
88
+ }
89
+ return String(value).replace(/[^a-zA-Z0-9_-]/g, "\\$&");
90
+ }
91
+
92
+ function escapeAttribute(value: unknown): string {
93
+ return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
94
+ }
95
+
96
+ function attributeSelector(el: Element | null, name: string): string {
97
+ var value = el && el.getAttribute && el.getAttribute(name);
98
+ return value ? "[" + name + '="' + escapeAttribute(value) + '"]' : "";
99
+ }
100
+
101
+ function classSelectorSuffix(el: Element | null, maxCount: number): string {
102
+ if (!el || !el.classList) return "";
103
+ return Array.prototype.slice
104
+ .call(el.classList, 0, maxCount)
105
+ .map(function (token) {
106
+ return "." + escapeIdent(token);
107
+ })
108
+ .join("");
109
+ }
110
+
111
+ function selectorPart(el: Element | null): string {
112
+ if (!el || !el.tagName) return "";
113
+ var stableSelector =
114
+ attributeSelector(el, "data-agent-native-node-id") ||
115
+ attributeSelector(el, "data-code-layer-id") ||
116
+ attributeSelector(el, "data-layer-id") ||
117
+ attributeSelector(el, "data-builder-id") ||
118
+ attributeSelector(el, "data-loc");
119
+ if (stableSelector) return el.tagName.toLowerCase() + stableSelector;
120
+ if (el.id) return "#" + escapeIdent(el.id);
121
+ var part =
122
+ el.tagName.toLowerCase() + (stableSelector || classSelectorSuffix(el, 2));
123
+ var parent = el.parentElement;
124
+ if (parent) {
125
+ var sameTag = Array.prototype.filter.call(
126
+ parent.children,
127
+ function (child) {
128
+ return child.tagName === el.tagName;
129
+ },
130
+ );
131
+ if (sameTag.length > 1) {
132
+ part += ":nth-of-type(" + (sameTag.indexOf(el) + 1) + ")";
133
+ }
134
+ }
135
+ return part;
136
+ }
137
+
138
+ function selectorPath(el: Element | null, stopEl?: Element | null): string {
139
+ var parts = [];
140
+ var node = el;
141
+ while (node && node.nodeType === 1) {
142
+ if (node !== stopEl) parts.unshift(selectorPart(node));
143
+ if (node === stopEl) break;
144
+ node = node.parentElement;
145
+ }
146
+ return parts.slice(-5).join(" > ");
147
+ }
148
+
149
+ function getSourceId(el: Element | null): string {
150
+ if (!el || !el.getAttribute) return "";
151
+ return (
152
+ el.getAttribute("data-agent-native-node-id") ||
153
+ el.getAttribute("data-code-layer-id") ||
154
+ el.getAttribute("data-layer-id") ||
155
+ el.getAttribute("data-builder-id") ||
156
+ el.getAttribute("data-loc") ||
157
+ el.id ||
158
+ ""
159
+ );
160
+ }
161
+
162
+ function isDocumentRootElement(el: Element | null): boolean {
163
+ return el === document.body || el === document.documentElement;
164
+ }
165
+
166
+ function closestStableSourceElement(el: Element | null): Element | null {
167
+ if (!el || !el.closest) return null;
168
+ var stable = el.closest(
169
+ "[data-agent-native-node-id],[data-code-layer-id],[data-layer-id],[data-builder-id],[data-loc]",
170
+ );
171
+ if (!stable || isDocumentRootElement(stable)) return null;
172
+ return stable;
173
+ }
174
+
175
+ function hasStableOwnSource(el: Element | null): boolean {
176
+ return !!(el && !isDocumentRootElement(el) && getSourceId(el));
177
+ }
178
+
179
+ function selectionTargetForHit(hit: Element | null): Element | null {
180
+ if (!hit || isDocumentRootElement(hit)) return hit;
181
+ if (selectedEl && hit !== selectedEl && selectedEl.contains(hit))
182
+ return hit;
183
+ if (hasStableOwnSource(hit)) return hit;
184
+ return closestStableSourceElement(hit) || hit;
185
+ }
186
+
187
+ function freshRuntimeNodeId(prefix: string): string {
188
+ var random = "";
189
+ try {
190
+ if (window.crypto && window.crypto.getRandomValues) {
191
+ var bytes = new Uint32Array(2);
192
+ window.crypto.getRandomValues(bytes);
193
+ random = Array.prototype.map
194
+ .call(bytes, function (part: number) {
195
+ return part.toString(36);
196
+ })
197
+ .join("");
198
+ }
199
+ } catch (_err) {}
200
+ if (!random)
201
+ random = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
202
+ return "an-" + String(prefix || "copy") + "-" + random;
203
+ }
204
+
205
+ function resetRuntimeStableIds(root: Element | null): void {
206
+ if (!root || !root.querySelectorAll) return;
207
+ var nodes = [root].concat(
208
+ Array.prototype.slice.call(
209
+ root.querySelectorAll("[data-agent-native-node-id]"),
210
+ ),
211
+ );
212
+ nodes.forEach(function (node, index) {
213
+ if (node && node.setAttribute) {
214
+ node.setAttribute(
215
+ "data-agent-native-node-id",
216
+ freshRuntimeNodeId(index === 0 ? "copy" : "copy-child"),
217
+ );
218
+ }
219
+ });
220
+ }
221
+
222
+ function getSelector(el: Element | null): string {
223
+ if (!el) return "";
224
+ var stableOwnSelector =
225
+ attributeSelector(el, "data-agent-native-node-id") ||
226
+ attributeSelector(el, "data-code-layer-id") ||
227
+ attributeSelector(el, "data-layer-id") ||
228
+ attributeSelector(el, "data-builder-id") ||
229
+ attributeSelector(el, "data-loc");
230
+ if (stableOwnSelector) return stableOwnSelector;
231
+
232
+ if (el.id) return "#" + escapeIdent(el.id);
233
+ var stableAncestor = closestStableSourceElement(el);
234
+ if (stableAncestor && stableAncestor !== el) {
235
+ var stableAncestorSelector = selectorPart(stableAncestor);
236
+ if (stableAncestorSelector) {
237
+ var descendantPath = selectorPath(el, stableAncestor);
238
+ var descendantParts = descendantPath ? descendantPath.split(" > ") : [];
239
+ if (descendantParts.length) {
240
+ return stableAncestorSelector + " > " + descendantParts.join(" > ");
241
+ }
242
+ return stableAncestorSelector;
243
+ }
244
+ }
245
+
246
+ return selectorPath(el);
247
+ }
248
+
249
+ function getElementInfo(el: Element): unknown {
250
+ var cs = window.getComputedStyle(el);
251
+ var rect = el.getBoundingClientRect();
252
+ var parentStyles = el.parentElement
253
+ ? window.getComputedStyle(el.parentElement)
254
+ : null;
255
+ var parentDisplay = parentStyles ? parentStyles.display : undefined;
256
+ var sourceBacked =
257
+ hasStableOwnSource(el) || !!closestStableSourceElement(el);
258
+ var sourceId = sourceBacked ? getSourceId(el) || getSelector(el) : "";
259
+ var parentLayout = parentStyles
260
+ ? {
261
+ display: parentStyles.display,
262
+ flexDirection: parentStyles.flexDirection,
263
+ alignItems: parentStyles.alignItems,
264
+ justifyContent: parentStyles.justifyContent,
265
+ gap: parentStyles.gap,
266
+ gridTemplateColumns: parentStyles.gridTemplateColumns,
267
+ gridTemplateRows: parentStyles.gridTemplateRows,
268
+ position: parentStyles.position,
269
+ }
270
+ : undefined;
271
+ var capabilities = sourceBacked
272
+ ? [
273
+ {
274
+ kind: "deterministic-style-edit",
275
+ label: "deterministic-style-edit",
276
+ confidence: 0.92,
277
+ reason:
278
+ "Inline style can be patched and replayed through HMR/collab.",
279
+ },
280
+ ]
281
+ : [
282
+ {
283
+ kind: "unsupported",
284
+ label: "runtime-only-element",
285
+ confidence: 0.3,
286
+ reason: "This runtime node is not anchored to a source code layer.",
287
+ },
288
+ ];
289
+ if (sourceBacked && el.classList && el.classList.length > 0) {
290
+ capabilities.push({
291
+ kind: "deterministic-class-edit",
292
+ label: "deterministic-class-edit",
293
+ confidence: 0.78,
294
+ reason: "Class tokens are visible on the selected element.",
295
+ });
296
+ }
297
+ if (
298
+ sourceBacked &&
299
+ (parentDisplay === "flex" ||
300
+ parentDisplay === "inline-flex" ||
301
+ parentDisplay === "grid" ||
302
+ parentDisplay === "inline-grid")
303
+ ) {
304
+ capabilities.push({
305
+ kind: "agent-structural-edit",
306
+ label: "agent-structural-edit",
307
+ confidence: 0.54,
308
+ reason:
309
+ "Parent layout context decides whether movement means gap, order, alignment, or wrapper structure.",
310
+ });
311
+ }
312
+ // --- provenance: read source-location attributes when present ---
313
+ // These are emitted by connected apps via @vitejs/plugin-react jsxDEV or a
314
+ // Babel source plugin. Cross-origin localhost iframes cannot be read (CSP /
315
+ // same-origin policy), so this will be undefined in that case — expected.
316
+ var provenance:
317
+ | {
318
+ sourceFile?: string;
319
+ line?: number;
320
+ column?: number;
321
+ component?: string;
322
+ }
323
+ | undefined = undefined;
324
+ var dataSourceFile = el.getAttribute("data-source-file");
325
+ var dataSourceLine = el.getAttribute("data-source-line");
326
+ var dataSourceColumn = el.getAttribute("data-source-column");
327
+ var dataComponentName = el.getAttribute("data-component-name");
328
+ var dataLoc = el.getAttribute("data-loc");
329
+ // data-loc may encode "file:line:col" (Babel source plugin convention).
330
+ // Only parse it when data-source-file is absent, to avoid double-reads.
331
+ if (!dataSourceFile && dataLoc) {
332
+ var lastColonIndex = dataLoc.lastIndexOf(":");
333
+ var lastPart =
334
+ lastColonIndex >= 0 ? dataLoc.slice(lastColonIndex + 1) : "";
335
+ if (lastColonIndex >= 0 && /^\d+$/.test(lastPart)) {
336
+ var beforeLastPart = dataLoc.slice(0, lastColonIndex);
337
+ var previousColonIndex = beforeLastPart.lastIndexOf(":");
338
+ var previousPart =
339
+ previousColonIndex >= 0
340
+ ? beforeLastPart.slice(previousColonIndex + 1)
341
+ : "";
342
+ var hasColumn = /^\d+$/.test(previousPart);
343
+ dataSourceFile = hasColumn
344
+ ? beforeLastPart.slice(0, previousColonIndex)
345
+ : beforeLastPart;
346
+ dataSourceLine = hasColumn ? previousPart : lastPart;
347
+ if (hasColumn) dataSourceColumn = lastPart;
348
+ }
349
+ }
350
+ if (
351
+ dataSourceFile ||
352
+ dataSourceLine ||
353
+ dataSourceColumn ||
354
+ dataComponentName
355
+ ) {
356
+ provenance = {};
357
+ if (dataSourceFile) provenance.sourceFile = dataSourceFile;
358
+ if (dataSourceLine) {
359
+ var ln = parseInt(dataSourceLine, 10);
360
+ if (!isNaN(ln)) provenance.line = ln;
361
+ }
362
+ if (dataSourceColumn) {
363
+ var col = parseInt(dataSourceColumn, 10);
364
+ if (!isNaN(col)) provenance.column = col;
365
+ }
366
+ if (dataComponentName) provenance.component = dataComponentName;
367
+ }
368
+ return {
369
+ tagName: el.tagName.toLowerCase(),
370
+ id: el.id || undefined,
371
+ sourceId: sourceId,
372
+ selector: getSelector(el),
373
+ classes: Array.from(el.classList),
374
+ computedStyles: {
375
+ color: cs.color,
376
+ backgroundColor: cs.backgroundColor,
377
+ backgroundImage: cs.backgroundImage,
378
+ backgroundPosition: cs.backgroundPosition,
379
+ backgroundRepeat: cs.backgroundRepeat,
380
+ backgroundSize: cs.backgroundSize,
381
+ backgroundBlendMode: cs.backgroundBlendMode,
382
+ fontSize: cs.fontSize,
383
+ fontFamily: cs.fontFamily,
384
+ fontWeight: cs.fontWeight,
385
+ lineHeight: cs.lineHeight,
386
+ letterSpacing: cs.letterSpacing,
387
+ textAlign: cs.textAlign,
388
+ display: cs.display,
389
+ overflow: cs.overflow,
390
+ flexDirection: cs.flexDirection,
391
+ justifyContent: cs.justifyContent,
392
+ alignItems: cs.alignItems,
393
+ alignSelf: cs.alignSelf,
394
+ flexGrow: cs.flexGrow,
395
+ flexShrink: cs.flexShrink,
396
+ flexBasis: cs.flexBasis,
397
+ order: cs.order,
398
+ gridColumn: cs.gridColumn,
399
+ gridRow: cs.gridRow,
400
+ position: cs.position,
401
+ top: cs.top,
402
+ right: cs.right,
403
+ bottom: cs.bottom,
404
+ left: cs.left,
405
+ gap: cs.gap,
406
+ width: cs.width,
407
+ height: cs.height,
408
+ opacity: cs.opacity,
409
+ paddingTop: cs.paddingTop,
410
+ paddingRight: cs.paddingRight,
411
+ paddingBottom: cs.paddingBottom,
412
+ paddingLeft: cs.paddingLeft,
413
+ marginTop: cs.marginTop,
414
+ marginRight: cs.marginRight,
415
+ marginBottom: cs.marginBottom,
416
+ marginLeft: cs.marginLeft,
417
+ borderWidth: cs.borderWidth,
418
+ borderStyle: cs.borderStyle,
419
+ borderColor: cs.borderColor,
420
+ borderRadius: cs.borderRadius,
421
+ borderTopLeftRadius: cs.borderTopLeftRadius,
422
+ borderTopRightRadius: cs.borderTopRightRadius,
423
+ borderBottomRightRadius: cs.borderBottomRightRadius,
424
+ borderBottomLeftRadius: cs.borderBottomLeftRadius,
425
+ outlineWidth: cs.outlineWidth,
426
+ outlineStyle: cs.outlineStyle,
427
+ outlineColor: cs.outlineColor,
428
+ outlineOffset: cs.outlineOffset,
429
+ boxShadow: cs.boxShadow,
430
+ textShadow: cs.textShadow,
431
+ filter: cs.filter,
432
+ mixBlendMode: cs.mixBlendMode,
433
+ zIndex: cs.zIndex,
434
+ },
435
+ boundingRect: {
436
+ x: rect.x + (window.scrollX || window.pageXOffset || 0),
437
+ y: rect.y + (window.scrollY || window.pageYOffset || 0),
438
+ width: rect.width,
439
+ height: rect.height,
440
+ },
441
+ textContent: el.textContent ? el.textContent.slice(0, 200) : undefined,
442
+ htmlContent:
443
+ el.innerHTML && el.innerHTML !== el.textContent
444
+ ? el.innerHTML.slice(0, 4000)
445
+ : undefined,
446
+ isFlexContainer: cs.display === "flex" || cs.display === "inline-flex",
447
+ isFlexChild: parentDisplay === "flex" || parentDisplay === "inline-flex",
448
+ parentDisplay: parentDisplay,
449
+ parentLayout: parentLayout,
450
+ editCapabilities: capabilities,
451
+ confidence: capabilities.reduce(function (best, item) {
452
+ return Math.max(best, item.confidence || 0);
453
+ }, 0),
454
+ provenance: provenance,
455
+ };
456
+ }
457
+
458
+ var shieldOverlay = document.createElement("div");
459
+ shieldOverlay.setAttribute("data-agent-native-edit-overlay", "shield");
460
+ shieldOverlay.style.cssText =
461
+ "position:fixed;inset:0;z-index:99990;background:transparent;pointer-events:auto;touch-action:none;cursor:default;";
462
+ document.body.appendChild(shieldOverlay);
463
+
464
+ var highlightOverlay = document.createElement("div");
465
+ highlightOverlay.setAttribute("data-agent-native-edit-overlay", "highlight");
466
+ highlightOverlay.style.cssText =
467
+ "position:fixed;pointer-events:none;z-index:99997;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;";
468
+ document.body.appendChild(highlightOverlay);
469
+
470
+ var selectionOverlay = document.createElement("div");
471
+ selectionOverlay.setAttribute("data-agent-native-edit-overlay", "selection");
472
+ selectionOverlay.style.cssText =
473
+ "position:fixed;pointer-events:none;z-index:99998;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;cursor:default;";
474
+ ["n", "e", "s", "w"].forEach(function (pos) {
475
+ var edge = document.createElement("span");
476
+ edge.setAttribute("data-agent-native-edge-handle", pos);
477
+ var cursor = pos === "n" || pos === "s" ? "ns-resize" : "ew-resize";
478
+ edge.style.cssText =
479
+ "position:absolute;pointer-events:auto;cursor:" +
480
+ cursor +
481
+ ";background:transparent;";
482
+ if (pos === "n") {
483
+ edge.style.left = "0";
484
+ edge.style.right = "0";
485
+ edge.style.top = "-5px";
486
+ edge.style.height = "10px";
487
+ }
488
+ if (pos === "s") {
489
+ edge.style.left = "0";
490
+ edge.style.right = "0";
491
+ edge.style.bottom = "-5px";
492
+ edge.style.height = "10px";
493
+ }
494
+ if (pos === "e") {
495
+ edge.style.top = "0";
496
+ edge.style.bottom = "0";
497
+ edge.style.right = "-5px";
498
+ edge.style.width = "10px";
499
+ }
500
+ if (pos === "w") {
501
+ edge.style.top = "0";
502
+ edge.style.bottom = "0";
503
+ edge.style.left = "-5px";
504
+ edge.style.width = "10px";
505
+ }
506
+ selectionOverlay.appendChild(edge);
507
+ });
508
+ ["nw", "ne", "se", "sw"].forEach(function (pos) {
509
+ var handle = document.createElement("span");
510
+ handle.setAttribute("data-agent-native-edit-handle", pos);
511
+ var cursor =
512
+ pos === "n" || pos === "s"
513
+ ? "ns-resize"
514
+ : pos === "e" || pos === "w"
515
+ ? "ew-resize"
516
+ : pos === "nw" || pos === "se"
517
+ ? "nwse-resize"
518
+ : "nesw-resize";
519
+ handle.style.cssText =
520
+ "position:absolute;z-index:1;width:7px;height:7px;border:1px solid var(--design-editor-accent-color);background:var(--design-editor-accent-contrast-color);box-sizing:border-box;border-radius:1px;pointer-events:auto;cursor:" +
521
+ cursor +
522
+ ";";
523
+ if (pos.indexOf("n") !== -1) handle.style.top = "-4px";
524
+ if (pos.indexOf("s") !== -1) handle.style.bottom = "-4px";
525
+ if (pos.indexOf("w") !== -1) handle.style.left = "-4px";
526
+ if (pos.indexOf("e") !== -1) handle.style.right = "-4px";
527
+ if (pos === "n" || pos === "s") {
528
+ handle.style.left = "50%";
529
+ handle.style.transform = "translateX(-50%)";
530
+ }
531
+ if (pos === "e" || pos === "w") {
532
+ handle.style.top = "50%";
533
+ handle.style.transform = "translateY(-50%)";
534
+ }
535
+ selectionOverlay.appendChild(handle);
536
+ });
537
+ ["nw", "ne", "se", "sw"].forEach(function (pos) {
538
+ var rotate = document.createElement("span");
539
+ rotate.setAttribute("data-agent-native-rotate-handle", pos);
540
+ rotate.style.cssText =
541
+ "position:absolute;width:18px;height:18px;border-radius:999px;pointer-events:auto;cursor:grab;";
542
+ if (pos.indexOf("n") !== -1) rotate.style.top = "-26px";
543
+ if (pos.indexOf("s") !== -1) rotate.style.bottom = "-26px";
544
+ if (pos.indexOf("w") !== -1) rotate.style.left = "-26px";
545
+ if (pos.indexOf("e") !== -1) rotate.style.right = "-26px";
546
+ selectionOverlay.appendChild(rotate);
547
+ });
548
+ var spacingOverlay = document.createElement("div");
549
+ spacingOverlay.setAttribute("data-agent-native-spacing-overlay", "");
550
+ spacingOverlay.style.cssText =
551
+ "position:absolute;inset:0;display:none;pointer-events:none;";
552
+ selectionOverlay.appendChild(spacingOverlay);
553
+ document.body.appendChild(selectionOverlay);
554
+
555
+ var transformBadge = document.createElement("div");
556
+ transformBadge.setAttribute("data-agent-native-transform-badge", "");
557
+ transformBadge.style.cssText =
558
+ "position:fixed;z-index:100000;display:none;pointer-events:none;border:1px solid hsl(var(--border));border-radius:4px;background:hsl(var(--background) / 0.96);color:hsl(var(--foreground));font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;padding:3px 5px;box-shadow:0 8px 20px color-mix(in srgb, hsl(var(--foreground)) 16%, transparent);";
559
+ document.body.appendChild(transformBadge);
560
+
561
+ var spacingBadge = document.createElement("div");
562
+ spacingBadge.setAttribute("data-agent-native-spacing-badge", "");
563
+ spacingBadge.style.cssText =
564
+ "position:fixed;z-index:100000;display:none;pointer-events:none;border-radius:3px;color:white;font:10px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700;padding:2px 4px;box-shadow:0 4px 14px rgba(0,0,0,0.18);";
565
+ document.body.appendChild(spacingBadge);
566
+
567
+ var insertionGuide = document.createElement("div");
568
+ insertionGuide.setAttribute("data-agent-native-insertion-guide", "");
569
+ insertionGuide.style.cssText =
570
+ "position:fixed;z-index:100000;display:none;pointer-events:none;background:var(--design-editor-accent-color);border-radius:999px;box-shadow:0 0 0 1px var(--design-editor-accent-color);";
571
+ document.body.appendChild(insertionGuide);
572
+
573
+ var measurementOverlay = document.createElement("div");
574
+ measurementOverlay.setAttribute("data-agent-native-measurement-overlay", "");
575
+ measurementOverlay.style.cssText =
576
+ "position:fixed;inset:0;z-index:100001;display:none;pointer-events:none;color:var(--design-editor-measure-color);font:11px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;";
577
+ document.body.appendChild(measurementOverlay);
578
+
579
+ // Component-instance tag: a small pill that floats above the selection
580
+ // outline whenever the selected element carries a data-agent-native-component
581
+ // attribute. Clicking it sends a 'component-source-jump' message to the
582
+ // parent so the editor can invoke open-component-source.
583
+ var componentTagOverlay = document.createElement("div");
584
+ componentTagOverlay.setAttribute(
585
+ "data-agent-native-edit-overlay",
586
+ "component-tag",
587
+ );
588
+ componentTagOverlay.style.cssText =
589
+ [
590
+ "position:fixed",
591
+ "z-index:100002",
592
+ "display:none",
593
+ "pointer-events:auto",
594
+ "cursor:pointer",
595
+ "padding:2px 6px",
596
+ "border-radius:4px",
597
+ "font:11px/1.6 ui-sans-serif,system-ui,sans-serif",
598
+ "white-space:nowrap",
599
+ "user-select:none",
600
+ "-webkit-user-select:none",
601
+ "background:var(--design-editor-accent-color)",
602
+ "color:var(--design-editor-accent-contrast-color)",
603
+ "box-shadow:0 1px 4px color-mix(in srgb,var(--design-editor-accent-color) 40%,transparent)",
604
+ "border:1px solid color-mix(in srgb,var(--design-editor-accent-strong-color) 60%,transparent)",
605
+ "outline:2px solid transparent",
606
+ "transition:opacity 0.1s",
607
+ ].join(";") + ";";
608
+ document.body.appendChild(componentTagOverlay);
609
+
610
+ componentTagOverlay.addEventListener("click", function (e) {
611
+ e.stopPropagation();
612
+ e.preventDefault();
613
+ var nodeId =
614
+ componentTagOverlay.getAttribute("data-component-node-id") || "";
615
+ var componentName =
616
+ componentTagOverlay.getAttribute("data-component-name") || "";
617
+ if (!nodeId || !componentName) return;
618
+ try {
619
+ (window.parent as Window).postMessage(
620
+ {
621
+ type: "component-source-jump",
622
+ nodeId: nodeId,
623
+ componentName: componentName,
624
+ },
625
+ "*",
626
+ );
627
+ } catch (_err) {}
628
+ });
629
+
630
+ function updateComponentTag(el: Element | null): void {
631
+ if (!el) {
632
+ clearComponentTag();
633
+ return;
634
+ }
635
+ var compName =
636
+ el.getAttribute && el.getAttribute("data-agent-native-component");
637
+ if (!compName) {
638
+ clearComponentTag();
639
+ return;
640
+ }
641
+ var nodeId =
642
+ el.getAttribute("data-agent-native-node-id") ||
643
+ el.getAttribute("data-code-layer-id") ||
644
+ el.getAttribute("data-layer-id") ||
645
+ el.id ||
646
+ "";
647
+ componentTagOverlay.textContent = compName + " →";
648
+ componentTagOverlay.setAttribute("data-component-node-id", nodeId);
649
+ componentTagOverlay.setAttribute("data-component-name", compName);
650
+
651
+ var rect = el.getBoundingClientRect();
652
+ var tagHeight = 22;
653
+ var tagTop = rect.top - tagHeight - 4;
654
+ if (tagTop < 4) tagTop = rect.top + 4;
655
+ componentTagOverlay.style.display = "block";
656
+ componentTagOverlay.style.left = rect.left + "px";
657
+ componentTagOverlay.style.top = tagTop + "px";
658
+ // Accent outline on the selection overlay to distinguish component roots.
659
+ selectionOverlay.style.outline =
660
+ "2px solid var(--design-editor-accent-strong-color)";
661
+ selectionOverlay.style.outlineOffset = "2px";
662
+ }
663
+
664
+ function clearComponentTag(): void {
665
+ componentTagOverlay.style.display = "none";
666
+ componentTagOverlay.removeAttribute("data-component-node-id");
667
+ componentTagOverlay.removeAttribute("data-component-name");
668
+ selectionOverlay.style.outline = "";
669
+ selectionOverlay.style.outlineOffset = "";
670
+ }
671
+
672
+ var selectedEl: Element | null = null;
673
+ var hoveredEl: Element | null = null;
674
+ var activeTextEditEl: HTMLElement | null = null;
675
+ var textEditPointerState: {
676
+ shield: string;
677
+ selection: string;
678
+ highlight: string;
679
+ } | null = null;
680
+ var pendingStructureMoves: Record<
681
+ string,
682
+ {
683
+ requestId: string;
684
+ el: Element;
685
+ target: { anchor: Element; placement: string; axis?: string };
686
+ origin: { prevParent: Element; prevNextSibling: Node | null } | null;
687
+ }
688
+ > = {};
689
+ var pendingShieldDrag: {
690
+ el: Element;
691
+ startX: number;
692
+ startY: number;
693
+ lastX: number;
694
+ lastY: number;
695
+ requestId: string;
696
+ offsetX: number;
697
+ offsetY: number;
698
+ originalPointerEvents: string;
699
+ lastReorder: { anchor: Element; placement: string; axis?: string } | null;
700
+ } | null = null;
701
+ var suppressNextShieldClick = false;
702
+ var suppressNextShieldClickTimer: ReturnType<typeof setTimeout> | null = null;
703
+ var selectedSpacingHovered = false;
704
+ var hoveredSpacingHandleKey = "";
705
+ var spacingHandleStateByKey: Record<string, { value: number }> = {};
706
+ var spacingHandleNodesByKey: Record<string, Element> = {};
707
+ var spacingOverlayRenderKey = "";
708
+ var spacingDrag: {
709
+ key: string;
710
+ groupKey: string;
711
+ property: string;
712
+ oppositeProperty: string;
713
+ side: string;
714
+ orientation: string;
715
+ baseValue: number;
716
+ baseOppositeValue: number;
717
+ startX: number;
718
+ startY: number;
719
+ el: Element;
720
+ } | null = null;
721
+ var lockedSelectors: string[] = [];
722
+ var hiddenSelectors: string[] = [];
723
+ var lastEditorPointWasBlocked = false;
724
+
725
+ function clearRuntimeSelection(): void {
726
+ selectedEl = null;
727
+ hoveredEl = null;
728
+ selectedSpacingHovered = false;
729
+ hoveredSpacingHandleKey = "";
730
+ spacingDrag = null;
731
+ selectionOverlay.style.display = "none";
732
+ highlightOverlay.style.display = "none";
733
+ hideSpacingOverlay();
734
+ hideMeasurements();
735
+ clearComponentTag();
736
+ }
737
+
738
+ function matchesSelectorList(
739
+ el: Element | null,
740
+ selectors: string[],
741
+ ): boolean {
742
+ if (!el || !selectors || selectors.length === 0) return false;
743
+ for (var i = 0; i < selectors.length; i += 1) {
744
+ try {
745
+ if (el.matches(selectors[i]) || el.closest(selectors[i])) return true;
746
+ } catch (_err) {}
747
+ }
748
+ return false;
749
+ }
750
+
751
+ function matchesExactSelectorList(
752
+ el: Element | null,
753
+ selectors: string[],
754
+ ): boolean {
755
+ if (!el || !selectors || selectors.length === 0) return false;
756
+ for (var i = 0; i < selectors.length; i += 1) {
757
+ try {
758
+ if (el.matches(selectors[i])) return true;
759
+ } catch (_err) {}
760
+ }
761
+ return false;
762
+ }
763
+
764
+ function isLayerInteractionBlocked(el: Element | null): boolean {
765
+ if (!el) return false;
766
+ if (
767
+ el.closest &&
768
+ el.closest(
769
+ '[data-agent-native-locked="true"], [data-agent-native-hidden="true"]',
770
+ )
771
+ ) {
772
+ return true;
773
+ }
774
+ return (
775
+ matchesSelectorList(el, lockedSelectors) ||
776
+ matchesSelectorList(el, hiddenSelectors)
777
+ );
778
+ }
779
+
780
+ function applyHiddenSelectors(): void {
781
+ document
782
+ .querySelectorAll("[data-agent-native-runtime-hidden]")
783
+ .forEach(function (el: HTMLElement) {
784
+ var previous = el.getAttribute("data-agent-native-previous-display");
785
+ if (previous === null) {
786
+ el.style.removeProperty("display");
787
+ } else {
788
+ el.style.display = previous;
789
+ }
790
+ el.removeAttribute("data-agent-native-runtime-hidden");
791
+ el.removeAttribute("data-agent-native-previous-display");
792
+ });
793
+ hiddenSelectors.forEach(function (selector) {
794
+ try {
795
+ document.querySelectorAll(selector).forEach(function (el) {
796
+ if (!el.hasAttribute("data-agent-native-runtime-hidden")) {
797
+ el.setAttribute(
798
+ "data-agent-native-previous-display",
799
+ el.style.display || "",
800
+ );
801
+ }
802
+ el.setAttribute("data-agent-native-runtime-hidden", "true");
803
+ el.style.display = "none";
804
+ });
805
+ } catch (_err) {}
806
+ });
807
+ }
808
+
809
+ function replaceRuntimeDocument(
810
+ html: string,
811
+ preferredSelector: string,
812
+ selectorCandidates: string[],
813
+ ): void {
814
+ if (typeof html !== "string") return;
815
+ if (activeTextEditEl) {
816
+ applyHiddenSelectors();
817
+ refreshOverlays();
818
+ return;
819
+ }
820
+ var parser = new DOMParser();
821
+ var nextDoc = parser.parseFromString(html, "text/html");
822
+ if (!nextDoc || !nextDoc.body) return;
823
+
824
+ var persistentNodes = Array.prototype.slice.call(
825
+ document.querySelectorAll("[data-agent-native-edit-overlay]"),
826
+ );
827
+ var activeSelector =
828
+ preferredSelector || (selectedEl ? getSelector(selectedEl) : "");
829
+ var activeCandidates: string[] = [];
830
+ if (Array.isArray(selectorCandidates)) {
831
+ selectorCandidates.forEach(function (selector) {
832
+ if (
833
+ typeof selector === "string" &&
834
+ selector &&
835
+ activeCandidates.indexOf(selector) === -1
836
+ ) {
837
+ activeCandidates.push(selector);
838
+ }
839
+ });
840
+ }
841
+ if (activeSelector && activeCandidates.indexOf(activeSelector) === -1) {
842
+ activeCandidates.push(activeSelector);
843
+ }
844
+
845
+ var nextHeadHtml = nextDoc.head ? nextDoc.head.innerHTML : "";
846
+ if (
847
+ nextHeadHtml === document.head.innerHTML &&
848
+ activeCandidates.length > 0
849
+ ) {
850
+ var currentMatch = null;
851
+ var nextMatch = null;
852
+ var matchedSelector = "";
853
+ var fallbackCurrentMatch = null;
854
+ var fallbackSelector = "";
855
+ for (
856
+ var matchIndex = 0;
857
+ matchIndex < activeCandidates.length;
858
+ matchIndex += 1
859
+ ) {
860
+ try {
861
+ var currentCandidate = document.querySelector(
862
+ activeCandidates[matchIndex],
863
+ );
864
+ var nextCandidate = nextDoc.querySelector(
865
+ activeCandidates[matchIndex],
866
+ );
867
+ if (currentCandidate && !fallbackCurrentMatch) {
868
+ fallbackCurrentMatch = currentCandidate;
869
+ fallbackSelector = activeCandidates[matchIndex];
870
+ }
871
+ if (currentCandidate && nextCandidate) {
872
+ currentMatch = currentCandidate;
873
+ nextMatch = nextCandidate;
874
+ matchedSelector = activeCandidates[matchIndex];
875
+ break;
876
+ }
877
+ } catch (_err) {
878
+ // Keep trying later aliases; bridge selectors can differ between
879
+ // runtime and DOMParser passes.
880
+ }
881
+ }
882
+ if (!currentMatch && fallbackCurrentMatch) {
883
+ currentMatch = fallbackCurrentMatch;
884
+ matchedSelector = fallbackSelector;
885
+ }
886
+ if (
887
+ currentMatch &&
888
+ currentMatch !== document.body &&
889
+ currentMatch !== document.documentElement &&
890
+ !isOverlayElement(currentMatch)
891
+ ) {
892
+ if (nextMatch) {
893
+ currentMatch.replaceWith(document.importNode(nextMatch, true));
894
+ } else if (
895
+ currentMatch !== document.body &&
896
+ currentMatch !== document.documentElement
897
+ ) {
898
+ if (
899
+ currentMatch.parentNode &&
900
+ currentMatch.parentNode.contains(currentMatch)
901
+ ) {
902
+ currentMatch.remove();
903
+ }
904
+ }
905
+ applyHiddenSelectors();
906
+ selectedEl = null;
907
+ if (nextMatch) {
908
+ try {
909
+ selectedEl = document.querySelector(matchedSelector);
910
+ } catch (_err) {}
911
+ }
912
+ hoveredEl = null;
913
+ if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
914
+ positionOverlay(selectionOverlay, selectedEl);
915
+ (window.parent as Window).postMessage(
916
+ { type: "element-select", payload: getElementInfo(selectedEl) },
917
+ "*",
918
+ );
919
+ } else {
920
+ selectionOverlay.style.display = "none";
921
+ }
922
+ highlightOverlay.style.display = "none";
923
+ hideMeasurements();
924
+ refreshOverlays();
925
+ return;
926
+ }
927
+ }
928
+ if (document.head.innerHTML !== nextHeadHtml) {
929
+ document.head.innerHTML = nextHeadHtml;
930
+ }
931
+ Array.prototype.slice.call(document.body.attributes).forEach(function (
932
+ attribute: Attr,
933
+ ) {
934
+ document.body.removeAttribute(attribute.name);
935
+ });
936
+ Array.prototype.slice.call(nextDoc.body.attributes).forEach(function (
937
+ attribute: Attr,
938
+ ) {
939
+ document.body.setAttribute(attribute.name, attribute.value);
940
+ });
941
+ document.body.innerHTML = nextDoc.body.innerHTML;
942
+ persistentNodes.forEach(function (node) {
943
+ document.body.appendChild(node);
944
+ });
945
+ applyHiddenSelectors();
946
+
947
+ selectedEl = null;
948
+ hoveredEl = null;
949
+ for (var i = 0; i < activeCandidates.length && !selectedEl; i += 1) {
950
+ try {
951
+ var match = document.querySelector(activeCandidates[i]);
952
+ // Skip the editor's own injected overlay chrome and re-anchor to a
953
+ // source-backed element. A stale positional candidate like
954
+ // body > div:nth-of-type(6) can otherwise re-match an overlay div
955
+ // (the only direct div children of body at runtime), which then has
956
+ // no code-layer node and fails every edit.
957
+ if (
958
+ match &&
959
+ !isLayerInteractionBlocked(match) &&
960
+ !isOverlayElement(match)
961
+ ) {
962
+ selectedEl = selectionTargetForHit(match) || match;
963
+ }
964
+ } catch (_err) {}
965
+ }
966
+ if (selectedEl) {
967
+ positionOverlay(selectionOverlay, selectedEl);
968
+ (window.parent as Window).postMessage(
969
+ { type: "element-select", payload: getElementInfo(selectedEl) },
970
+ "*",
971
+ );
972
+ } else {
973
+ selectionOverlay.style.display = "none";
974
+ }
975
+ highlightOverlay.style.display = "none";
976
+ hideMeasurements();
977
+ refreshOverlays();
978
+ }
979
+
980
+ function hideSpacingOverlay(): void {
981
+ spacingOverlay.style.display = "none";
982
+ spacingOverlay.innerHTML = "";
983
+ spacingHandleStateByKey = {};
984
+ spacingHandleNodesByKey = {};
985
+ spacingOverlayRenderKey = "";
986
+ if (!spacingDrag) spacingBadge.style.display = "none";
987
+ }
988
+
989
+ function visibleLayoutChildren(el: Element | null): Element[] {
990
+ if (!el || !el.children) return [];
991
+ return Array.prototype.slice.call(el.children).filter(function (child) {
992
+ if (
993
+ !child ||
994
+ child.nodeType !== 1 ||
995
+ isOverlayElement(child) ||
996
+ isLayerInteractionBlocked(child)
997
+ )
998
+ return false;
999
+ var cs = window.getComputedStyle(child);
1000
+ if (
1001
+ cs.display === "none" ||
1002
+ cs.visibility === "hidden" ||
1003
+ cs.position === "fixed"
1004
+ )
1005
+ return false;
1006
+ var rect = child.getBoundingClientRect();
1007
+ return rect.width > 0 && rect.height > 0;
1008
+ });
1009
+ }
1010
+
1011
+ function spacingColor(kind: string): string {
1012
+ return kind === "gap" ? "#ff4fd8" : "var(--design-editor-accent-color)";
1013
+ }
1014
+
1015
+ function spacingFill(kind: string, orientation: string): string {
1016
+ var tint =
1017
+ kind === "gap" ? "rgba(255, 79, 216, 0.28)" : "rgba(46, 168, 255, 0.24)";
1018
+ var stripe =
1019
+ kind === "gap" ? "rgba(255, 79, 216, 0.58)" : "rgba(46, 168, 255, 0.52)";
1020
+ var angle = orientation === "vertical" ? "135deg" : "45deg";
1021
+ return (
1022
+ "repeating-linear-gradient(" +
1023
+ angle +
1024
+ ", " +
1025
+ stripe +
1026
+ " 0 1px, " +
1027
+ tint +
1028
+ " 1px 4px, transparent 4px 7px)"
1029
+ );
1030
+ }
1031
+
1032
+ function clampSpacingValue(value: number): number {
1033
+ var rounded = Math.round(value);
1034
+ if (!Number.isFinite(rounded)) return 0;
1035
+ return Math.max(0, Math.min(999, rounded));
1036
+ }
1037
+
1038
+ function makeSpacingHandle(config: {
1039
+ key: string;
1040
+ groupKey?: string;
1041
+ kind: string;
1042
+ property: string;
1043
+ oppositeProperty?: string;
1044
+ side?: string;
1045
+ orientation: string;
1046
+ value: number;
1047
+ region: { x: number; y: number; width: number; height: number };
1048
+ line?: unknown;
1049
+ }): unknown {
1050
+ var region = config.region;
1051
+ if (!region || region.width <= 0 || region.height <= 0) return null;
1052
+ return {
1053
+ key: config.key,
1054
+ groupKey: config.groupKey || config.key,
1055
+ kind: config.kind,
1056
+ property: config.property,
1057
+ oppositeProperty: config.oppositeProperty || "",
1058
+ side: config.side || "",
1059
+ orientation: config.orientation,
1060
+ value: clampSpacingValue(config.value),
1061
+ region: {
1062
+ x: Math.round(region.x),
1063
+ y: Math.round(region.y),
1064
+ width: Math.max(1, Math.round(region.width)),
1065
+ height: Math.max(1, Math.round(region.height)),
1066
+ },
1067
+ line: config.line,
1068
+ };
1069
+ }
1070
+
1071
+ function childLocalRect(
1072
+ child: Element,
1073
+ containerRect: DOMRect | { left: number; top: number },
1074
+ ): {
1075
+ left: number;
1076
+ top: number;
1077
+ right: number;
1078
+ bottom: number;
1079
+ width: number;
1080
+ height: number;
1081
+ } {
1082
+ var rect = child.getBoundingClientRect();
1083
+ return {
1084
+ left: rect.left - containerRect.left,
1085
+ top: rect.top - containerRect.top,
1086
+ right: rect.right - containerRect.left,
1087
+ bottom: rect.bottom - containerRect.top,
1088
+ width: rect.width,
1089
+ height: rect.height,
1090
+ };
1091
+ }
1092
+
1093
+ function childRectsOverlap(
1094
+ a: { top: number; bottom: number; left: number; right: number },
1095
+ b: { top: number; bottom: number; left: number; right: number },
1096
+ axis: string,
1097
+ ): boolean {
1098
+ if (axis === "x") {
1099
+ return Math.max(a.top, b.top) < Math.min(a.bottom, b.bottom);
1100
+ }
1101
+ return Math.max(a.left, b.left) < Math.min(a.right, b.right);
1102
+ }
1103
+
1104
+ function buildPaddingSpacingHandles(
1105
+ el: Element,
1106
+ rect: DOMRect,
1107
+ cs: CSSStyleDeclaration,
1108
+ ): unknown[] {
1109
+ var handles = [];
1110
+ var borderTop = readPx(cs.borderTopWidth);
1111
+ var borderRight = readPx(cs.borderRightWidth);
1112
+ var borderBottom = readPx(cs.borderBottomWidth);
1113
+ var borderLeft = readPx(cs.borderLeftWidth);
1114
+ var paddingTop = readPx(cs.paddingTop);
1115
+ var paddingRight = readPx(cs.paddingRight);
1116
+ var paddingBottom = readPx(cs.paddingBottom);
1117
+ var paddingLeft = readPx(cs.paddingLeft);
1118
+ var sx = chromeScaleX();
1119
+ var sy = chromeScaleY();
1120
+ var line = Math.max(1, chromeLineScale());
1121
+ var hLineWidth = Math.max(6, Math.min(18, rect.width * 0.12)) * sx;
1122
+ var vLineHeight = Math.max(6, Math.min(18, rect.height * 0.12)) * sy;
1123
+ var innerLeft = borderLeft;
1124
+ var innerTop = borderTop;
1125
+ var innerWidth = Math.max(1, rect.width - borderLeft - borderRight);
1126
+ var innerHeight = Math.max(1, rect.height - borderTop - borderBottom);
1127
+ if (paddingTop > 0) {
1128
+ handles.push(
1129
+ makeSpacingHandle({
1130
+ key: "padding:top",
1131
+ kind: "padding",
1132
+ property: "paddingTop",
1133
+ oppositeProperty: "paddingBottom",
1134
+ side: "top",
1135
+ orientation: "horizontal",
1136
+ value: paddingTop,
1137
+ region: {
1138
+ x: innerLeft,
1139
+ y: innerTop,
1140
+ width: innerWidth,
1141
+ height: paddingTop,
1142
+ },
1143
+ line: {
1144
+ x: rect.width / 2 - hLineWidth / 2,
1145
+ y: innerTop + paddingTop / 2 - line / 2,
1146
+ width: hLineWidth,
1147
+ height: line,
1148
+ },
1149
+ }),
1150
+ );
1151
+ }
1152
+ if (paddingBottom > 0) {
1153
+ handles.push(
1154
+ makeSpacingHandle({
1155
+ key: "padding:bottom",
1156
+ kind: "padding",
1157
+ property: "paddingBottom",
1158
+ oppositeProperty: "paddingTop",
1159
+ side: "bottom",
1160
+ orientation: "horizontal",
1161
+ value: paddingBottom,
1162
+ region: {
1163
+ x: innerLeft,
1164
+ y: rect.height - borderBottom - paddingBottom,
1165
+ width: innerWidth,
1166
+ height: paddingBottom,
1167
+ },
1168
+ line: {
1169
+ x: rect.width / 2 - hLineWidth / 2,
1170
+ y: rect.height - borderBottom - paddingBottom / 2 - line / 2,
1171
+ width: hLineWidth,
1172
+ height: line,
1173
+ },
1174
+ }),
1175
+ );
1176
+ }
1177
+ if (paddingLeft > 0) {
1178
+ handles.push(
1179
+ makeSpacingHandle({
1180
+ key: "padding:left",
1181
+ kind: "padding",
1182
+ property: "paddingLeft",
1183
+ oppositeProperty: "paddingRight",
1184
+ side: "left",
1185
+ orientation: "vertical",
1186
+ value: paddingLeft,
1187
+ region: {
1188
+ x: innerLeft,
1189
+ y: innerTop,
1190
+ width: paddingLeft,
1191
+ height: innerHeight,
1192
+ },
1193
+ line: {
1194
+ x: innerLeft + paddingLeft / 2 - line / 2,
1195
+ y: rect.height / 2 - vLineHeight / 2,
1196
+ width: line,
1197
+ height: vLineHeight,
1198
+ },
1199
+ }),
1200
+ );
1201
+ }
1202
+ if (paddingRight > 0) {
1203
+ handles.push(
1204
+ makeSpacingHandle({
1205
+ key: "padding:right",
1206
+ kind: "padding",
1207
+ property: "paddingRight",
1208
+ oppositeProperty: "paddingLeft",
1209
+ side: "right",
1210
+ orientation: "vertical",
1211
+ value: paddingRight,
1212
+ region: {
1213
+ x: rect.width - borderRight - paddingRight,
1214
+ y: innerTop,
1215
+ width: paddingRight,
1216
+ height: innerHeight,
1217
+ },
1218
+ line: {
1219
+ x: rect.width - borderRight - paddingRight / 2 - line / 2,
1220
+ y: rect.height / 2 - vLineHeight / 2,
1221
+ width: line,
1222
+ height: vLineHeight,
1223
+ },
1224
+ }),
1225
+ );
1226
+ }
1227
+ return handles.filter(Boolean);
1228
+ }
1229
+
1230
+ function buildGapSpacingHandles(
1231
+ el: Element,
1232
+ rect: DOMRect,
1233
+ cs: CSSStyleDeclaration,
1234
+ ): unknown[] {
1235
+ var children = visibleLayoutChildren(el);
1236
+ if (children.length < 2) return [];
1237
+ var handles = [];
1238
+ var sx = chromeScaleX();
1239
+ var sy = chromeScaleY();
1240
+ var line = Math.max(1, chromeLineScale());
1241
+ var hLineWidth = 8 * sx;
1242
+ var vLineHeight = 8 * sy;
1243
+ var isFlex = cs.display === "flex" || cs.display === "inline-flex";
1244
+ var isGrid = cs.display === "grid" || cs.display === "inline-grid";
1245
+ if (!isFlex && !isGrid) return handles;
1246
+ var primaryAxis =
1247
+ isFlex && cs.flexDirection && cs.flexDirection.indexOf("column") === 0
1248
+ ? "y"
1249
+ : "x";
1250
+ var childRects = children.map(function (child) {
1251
+ return childLocalRect(child, rect);
1252
+ });
1253
+
1254
+ function addAxisGaps(axis, property, groupKey) {
1255
+ var cssGap = readPx(cs[property]);
1256
+ if (cssGap <= 0) return;
1257
+ var sorted = childRects.slice().sort(function (a, b) {
1258
+ return axis === "x" ? a.left - b.left : a.top - b.top;
1259
+ });
1260
+ var count = 0;
1261
+ for (var i = 0; i < sorted.length - 1; i += 1) {
1262
+ var a = sorted[i];
1263
+ var b = sorted[i + 1];
1264
+ if (!childRectsOverlap(a, b, axis)) continue;
1265
+ var gap = axis === "x" ? b.left - a.right : b.top - a.bottom;
1266
+ if (gap <= 1) continue;
1267
+ if (axis === "x") {
1268
+ var top = Math.max(a.top, b.top);
1269
+ var bottom = Math.min(a.bottom, b.bottom);
1270
+ var height = Math.max(1, bottom - top);
1271
+ handles.push(
1272
+ makeSpacingHandle({
1273
+ key: groupKey + ":" + count,
1274
+ groupKey: groupKey,
1275
+ kind: "gap",
1276
+ property: property,
1277
+ orientation: "vertical",
1278
+ value: cssGap,
1279
+ region: { x: a.right, y: top, width: gap, height: height },
1280
+ line: {
1281
+ x: a.right + gap / 2 - line / 2,
1282
+ y: top + height / 2 - vLineHeight / 2,
1283
+ width: line,
1284
+ height: vLineHeight,
1285
+ },
1286
+ }),
1287
+ );
1288
+ } else {
1289
+ var left = Math.max(a.left, b.left);
1290
+ var right = Math.min(a.right, b.right);
1291
+ var width = Math.max(1, right - left);
1292
+ handles.push(
1293
+ makeSpacingHandle({
1294
+ key: groupKey + ":" + count,
1295
+ groupKey: groupKey,
1296
+ kind: "gap",
1297
+ property: property,
1298
+ orientation: "horizontal",
1299
+ value: cssGap,
1300
+ region: { x: left, y: a.bottom, width: width, height: gap },
1301
+ line: {
1302
+ x: left + width / 2 - hLineWidth / 2,
1303
+ y: a.bottom + gap / 2 - line / 2,
1304
+ width: hLineWidth,
1305
+ height: line,
1306
+ },
1307
+ }),
1308
+ );
1309
+ }
1310
+ count += 1;
1311
+ }
1312
+ }
1313
+
1314
+ if (primaryAxis === "x") {
1315
+ addAxisGaps("x", "columnGap", "gap:column");
1316
+ if (isGrid) addAxisGaps("y", "rowGap", "gap:row");
1317
+ } else {
1318
+ addAxisGaps("y", "rowGap", "gap:row");
1319
+ if (isGrid) addAxisGaps("x", "columnGap", "gap:column");
1320
+ }
1321
+ return handles.filter(Boolean);
1322
+ }
1323
+
1324
+ function buildSpacingHandles(el: Element | null): ({
1325
+ key: string;
1326
+ groupKey: string;
1327
+ kind: string;
1328
+ property: string;
1329
+ oppositeProperty: string;
1330
+ side: string;
1331
+ orientation: string;
1332
+ value: number;
1333
+ region: { x: number; y: number; width: number; height: number };
1334
+ line: { x: number; y: number; width: number; height: number } | undefined;
1335
+ } | null)[] {
1336
+ if (!el || !document.documentElement.contains(el)) return [];
1337
+ if (Math.abs(currentRotation(el)) > 0.01) return [];
1338
+ var children = visibleLayoutChildren(el);
1339
+ if (children.length === 0) return [];
1340
+ var rect = el.getBoundingClientRect();
1341
+ if (rect.width <= 0 || rect.height <= 0) return [];
1342
+ var cs = window.getComputedStyle(el);
1343
+ return buildPaddingSpacingHandles(el, rect, cs).concat(
1344
+ buildGapSpacingHandles(el, rect, cs),
1345
+ );
1346
+ }
1347
+
1348
+ function showSpacingBadgeForHandle(
1349
+ handle: {
1350
+ key: string;
1351
+ groupKey: string;
1352
+ kind: string;
1353
+ property: string;
1354
+ oppositeProperty: string;
1355
+ side: string;
1356
+ orientation: string;
1357
+ value: number;
1358
+ region: { x: number; y: number; width: number; height: number };
1359
+ line: { x: number; y: number; width: number; height: number } | undefined;
1360
+ } | null,
1361
+ value: number,
1362
+ ): void {
1363
+ if (!selectedEl || !handle) {
1364
+ spacingBadge.style.display = "none";
1365
+ return;
1366
+ }
1367
+ var rect = selectedEl.getBoundingClientRect();
1368
+ var x = rect.left + handle.region.x + handle.region.width / 2;
1369
+ var y = rect.top + handle.region.y + handle.region.height / 2;
1370
+ spacingBadge.textContent = String(clampSpacingValue(value));
1371
+ spacingBadge.style.display = "block";
1372
+ spacingBadge.style.background = spacingColor(handle.kind);
1373
+ spacingBadge.style.left = x + "px";
1374
+ spacingBadge.style.top = y + "px";
1375
+ spacingBadge.style.transform = "translate(-50%, -50%)";
1376
+ }
1377
+
1378
+ function renderSpacingHandle(
1379
+ handle: {
1380
+ key: string;
1381
+ groupKey: string;
1382
+ kind: string;
1383
+ property: string;
1384
+ oppositeProperty: string;
1385
+ side: string;
1386
+ orientation: string;
1387
+ value: number;
1388
+ region: { x: number; y: number; width: number; height: number };
1389
+ line: { x: number; y: number; width: number; height: number } | undefined;
1390
+ } | null,
1391
+ activeGroupKey: string,
1392
+ ): void {
1393
+ if (!handle) return;
1394
+ spacingHandleStateByKey[handle.key] = handle;
1395
+ var highlighted = Boolean(
1396
+ activeGroupKey && handle.groupKey === activeGroupKey,
1397
+ );
1398
+ var lineNode = document.createElement("span");
1399
+ lineNode.setAttribute("data-agent-native-spacing-line", handle.kind);
1400
+ lineNode.style.display = "block";
1401
+ lineNode.style.left = handle.line.x + "px";
1402
+ lineNode.style.top = handle.line.y + "px";
1403
+ lineNode.style.width = Math.max(1, handle.line.width) + "px";
1404
+ lineNode.style.height = Math.max(1, handle.line.height) + "px";
1405
+ lineNode.style.background = spacingColor(handle.kind);
1406
+ spacingOverlay.appendChild(lineNode);
1407
+
1408
+ var regionNode = document.createElement("span");
1409
+ regionNode.setAttribute("data-agent-native-spacing-region", handle.kind);
1410
+ regionNode.setAttribute("data-orientation", handle.orientation);
1411
+ regionNode.setAttribute("data-spacing-key", handle.key);
1412
+ regionNode.style.display = "block";
1413
+ regionNode.style.left = handle.region.x + "px";
1414
+ regionNode.style.top = handle.region.y + "px";
1415
+ regionNode.style.width = handle.region.width + "px";
1416
+ regionNode.style.height = handle.region.height + "px";
1417
+ regionNode.style.background = highlighted
1418
+ ? spacingFill(handle.kind, handle.orientation)
1419
+ : "transparent";
1420
+ regionNode.style.outline = highlighted
1421
+ ? "1px solid " + spacingColor(handle.kind)
1422
+ : "0";
1423
+ regionNode.style.outlineOffset = "-1px";
1424
+ regionNode.addEventListener(
1425
+ "mousedown",
1426
+ function (event) {
1427
+ startSpacingDrag(handle.key, event);
1428
+ },
1429
+ true,
1430
+ );
1431
+ spacingHandleNodesByKey[handle.key] = regionNode;
1432
+ spacingOverlay.appendChild(regionNode);
1433
+ }
1434
+
1435
+ function updateSpacingOverlay(el: Element | null): void {
1436
+ if (el && el !== selectedEl) {
1437
+ hideSpacingOverlay();
1438
+ return;
1439
+ }
1440
+ if (!selectedEl || !document.documentElement.contains(selectedEl)) {
1441
+ hideSpacingOverlay();
1442
+ return;
1443
+ }
1444
+ if (!selectedSpacingHovered && !hoveredSpacingHandleKey && !spacingDrag) {
1445
+ hideSpacingOverlay();
1446
+ return;
1447
+ }
1448
+ var handles = buildSpacingHandles(selectedEl);
1449
+ if (handles.length === 0) {
1450
+ hideSpacingOverlay();
1451
+ return;
1452
+ }
1453
+ var activeHandle =
1454
+ (spacingDrag && spacingDrag.handle) ||
1455
+ handles.find(function (handle) {
1456
+ return handle.key === hoveredSpacingHandleKey;
1457
+ }) ||
1458
+ null;
1459
+ var activeGroupKey = activeHandle ? activeHandle.groupKey : "";
1460
+ var nextRenderKey =
1461
+ activeGroupKey +
1462
+ "|" +
1463
+ handles
1464
+ .map(function (handle) {
1465
+ return [
1466
+ handle.key,
1467
+ handle.value,
1468
+ handle.region.x,
1469
+ handle.region.y,
1470
+ handle.region.width,
1471
+ handle.region.height,
1472
+ handle.line.x,
1473
+ handle.line.y,
1474
+ handle.line.width,
1475
+ handle.line.height,
1476
+ ].join(",");
1477
+ })
1478
+ .join("|");
1479
+ if (
1480
+ spacingOverlay.style.display === "block" &&
1481
+ spacingOverlayRenderKey === nextRenderKey
1482
+ ) {
1483
+ if (activeHandle) {
1484
+ showSpacingBadgeForHandle(
1485
+ activeHandle,
1486
+ spacingDrag ? spacingDrag.currentValue : activeHandle.value,
1487
+ );
1488
+ } else {
1489
+ spacingBadge.style.display = "none";
1490
+ }
1491
+ return;
1492
+ }
1493
+ spacingOverlayRenderKey = nextRenderKey;
1494
+ spacingOverlay.style.display = "block";
1495
+ spacingOverlay.innerHTML = "";
1496
+ spacingHandleStateByKey = {};
1497
+ spacingHandleNodesByKey = {};
1498
+ handles.forEach(function (handle) {
1499
+ renderSpacingHandle(handle, activeGroupKey);
1500
+ });
1501
+ if (activeHandle) {
1502
+ showSpacingBadgeForHandle(
1503
+ activeHandle,
1504
+ spacingDrag ? spacingDrag.currentValue : activeHandle.value,
1505
+ );
1506
+ } else {
1507
+ spacingBadge.style.display = "none";
1508
+ }
1509
+ }
1510
+
1511
+ function handleSpacingOverlayPointerMove(e: PointerEvent): void {
1512
+ if (spacingDrag) return;
1513
+ var target =
1514
+ e.target && e.target.closest
1515
+ ? e.target.closest("[data-agent-native-spacing-region]")
1516
+ : null;
1517
+ var spacingKey =
1518
+ target && target.getAttribute
1519
+ ? target.getAttribute("data-spacing-key")
1520
+ : "";
1521
+ if (!spacingKey) return;
1522
+ stopNativeInteraction(e);
1523
+ selectedSpacingHovered = true;
1524
+ if (hoveredSpacingHandleKey !== spacingKey) {
1525
+ hoveredSpacingHandleKey = spacingKey;
1526
+ updateSpacingOverlay(selectedEl);
1527
+ }
1528
+ }
1529
+
1530
+ function spacingRegionFromPoint(
1531
+ clientX: number,
1532
+ clientY: number,
1533
+ ): Element | null {
1534
+ var targets = document.elementsFromPoint
1535
+ ? document.elementsFromPoint(clientX, clientY)
1536
+ : [document.elementFromPoint(clientX, clientY)];
1537
+ for (var i = 0; i < targets.length; i += 1) {
1538
+ var target = targets[i];
1539
+ if (!target || target.nodeType !== 1 || !target.closest) continue;
1540
+ var region = target.closest("[data-agent-native-spacing-region]");
1541
+ if (region) return region;
1542
+ }
1543
+ return null;
1544
+ }
1545
+
1546
+ function selectedSpacingSurfaceContainsPoint(
1547
+ clientX: number,
1548
+ clientY: number,
1549
+ ): boolean {
1550
+ if (!selectedEl || !document.documentElement.contains(selectedEl))
1551
+ return false;
1552
+ var region = spacingRegionFromPoint(clientX, clientY);
1553
+ if (region) {
1554
+ var spacingKey = region.getAttribute
1555
+ ? region.getAttribute("data-spacing-key")
1556
+ : "";
1557
+ if (spacingKey) hoveredSpacingHandleKey = spacingKey;
1558
+ selectedSpacingHovered = true;
1559
+ return true;
1560
+ }
1561
+ var hit = elementFromEditorPoint(clientX, clientY);
1562
+ if (
1563
+ hit &&
1564
+ (hit === selectedEl || (selectedEl.contains && selectedEl.contains(hit)))
1565
+ ) {
1566
+ selectedSpacingHovered = true;
1567
+ return true;
1568
+ }
1569
+ return false;
1570
+ }
1571
+
1572
+ function shouldKeepSpacingOverlayForLeave(e: PointerEvent): boolean {
1573
+ if (spacingDrag) return true;
1574
+ if (e.relatedTarget && isOverlayElement(e.relatedTarget)) return true;
1575
+ if (Number.isFinite(e.clientX) && Number.isFinite(e.clientY)) {
1576
+ return selectedSpacingSurfaceContainsPoint(e.clientX, e.clientY);
1577
+ }
1578
+ return false;
1579
+ }
1580
+
1581
+ function applyEditorChromeScale() {
1582
+ syncEditorChromeScaleVars();
1583
+ var sx = chromeScaleX();
1584
+ var sy = chromeScaleY();
1585
+ var line = chromeLineScale();
1586
+ highlightOverlay.style.borderWidth = 1.5 * line + "px";
1587
+ selectionOverlay.style.borderWidth = 1.5 * line + "px";
1588
+ if (selectedEl) updateSpacingOverlay(selectedEl);
1589
+
1590
+ selectionOverlay
1591
+ .querySelectorAll("[data-agent-native-edge-handle]")
1592
+ .forEach(function (edge) {
1593
+ var pos = edge.getAttribute("data-agent-native-edge-handle");
1594
+ if (pos === "n" || pos === "s") {
1595
+ edge.style.height = 10 * sy + "px";
1596
+ edge.style[pos === "n" ? "top" : "bottom"] = -5 * sy + "px";
1597
+ }
1598
+ if (pos === "e" || pos === "w") {
1599
+ edge.style.width = 10 * sx + "px";
1600
+ edge.style[pos === "w" ? "left" : "right"] = -5 * sx + "px";
1601
+ }
1602
+ });
1603
+
1604
+ selectionOverlay
1605
+ .querySelectorAll("[data-agent-native-edit-handle]")
1606
+ .forEach(function (handle) {
1607
+ var pos = handle.getAttribute("data-agent-native-edit-handle") || "";
1608
+ handle.style.width = 7 * sx + "px";
1609
+ handle.style.height = 7 * sy + "px";
1610
+ handle.style.borderWidth = Math.max(1, 1 * line) + "px";
1611
+ if (pos.indexOf("n") !== -1) handle.style.top = -4 * sy + "px";
1612
+ if (pos.indexOf("s") !== -1) handle.style.bottom = -4 * sy + "px";
1613
+ if (pos.indexOf("w") !== -1) handle.style.left = -4 * sx + "px";
1614
+ if (pos.indexOf("e") !== -1) handle.style.right = -4 * sx + "px";
1615
+ });
1616
+
1617
+ selectionOverlay
1618
+ .querySelectorAll("[data-agent-native-rotate-handle]")
1619
+ .forEach(function (handle) {
1620
+ var pos = handle.getAttribute("data-agent-native-rotate-handle") || "";
1621
+ handle.style.width = 18 * sx + "px";
1622
+ handle.style.height = 18 * sy + "px";
1623
+ if (pos.indexOf("n") !== -1) handle.style.top = -26 * sy + "px";
1624
+ if (pos.indexOf("s") !== -1) handle.style.bottom = -26 * sy + "px";
1625
+ if (pos.indexOf("w") !== -1) handle.style.left = -26 * sx + "px";
1626
+ if (pos.indexOf("e") !== -1) handle.style.right = -26 * sx + "px";
1627
+ });
1628
+ }
1629
+
1630
+ function positionOverlay(overlay: HTMLElement, el: Element): void {
1631
+ if (!el || !document.documentElement.contains(el)) {
1632
+ overlay.style.display = "none";
1633
+ if (overlay === selectionOverlay) clearComponentTag();
1634
+ return;
1635
+ }
1636
+ // For the selection overlay, prefer the CSS box + rotation transform so
1637
+ // the handles hug the rotated element rather than its inflated AABB.
1638
+ if (overlay === selectionOverlay) {
1639
+ var elCs = window.getComputedStyle(el);
1640
+ var elLeft = readFinitePx(el.style.left || elCs.left);
1641
+ var elTop = readFinitePx(el.style.top || elCs.top);
1642
+ var elW = readFinitePx(el.style.width || elCs.width);
1643
+ var elH = readFinitePx(el.style.height || elCs.height);
1644
+ var elRot = currentRotation(el);
1645
+ var canUseLocalBox =
1646
+ Math.abs(elRot) > 0.01 &&
1647
+ elLeft !== null &&
1648
+ elTop !== null &&
1649
+ elW !== null &&
1650
+ elH !== null;
1651
+ // Convert element-local left/top to viewport coords by walking to the
1652
+ // nearest positioned ancestor (same reference frame as getBoundingClientRect).
1653
+ if (canUseLocalBox) {
1654
+ var parentRect = (
1655
+ (el as HTMLElement).offsetParent || document.documentElement
1656
+ ).getBoundingClientRect();
1657
+ overlay.style.display = "block";
1658
+ overlay.style.left = parentRect.left + elLeft + "px";
1659
+ overlay.style.top = parentRect.top + elTop + "px";
1660
+ overlay.style.width = elW + "px";
1661
+ overlay.style.height = elH + "px";
1662
+ overlay.style.transform = "rotate(" + elRot + "deg)";
1663
+ overlay.style.transformOrigin = "0 0";
1664
+ updateSpacingOverlay(el);
1665
+ updateComponentTag(el);
1666
+ return;
1667
+ }
1668
+ }
1669
+ var rect = el.getBoundingClientRect();
1670
+ overlay.style.display = "block";
1671
+ overlay.style.top = rect.top + "px";
1672
+ overlay.style.left = rect.left + "px";
1673
+ overlay.style.width = rect.width + "px";
1674
+ overlay.style.height = rect.height + "px";
1675
+ overlay.style.transform = "";
1676
+ if (overlay === selectionOverlay) {
1677
+ updateSpacingOverlay(el);
1678
+ updateComponentTag(el);
1679
+ }
1680
+ }
1681
+
1682
+ function refreshOverlays(): void {
1683
+ if (hoveredEl && hoveredEl !== selectedEl) {
1684
+ positionOverlay(highlightOverlay, hoveredEl);
1685
+ } else {
1686
+ highlightOverlay.style.display = "none";
1687
+ }
1688
+ if (selectedEl) positionOverlay(selectionOverlay, selectedEl);
1689
+ }
1690
+
1691
+ function hideMeasurements(): void {
1692
+ measurementOverlay.style.display = "none";
1693
+ measurementOverlay.innerHTML = "";
1694
+ }
1695
+
1696
+ function addMeasurementLine(x1, y1, x2, y2, label) {
1697
+ var horizontal = Math.abs(x2 - x1) >= Math.abs(y2 - y1);
1698
+ var line = document.createElement("div");
1699
+ var labelEl = document.createElement("div");
1700
+ if (horizontal) {
1701
+ var left = Math.min(x1, x2);
1702
+ var width = Math.max(1, Math.abs(x2 - x1));
1703
+ line.style.cssText =
1704
+ "position:fixed;left:" +
1705
+ left +
1706
+ "px;top:" +
1707
+ y1 +
1708
+ "px;width:" +
1709
+ width +
1710
+ "px;border-top:1px dashed var(--design-editor-measure-color);";
1711
+ labelEl.style.cssText =
1712
+ "position:fixed;left:" +
1713
+ (left + width / 2) +
1714
+ "px;top:" +
1715
+ (y1 - 9) +
1716
+ "px;transform:translateX(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;";
1717
+ } else {
1718
+ var top = Math.min(y1, y2);
1719
+ var height = Math.max(1, Math.abs(y2 - y1));
1720
+ line.style.cssText =
1721
+ "position:fixed;left:" +
1722
+ x1 +
1723
+ "px;top:" +
1724
+ top +
1725
+ "px;height:" +
1726
+ height +
1727
+ "px;border-left:1px dashed var(--design-editor-measure-color);";
1728
+ labelEl.style.cssText =
1729
+ "position:fixed;left:" +
1730
+ (x1 + 5) +
1731
+ "px;top:" +
1732
+ (top + height / 2) +
1733
+ "px;transform:translateY(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;";
1734
+ }
1735
+ labelEl.textContent = label;
1736
+ measurementOverlay.appendChild(line);
1737
+ measurementOverlay.appendChild(labelEl);
1738
+ }
1739
+
1740
+ function showMeasurements(a, b) {
1741
+ if (!a || !b || a === b) {
1742
+ hideMeasurements();
1743
+ return;
1744
+ }
1745
+ var selectedRect = a.getBoundingClientRect();
1746
+ var hoverRect = b.getBoundingClientRect();
1747
+ measurementOverlay.innerHTML = "";
1748
+ measurementOverlay.style.display = "block";
1749
+
1750
+ if (hoverRect.right <= selectedRect.left) {
1751
+ var yLeft = Math.max(
1752
+ hoverRect.top,
1753
+ Math.min(hoverRect.bottom, selectedRect.top + selectedRect.height / 2),
1754
+ );
1755
+ addMeasurementLine(
1756
+ hoverRect.right,
1757
+ yLeft,
1758
+ selectedRect.left,
1759
+ yLeft,
1760
+ Math.round(selectedRect.left - hoverRect.right) + "px",
1761
+ );
1762
+ return;
1763
+ }
1764
+ if (selectedRect.right <= hoverRect.left) {
1765
+ var yRight = Math.max(
1766
+ selectedRect.top,
1767
+ Math.min(selectedRect.bottom, hoverRect.top + hoverRect.height / 2),
1768
+ );
1769
+ addMeasurementLine(
1770
+ selectedRect.right,
1771
+ yRight,
1772
+ hoverRect.left,
1773
+ yRight,
1774
+ Math.round(hoverRect.left - selectedRect.right) + "px",
1775
+ );
1776
+ return;
1777
+ }
1778
+ if (hoverRect.bottom <= selectedRect.top) {
1779
+ var xTop = Math.max(
1780
+ hoverRect.left,
1781
+ Math.min(hoverRect.right, selectedRect.left + selectedRect.width / 2),
1782
+ );
1783
+ addMeasurementLine(
1784
+ xTop,
1785
+ hoverRect.bottom,
1786
+ xTop,
1787
+ selectedRect.top,
1788
+ Math.round(selectedRect.top - hoverRect.bottom) + "px",
1789
+ );
1790
+ return;
1791
+ }
1792
+ if (selectedRect.bottom <= hoverRect.top) {
1793
+ var xBottom = Math.max(
1794
+ selectedRect.left,
1795
+ Math.min(selectedRect.right, hoverRect.left + hoverRect.width / 2),
1796
+ );
1797
+ addMeasurementLine(
1798
+ xBottom,
1799
+ selectedRect.bottom,
1800
+ xBottom,
1801
+ hoverRect.top,
1802
+ Math.round(hoverRect.top - selectedRect.bottom) + "px",
1803
+ );
1804
+ return;
1805
+ }
1806
+ addMeasurementLine(
1807
+ selectedRect.left + selectedRect.width / 2,
1808
+ selectedRect.top + selectedRect.height / 2,
1809
+ hoverRect.left + hoverRect.width / 2,
1810
+ hoverRect.top + hoverRect.height / 2,
1811
+ Math.round(
1812
+ Math.hypot(
1813
+ hoverRect.left +
1814
+ hoverRect.width / 2 -
1815
+ (selectedRect.left + selectedRect.width / 2),
1816
+ hoverRect.top +
1817
+ hoverRect.height / 2 -
1818
+ (selectedRect.top + selectedRect.height / 2),
1819
+ ),
1820
+ ) + "px",
1821
+ );
1822
+ }
1823
+
1824
+ function dragEventNames(e) {
1825
+ var pointerGesture = e && e.type && e.type.indexOf("pointer") === 0;
1826
+ return pointerGesture
1827
+ ? { move: "pointermove", up: "pointerup" }
1828
+ : { move: "mousemove", up: "mouseup" };
1829
+ }
1830
+
1831
+ function elementFromEditorPoint(
1832
+ clientX: number,
1833
+ clientY: number,
1834
+ ): Element | null {
1835
+ lastEditorPointWasBlocked = false;
1836
+ var shieldPointerEvents = shieldOverlay.style.pointerEvents;
1837
+ var selectionPointerEvents = selectionOverlay.style.pointerEvents;
1838
+ var highlightPointerEvents = highlightOverlay.style.pointerEvents;
1839
+ shieldOverlay.style.pointerEvents = "none";
1840
+ selectionOverlay.style.pointerEvents = "none";
1841
+ highlightOverlay.style.pointerEvents = "none";
1842
+ var targets = document.elementsFromPoint
1843
+ ? document.elementsFromPoint(clientX, clientY)
1844
+ : [document.elementFromPoint(clientX, clientY)];
1845
+ shieldOverlay.style.pointerEvents = shieldPointerEvents;
1846
+ selectionOverlay.style.pointerEvents = selectionPointerEvents;
1847
+ highlightOverlay.style.pointerEvents = highlightPointerEvents;
1848
+ for (var i = 0; i < targets.length; i += 1) {
1849
+ var target = targets[i];
1850
+ if (!target || target.nodeType !== 1) continue;
1851
+ if (isOverlayElement(target)) continue;
1852
+ if (isLayerInteractionBlocked(target)) {
1853
+ lastEditorPointWasBlocked = true;
1854
+ return null;
1855
+ }
1856
+ return target;
1857
+ }
1858
+ return null;
1859
+ }
1860
+
1861
+ function stopNativeInteraction(e: Event): void {
1862
+ e.preventDefault();
1863
+ e.stopPropagation();
1864
+ if (e.stopImmediatePropagation) e.stopImmediatePropagation();
1865
+ }
1866
+
1867
+ function isEditorTypingTarget(target) {
1868
+ if (!target || !target.closest) return false;
1869
+ return !!target.closest(
1870
+ 'input, textarea, select, [contenteditable], [role="textbox"], [data-agent-native-text-editing]',
1871
+ );
1872
+ }
1873
+
1874
+ function shouldForwardDesignHotkey(e) {
1875
+ if (activeTextEditEl || isEditorTypingTarget(e.target) || e.isComposing)
1876
+ return false;
1877
+ var key = e.key;
1878
+ var normalized = key && key.length === 1 ? key.toLowerCase() : key;
1879
+ var primary = e.metaKey || e.ctrlKey;
1880
+ if (key === "Escape" || key === "Enter") return true;
1881
+ // Forward Tab only when an element is actively selected so the iframe does
1882
+ // not intercept Tab when the user is tabbing through browser UI with nothing
1883
+ // selected (preserves native keyboard accessibility).
1884
+ if (key === "Tab") return !!selectedEl;
1885
+ if (key === "Delete" || key === "Backspace") return !primary;
1886
+ if (/^Arrow/.test(key || "")) return !e.altKey;
1887
+ if (primary) {
1888
+ return (
1889
+ [
1890
+ "z",
1891
+ "y",
1892
+ "a",
1893
+ "x",
1894
+ "c",
1895
+ "v",
1896
+ "d",
1897
+ "g",
1898
+ "=",
1899
+ "+",
1900
+ "-",
1901
+ "0",
1902
+ "]",
1903
+ "[",
1904
+ ].indexOf(normalized) !== -1 ||
1905
+ e.code === "Digit1" ||
1906
+ e.code === "Digit2" ||
1907
+ key === "1" ||
1908
+ key === "2"
1909
+ );
1910
+ }
1911
+ if (
1912
+ e.shiftKey &&
1913
+ (e.code === "Digit1" || e.code === "Digit2" || key === "1" || key === "2")
1914
+ )
1915
+ return true;
1916
+ return (
1917
+ !e.altKey &&
1918
+ !e.shiftKey &&
1919
+ ["v", "f", "r", "t", "p", "h", "c", "k"].indexOf(normalized) !== -1
1920
+ );
1921
+ }
1922
+
1923
+ function blurActiveTextEditor(): void {
1924
+ var active = document.activeElement;
1925
+ if (
1926
+ active &&
1927
+ active.closest &&
1928
+ active.closest("[data-agent-native-text-editing]") &&
1929
+ typeof active.blur === "function"
1930
+ ) {
1931
+ active.blur();
1932
+ }
1933
+ }
1934
+
1935
+ function setTextEditingPointerPassthrough(enabled: boolean): void {
1936
+ if (enabled) {
1937
+ if (!textEditPointerState) {
1938
+ textEditPointerState = {
1939
+ shield: shieldOverlay.style.pointerEvents,
1940
+ selection: selectionOverlay.style.pointerEvents,
1941
+ highlight: highlightOverlay.style.pointerEvents,
1942
+ };
1943
+ }
1944
+ shieldOverlay.style.pointerEvents = "none";
1945
+ selectionOverlay.style.pointerEvents = "none";
1946
+ highlightOverlay.style.pointerEvents = "none";
1947
+ return;
1948
+ }
1949
+ if (!textEditPointerState) return;
1950
+ shieldOverlay.style.pointerEvents = textEditPointerState.shield;
1951
+ selectionOverlay.style.pointerEvents = textEditPointerState.selection;
1952
+ highlightOverlay.style.pointerEvents = textEditPointerState.highlight;
1953
+ textEditPointerState = null;
1954
+ }
1955
+
1956
+ function hasTextContent(el: Element | null): boolean {
1957
+ return !!(el && el.textContent && el.textContent.trim().length > 0);
1958
+ }
1959
+
1960
+ function isInlineEditableDescendant(el: Element | null): boolean {
1961
+ if (!el || !el.tagName) return false;
1962
+ // Allowlist covers inline markup AND common block-level text containers
1963
+ // (p, h1-h6, li, etc.) so that paragraphs with inline markup like
1964
+ // <p>Hello <strong>world</strong></p> can be double-click edited.
1965
+ return (
1966
+ [
1967
+ // Inline formatting
1968
+ "a",
1969
+ "abbr",
1970
+ "b",
1971
+ "br",
1972
+ "cite",
1973
+ "code",
1974
+ "em",
1975
+ "i",
1976
+ "mark",
1977
+ "small",
1978
+ "span",
1979
+ "strong",
1980
+ "sub",
1981
+ "sup",
1982
+ "time",
1983
+ "u",
1984
+ "wbr",
1985
+ // Block-level text containers
1986
+ "p",
1987
+ "h1",
1988
+ "h2",
1989
+ "h3",
1990
+ "h4",
1991
+ "h5",
1992
+ "h6",
1993
+ "li",
1994
+ "ul",
1995
+ "ol",
1996
+ "dl",
1997
+ "dt",
1998
+ "dd",
1999
+ "label",
2000
+ "caption",
2001
+ "td",
2002
+ "th",
2003
+ ].indexOf(el.tagName.toLowerCase()) !== -1
2004
+ );
2005
+ }
2006
+
2007
+ function hasOnlyInlineEditableChildren(el) {
2008
+ if (!el || !hasTextContent(el)) return false;
2009
+ var descendants = el.querySelectorAll ? el.querySelectorAll("*") : [];
2010
+ for (var i = 0; i < descendants.length; i += 1) {
2011
+ if (!isInlineEditableDescendant(descendants[i])) return false;
2012
+ }
2013
+ return true;
2014
+ }
2015
+
2016
+ function findTextEditTarget(hit) {
2017
+ if (
2018
+ !hit ||
2019
+ hit.nodeType !== 1 ||
2020
+ hit === document.body ||
2021
+ hit === document.documentElement
2022
+ )
2023
+ return null;
2024
+ var selectedContainsHit =
2025
+ selectedEl && selectedEl.contains && selectedEl.contains(hit);
2026
+ if (selectedContainsHit && hasOnlyInlineEditableChildren(selectedEl))
2027
+ return selectedEl;
2028
+
2029
+ var candidate = null;
2030
+ var node = hit;
2031
+ while (
2032
+ node &&
2033
+ node.nodeType === 1 &&
2034
+ node !== document.body &&
2035
+ node !== document.documentElement
2036
+ ) {
2037
+ if (hasOnlyInlineEditableChildren(node)) {
2038
+ candidate = node;
2039
+ }
2040
+ if (selectedEl && node === selectedEl) break;
2041
+ node = node.parentElement;
2042
+ }
2043
+ // Return null (not the raw hit) when no text-editable ancestor is found.
2044
+ // Falling back to the raw hit element makes non-text nodes like <img> or
2045
+ // <canvas> contenteditable, which leaves the editor in a broken state.
2046
+ return candidate || null;
2047
+ }
2048
+
2049
+ function selectElementAtEvent(e) {
2050
+ stopNativeInteraction(e);
2051
+ blurActiveTextEditor();
2052
+ if (suppressNextShieldClick) {
2053
+ suppressNextShieldClick = false;
2054
+ if (suppressNextShieldClickTimer !== null) {
2055
+ clearTimeout(suppressNextShieldClickTimer);
2056
+ suppressNextShieldClickTimer = null;
2057
+ }
2058
+ return;
2059
+ }
2060
+ // Suppress the first click in a double-click sequence — the dblclick
2061
+ // handler (beginTextEditingFromEvent) will fire immediately after and a
2062
+ // spurious element-select would cause inspector flicker.
2063
+ if (e.detail >= 2) return;
2064
+ var target = elementFromEditorPoint(e.clientX, e.clientY);
2065
+ if (!target && lastEditorPointWasBlocked) return;
2066
+ if (
2067
+ !target ||
2068
+ target === document.body ||
2069
+ target === document.documentElement
2070
+ ) {
2071
+ // Click on empty canvas: clear the current selection (matches Figma).
2072
+ clearRuntimeSelection();
2073
+ (window.parent as Window).postMessage({ type: "clear-selection" }, "*");
2074
+ return;
2075
+ }
2076
+ selectedSpacingHovered = false;
2077
+ hoveredSpacingHandleKey = "";
2078
+ selectedEl = selectionTargetForHit(target);
2079
+ if (!selectedEl || isLayerInteractionBlocked(selectedEl)) {
2080
+ selectedEl = null;
2081
+ selectionOverlay.style.display = "none";
2082
+ clearComponentTag();
2083
+ return;
2084
+ }
2085
+ var info = getElementInfo(selectedEl);
2086
+ positionOverlay(selectionOverlay, selectedEl);
2087
+ (window.parent as Window).postMessage(
2088
+ { type: "element-select", payload: info },
2089
+ "*",
2090
+ );
2091
+ }
2092
+
2093
+ function suppressNextShieldClickBriefly() {
2094
+ suppressNextShieldClick = true;
2095
+ if (suppressNextShieldClickTimer !== null) {
2096
+ clearTimeout(suppressNextShieldClickTimer);
2097
+ }
2098
+ suppressNextShieldClickTimer = setTimeout(function () {
2099
+ suppressNextShieldClick = false;
2100
+ suppressNextShieldClickTimer = null;
2101
+ }, 250);
2102
+ }
2103
+
2104
+ function openContextMenuAtEvent(e) {
2105
+ stopNativeInteraction(e);
2106
+ blurActiveTextEditor();
2107
+ var target = elementFromEditorPoint(e.clientX, e.clientY);
2108
+ if (!target && lastEditorPointWasBlocked) return;
2109
+ var info = null;
2110
+ if (target) {
2111
+ selectedSpacingHovered = false;
2112
+ hoveredSpacingHandleKey = "";
2113
+ selectedEl = selectionTargetForHit(target);
2114
+ if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
2115
+ info = getElementInfo(selectedEl);
2116
+ positionOverlay(selectionOverlay, selectedEl);
2117
+ (window.parent as Window).postMessage(
2118
+ { type: "element-select", payload: info },
2119
+ "*",
2120
+ );
2121
+ } else {
2122
+ selectedEl = null;
2123
+ selectionOverlay.style.display = "none";
2124
+ clearComponentTag();
2125
+ }
2126
+ }
2127
+ (window.parent as Window).postMessage(
2128
+ {
2129
+ type: "element-contextmenu",
2130
+ clientX: e.clientX,
2131
+ clientY: e.clientY,
2132
+ payload: info,
2133
+ },
2134
+ "*",
2135
+ );
2136
+ }
2137
+
2138
+ function findRuntimeTarget(selector, selectorCandidates) {
2139
+ var candidates: string[] = [];
2140
+ if (Array.isArray(selectorCandidates)) {
2141
+ selectorCandidates.forEach(function (candidate) {
2142
+ if (
2143
+ typeof candidate === "string" &&
2144
+ candidate &&
2145
+ candidates.indexOf(candidate) === -1
2146
+ ) {
2147
+ candidates.push(candidate);
2148
+ }
2149
+ });
2150
+ }
2151
+ if (selector && candidates.indexOf(selector) === -1)
2152
+ candidates.push(selector);
2153
+ if (
2154
+ selectedEl &&
2155
+ document.documentElement.contains(selectedEl) &&
2156
+ (candidates.length === 0 ||
2157
+ matchesExactSelectorList(selectedEl, candidates))
2158
+ ) {
2159
+ return selectedEl;
2160
+ }
2161
+ for (var i = 0; i < candidates.length; i += 1) {
2162
+ try {
2163
+ var match = document.querySelector(candidates[i]);
2164
+ if (match && !isLayerInteractionBlocked(match)) return match;
2165
+ } catch (_err) {}
2166
+ }
2167
+ return null;
2168
+ }
2169
+
2170
+ function removeRuntimeTarget(selector, selectorCandidates) {
2171
+ var target = findRuntimeTarget(selector, selectorCandidates);
2172
+ if (
2173
+ !target ||
2174
+ target === document.body ||
2175
+ target === document.documentElement
2176
+ )
2177
+ return false;
2178
+ if (target.parentElement) target.parentElement.removeChild(target);
2179
+ if (
2180
+ selectedEl === target ||
2181
+ !document.documentElement.contains(selectedEl)
2182
+ ) {
2183
+ selectedEl = null;
2184
+ selectionOverlay.style.display = "none";
2185
+ }
2186
+ hoveredEl = null;
2187
+ highlightOverlay.style.display = "none";
2188
+ hideMeasurements();
2189
+ refreshOverlays();
2190
+ return true;
2191
+ }
2192
+
2193
+ function readPx(value: string): number {
2194
+ var num = parseFloat(value);
2195
+ return Number.isFinite(num) ? num : 0;
2196
+ }
2197
+
2198
+ function readFinitePx(value) {
2199
+ if (!value || value === "auto") return null;
2200
+ var num = parseFloat(value);
2201
+ return Number.isFinite(num) ? num : null;
2202
+ }
2203
+
2204
+ function currentRotation(el) {
2205
+ var transform =
2206
+ el.style.transform || window.getComputedStyle(el).transform || "";
2207
+ var match = transform.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/);
2208
+ if (match) return parseFloat(match[1]) || 0;
2209
+ if (transform && transform !== "none" && window.DOMMatrixReadOnly) {
2210
+ try {
2211
+ var matrix = new DOMMatrixReadOnly(transform);
2212
+ return Math.round((Math.atan2(matrix.b, matrix.a) * 180) / Math.PI);
2213
+ } catch (err) {}
2214
+ }
2215
+ return 0;
2216
+ }
2217
+
2218
+ function mergeRotation(el, degrees) {
2219
+ var inline = el.style.transform || "";
2220
+ var next = inline.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/)
2221
+ ? inline.replace(
2222
+ /rotate\((-?\d+(?:\.\d+)?)deg\)/,
2223
+ "rotate(" + degrees + "deg)",
2224
+ )
2225
+ : (inline && inline !== "none" ? inline + " " : "") +
2226
+ "rotate(" +
2227
+ degrees +
2228
+ "deg)";
2229
+ return next.trim();
2230
+ }
2231
+
2232
+ function ensurePositionable(el) {
2233
+ var cs = window.getComputedStyle(el);
2234
+ if (cs.position === "static") {
2235
+ el.style.position = "relative";
2236
+ if (!el.style.left) el.style.left = "0px";
2237
+ if (!el.style.top) el.style.top = "0px";
2238
+ }
2239
+ }
2240
+
2241
+ function postVisualStyleChange(styles) {
2242
+ if (!selectedEl) return;
2243
+ (window.parent as Window).postMessage(
2244
+ {
2245
+ type: "visual-style-change",
2246
+ selector: getSelector(selectedEl),
2247
+ styles: styles,
2248
+ payload: getElementInfo(selectedEl),
2249
+ },
2250
+ "*",
2251
+ );
2252
+ }
2253
+
2254
+ function spacingValueFromPointer(
2255
+ handle,
2256
+ originValue,
2257
+ startX,
2258
+ startY,
2259
+ clientX,
2260
+ clientY,
2261
+ ) {
2262
+ var delta =
2263
+ handle.orientation === "vertical" ? clientX - startX : clientY - startY;
2264
+ if (
2265
+ handle.kind === "padding" &&
2266
+ (handle.side === "right" || handle.side === "bottom")
2267
+ ) {
2268
+ delta = -delta;
2269
+ }
2270
+ return clampSpacingValue(originValue + delta);
2271
+ }
2272
+
2273
+ function applySpacingDragValue(
2274
+ target: Element,
2275
+ handle: {
2276
+ key: string;
2277
+ groupKey: string;
2278
+ kind: string;
2279
+ property: string;
2280
+ oppositeProperty: string;
2281
+ side: string;
2282
+ orientation: string;
2283
+ value: number;
2284
+ region: { x: number; y: number; width: number; height: number };
2285
+ line: { x: number; y: number; width: number; height: number } | undefined;
2286
+ } | null,
2287
+ value: number,
2288
+ mirrorOpposite: boolean,
2289
+ ): void {
2290
+ if (!target || !handle) return;
2291
+ target.style[handle.property] = value + "px";
2292
+ if (
2293
+ handle.kind === "padding" &&
2294
+ mirrorOpposite &&
2295
+ handle.oppositeProperty
2296
+ ) {
2297
+ target.style[handle.oppositeProperty] = value + "px";
2298
+ }
2299
+ }
2300
+
2301
+ function startSpacingDrag(key, e) {
2302
+ var handle = spacingHandleStateByKey[key];
2303
+ if (!selectedEl || !handle || isLayerInteractionBlocked(selectedEl)) return;
2304
+ e.preventDefault();
2305
+ e.stopPropagation();
2306
+ if (e.stopImmediatePropagation) e.stopImmediatePropagation();
2307
+ var events = dragEventNames(e);
2308
+ var dragEl = selectedEl;
2309
+ var originValue = handle.value;
2310
+ var startX = e.clientX;
2311
+ var startY = e.clientY;
2312
+ hoveredSpacingHandleKey = key;
2313
+ selectedSpacingHovered = true;
2314
+ spacingDrag = {
2315
+ handle: handle,
2316
+ currentValue: originValue,
2317
+ mirrorOpposite: !!e.altKey,
2318
+ };
2319
+ showSpacingBadgeForHandle(handle, originValue);
2320
+
2321
+ function onMove(ev) {
2322
+ if (!dragEl || !document.documentElement.contains(dragEl)) return;
2323
+ var nextValue = spacingValueFromPointer(
2324
+ handle,
2325
+ originValue,
2326
+ startX,
2327
+ startY,
2328
+ ev.clientX,
2329
+ ev.clientY,
2330
+ );
2331
+ spacingDrag = {
2332
+ handle: handle,
2333
+ currentValue: nextValue,
2334
+ mirrorOpposite: !!ev.altKey,
2335
+ };
2336
+ applySpacingDragValue(dragEl, handle, nextValue, !!ev.altKey);
2337
+ positionOverlay(selectionOverlay, dragEl);
2338
+ showSpacingBadgeForHandle(handle, nextValue);
2339
+ }
2340
+
2341
+ function onUp(ev) {
2342
+ document.removeEventListener(events.move, onMove, true);
2343
+ document.removeEventListener(events.up, onUp, true);
2344
+ if (!dragEl || !document.documentElement.contains(dragEl)) {
2345
+ spacingDrag = null;
2346
+ return;
2347
+ }
2348
+ var finalValue = spacingDrag ? spacingDrag.currentValue : originValue;
2349
+ var mirrorOpposite = spacingDrag
2350
+ ? spacingDrag.mirrorOpposite
2351
+ : !!ev.altKey;
2352
+ applySpacingDragValue(dragEl, handle, finalValue, mirrorOpposite);
2353
+ selectedEl = dragEl;
2354
+ spacingDrag = null;
2355
+ var styles = {};
2356
+ styles[handle.property] = finalValue + "px";
2357
+ if (
2358
+ handle.kind === "padding" &&
2359
+ mirrorOpposite &&
2360
+ handle.oppositeProperty
2361
+ ) {
2362
+ styles[handle.oppositeProperty] = finalValue + "px";
2363
+ }
2364
+ postVisualStyleChange(styles);
2365
+ positionOverlay(selectionOverlay, dragEl);
2366
+ }
2367
+
2368
+ document.addEventListener(events.move, onMove, true);
2369
+ document.addEventListener(events.up, onUp, true);
2370
+ }
2371
+
2372
+ function postTextContentChange(el, value, html) {
2373
+ (window.parent as Window).postMessage(
2374
+ {
2375
+ type: "text-content-change",
2376
+ selector: getSelector(el),
2377
+ value: value,
2378
+ html: html,
2379
+ payload: getElementInfo(el),
2380
+ },
2381
+ "*",
2382
+ );
2383
+ }
2384
+
2385
+ function postTextEditingState(el: Element | null, active: boolean): void {
2386
+ var selection: Selection | null = window.getSelection
2387
+ ? window.getSelection()
2388
+ : null;
2389
+ (window.parent as Window).postMessage(
2390
+ {
2391
+ type: "text-editing-state",
2392
+ active: !!active,
2393
+ selector: el ? getSelector(el) : "",
2394
+ hasRange: !!(
2395
+ active &&
2396
+ selection &&
2397
+ selection.rangeCount > 0 &&
2398
+ !selection.isCollapsed &&
2399
+ selectionBelongsToElement(selection, el)
2400
+ ),
2401
+ },
2402
+ "*",
2403
+ );
2404
+ }
2405
+
2406
+ function insertPlainTextAtSelection(text: string): void {
2407
+ if (!text) return;
2408
+ if (
2409
+ document.queryCommandSupported &&
2410
+ document.queryCommandSupported("insertText")
2411
+ ) {
2412
+ document.execCommand("insertText", false, text);
2413
+ return;
2414
+ }
2415
+ var selection: Selection | null = window.getSelection
2416
+ ? window.getSelection()
2417
+ : null;
2418
+ if (!selection || selection.rangeCount === 0) return;
2419
+ var range = selection.getRangeAt(0);
2420
+ range.deleteContents();
2421
+ var textNode = document.createTextNode(text);
2422
+ range.insertNode(textNode);
2423
+ range.setStartAfter(textNode);
2424
+ range.setEndAfter(textNode);
2425
+ selection.removeAllRanges();
2426
+ selection.addRange(range);
2427
+ }
2428
+
2429
+ function selectionBelongsToElement(
2430
+ selection: Selection | null,
2431
+ el: Element | null,
2432
+ ): boolean {
2433
+ if (!selection || !el || selection.rangeCount === 0) return false;
2434
+ var range = selection.getRangeAt(0);
2435
+ var ancestor = range.commonAncestorContainer;
2436
+ var ancestorEl =
2437
+ ancestor && ancestor.nodeType === 1
2438
+ ? ancestor
2439
+ : ancestor && ancestor.parentElement;
2440
+ return !!(ancestorEl && (ancestorEl === el || el.contains(ancestorEl)));
2441
+ }
2442
+
2443
+ function normalizeCssPropertyName(property: unknown): string {
2444
+ var prop = String(property || "").trim();
2445
+ if (!prop) return "";
2446
+ if (prop.indexOf("--") === 0) return prop;
2447
+ return prop.replace(/([A-Z])/g, "-$1").toLowerCase();
2448
+ }
2449
+
2450
+ function applyInlineStyleProperty(
2451
+ el: HTMLElement | null,
2452
+ property: unknown,
2453
+ value: unknown,
2454
+ ): boolean {
2455
+ if (!el || !property) return false;
2456
+ var cssProperty = normalizeCssPropertyName(property);
2457
+ if (!cssProperty) return false;
2458
+ el.style.setProperty(cssProperty, String(value));
2459
+ return true;
2460
+ }
2461
+
2462
+ function applyTextRangeStyle(property: unknown, value: unknown): boolean {
2463
+ if (!activeTextEditEl || !property) return false;
2464
+ var selection: Selection | null = window.getSelection
2465
+ ? window.getSelection()
2466
+ : null;
2467
+ if (!selection || selection.rangeCount === 0 || selection.isCollapsed)
2468
+ return false;
2469
+ if (!selectionBelongsToElement(selection, activeTextEditEl)) return false;
2470
+ var range = selection.getRangeAt(0);
2471
+ var span = document.createElement("span");
2472
+ applyInlineStyleProperty(span, property, value);
2473
+ if (!span.getAttribute("style")) return false;
2474
+ try {
2475
+ range.surroundContents(span);
2476
+ } catch (err) {
2477
+ var contents = range.extractContents();
2478
+ span.appendChild(contents);
2479
+ range.insertNode(span);
2480
+ }
2481
+ selection.removeAllRanges();
2482
+ var nextRange = document.createRange();
2483
+ nextRange.selectNodeContents(span);
2484
+ selection.addRange(nextRange);
2485
+ return true;
2486
+ }
2487
+
2488
+ function showTransformBadge(
2489
+ text: string,
2490
+ clientX: number,
2491
+ clientY: number,
2492
+ ): void {
2493
+ transformBadge.textContent = text;
2494
+ transformBadge.style.display = "block";
2495
+ transformBadge.style.left = clientX + 12 + "px";
2496
+ transformBadge.style.top = clientY + 12 + "px";
2497
+ }
2498
+
2499
+ function hideTransformBadge(): void {
2500
+ transformBadge.style.display = "none";
2501
+ }
2502
+
2503
+ function hideInsertionGuide(): void {
2504
+ insertionGuide.style.display = "none";
2505
+ }
2506
+
2507
+ function isOverlayElement(el: Element | null): boolean {
2508
+ // Use closest() so that children of overlay elements (e.g. spacing-region
2509
+ // spans inside selectionOverlay that have pointer-events:auto via a CSS rule
2510
+ // and can therefore still be returned by elementFromPoint even when the
2511
+ // parent overlay has pointer-events:none set inline) are also treated as
2512
+ // overlay elements and never used as drag-drop anchor targets.
2513
+ return Boolean(
2514
+ el && el.closest && el.closest("[data-agent-native-edit-overlay]"),
2515
+ );
2516
+ }
2517
+
2518
+ function draggableElementChildren(parent: Element): Element[] {
2519
+ return Array.prototype.slice.call(parent.children).filter(function (child) {
2520
+ return (
2521
+ child.nodeType === 1 &&
2522
+ !isOverlayElement(child) &&
2523
+ !isLayerInteractionBlocked(child)
2524
+ );
2525
+ });
2526
+ }
2527
+
2528
+ function isFlowReorderCandidate(el) {
2529
+ if (!el || !el.parentElement) return false;
2530
+ if (el === document.body || el === document.documentElement) return false;
2531
+ var cs = window.getComputedStyle(el);
2532
+ if (cs.position === "absolute" || cs.position === "fixed") return false;
2533
+ return true;
2534
+ }
2535
+
2536
+ // keep in sync with EditPanel.tsx CONTAINER_TAGS/LEAF_TAGS (~line 1679)
2537
+ var BRIDGE_CONTAINER_TAGS = [
2538
+ "div",
2539
+ "section",
2540
+ "main",
2541
+ "header",
2542
+ "footer",
2543
+ "nav",
2544
+ "article",
2545
+ "aside",
2546
+ "form",
2547
+ "ul",
2548
+ "ol",
2549
+ "figure",
2550
+ "fieldset",
2551
+ "details",
2552
+ "dialog",
2553
+ "blockquote",
2554
+ "table",
2555
+ "tbody",
2556
+ "thead",
2557
+ "tr",
2558
+ ];
2559
+ var BRIDGE_LEAF_TAGS = [
2560
+ "img",
2561
+ "video",
2562
+ "picture",
2563
+ "audio",
2564
+ "canvas",
2565
+ "svg",
2566
+ "path",
2567
+ "input",
2568
+ "textarea",
2569
+ "select",
2570
+ "br",
2571
+ "hr",
2572
+ "iframe",
2573
+ ];
2574
+ var BRIDGE_TEXT_TAGS = [
2575
+ "p",
2576
+ "h1",
2577
+ "h2",
2578
+ "h3",
2579
+ "h4",
2580
+ "h5",
2581
+ "h6",
2582
+ "span",
2583
+ "a",
2584
+ "strong",
2585
+ "em",
2586
+ "label",
2587
+ "li",
2588
+ ];
2589
+
2590
+ function isContainerDropTarget(el: Element | null): boolean {
2591
+ if (!el || el === document.documentElement) return false;
2592
+ if (isOverlayElement(el) || isLayerInteractionBlocked(el)) return false;
2593
+ if (el === document.body) return true;
2594
+ var tag = (el.tagName || "").toLowerCase();
2595
+ // Reject leaf/text tags — they cannot accept children
2596
+ if (
2597
+ BRIDGE_LEAF_TAGS.indexOf(tag) !== -1 ||
2598
+ BRIDGE_TEXT_TAGS.indexOf(tag) !== -1
2599
+ )
2600
+ return false;
2601
+ var cs = window.getComputedStyle(el);
2602
+ if (
2603
+ cs.display === "flex" ||
2604
+ cs.display === "inline-flex" ||
2605
+ cs.display === "grid" ||
2606
+ cs.display === "inline-grid"
2607
+ ) {
2608
+ return true;
2609
+ }
2610
+ return BRIDGE_CONTAINER_TAGS.indexOf(tag) !== -1;
2611
+ }
2612
+
2613
+ function edgePlacementForRect(
2614
+ rect: DOMRect,
2615
+ axis: string,
2616
+ clientX: number,
2617
+ clientY: number,
2618
+ ): string | null {
2619
+ var size = axis === "x" ? rect.width : rect.height;
2620
+ if (!size) return null;
2621
+ var offset = axis === "x" ? clientX - rect.left : clientY - rect.top;
2622
+ if (offset < size * 0.22) return "before";
2623
+ if (offset > size * 0.78) return "after";
2624
+ return null;
2625
+ }
2626
+
2627
+ function parentFlowAxis(parent: Element): string {
2628
+ var cs = window.getComputedStyle(parent);
2629
+ if (cs.display === "flex" || cs.display === "inline-flex") {
2630
+ var isRow = cs.flexDirection && cs.flexDirection.indexOf("row") === 0;
2631
+ // Wrapping row containers need Y-axis awareness for inter-row targeting;
2632
+ // fall back to column-axis insertion so the heuristic picks the right row.
2633
+ var wraps = cs.flexWrap === "wrap" || cs.flexWrap === "wrap-reverse";
2634
+ if (isRow && !wraps) return "x";
2635
+ return "y";
2636
+ }
2637
+ if (cs.display === "grid" || cs.display === "inline-grid") {
2638
+ var cols = (cs.gridTemplateColumns || "")
2639
+ .split(" ")
2640
+ .filter(Boolean).length;
2641
+ return cols > 1 ? "x" : "y";
2642
+ }
2643
+ return "y";
2644
+ }
2645
+
2646
+ function reorderTargetForPoint(el, clientX, clientY) {
2647
+ if (!el || !el.parentElement) return null;
2648
+ var hit = elementFromEditorPoint(clientX, clientY);
2649
+ if (
2650
+ hit &&
2651
+ hit !== document.documentElement &&
2652
+ hit !== el &&
2653
+ !el.contains(hit) &&
2654
+ !isOverlayElement(hit)
2655
+ ) {
2656
+ if (isContainerDropTarget(hit)) {
2657
+ var containerRect = hit.getBoundingClientRect();
2658
+ var edgeAxis = hit.parentElement
2659
+ ? parentFlowAxis(hit.parentElement)
2660
+ : parentFlowAxis(hit);
2661
+ var edgePlacement = edgePlacementForRect(
2662
+ containerRect,
2663
+ edgeAxis,
2664
+ clientX,
2665
+ clientY,
2666
+ );
2667
+ if (!edgePlacement) {
2668
+ return {
2669
+ anchor: hit,
2670
+ placement: "inside",
2671
+ axis: parentFlowAxis(hit),
2672
+ };
2673
+ }
2674
+ return { anchor: hit, placement: edgePlacement, axis: edgeAxis };
2675
+ }
2676
+ var hitParent = hit.parentElement;
2677
+ if (hitParent) {
2678
+ var hitAxis = parentFlowAxis(hitParent);
2679
+ var hitRect = hit.getBoundingClientRect();
2680
+ var hitCenter =
2681
+ hitAxis === "x"
2682
+ ? hitRect.left + hitRect.width / 2
2683
+ : hitRect.top + hitRect.height / 2;
2684
+ var hitPointer = hitAxis === "x" ? clientX : clientY;
2685
+ return {
2686
+ anchor: hit,
2687
+ placement: hitPointer < hitCenter ? "before" : "after",
2688
+ axis: hitAxis,
2689
+ };
2690
+ }
2691
+ }
2692
+ var parent = el.parentElement;
2693
+ var axis = parentFlowAxis(parent);
2694
+ var siblings = draggableElementChildren(parent).filter(function (child) {
2695
+ return child !== el;
2696
+ });
2697
+ if (!siblings.length) return null;
2698
+ var beforeTarget = null;
2699
+ for (var i = 0; i < siblings.length; i += 1) {
2700
+ var rect = siblings[i].getBoundingClientRect();
2701
+ var center =
2702
+ axis === "x" ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
2703
+ var pointer = axis === "x" ? clientX : clientY;
2704
+ if (pointer < center) {
2705
+ beforeTarget = siblings[i];
2706
+ break;
2707
+ }
2708
+ }
2709
+ var anchor = beforeTarget || siblings[siblings.length - 1];
2710
+ var placement = beforeTarget ? "before" : "after";
2711
+ return { anchor: anchor, placement: placement, axis: axis };
2712
+ }
2713
+
2714
+ function showInsertionGuideFor(target) {
2715
+ if (!target || !target.anchor) {
2716
+ hideInsertionGuide();
2717
+ return;
2718
+ }
2719
+ var rect = target.anchor.getBoundingClientRect();
2720
+ insertionGuide.style.display = "block";
2721
+ insertionGuide.style.background = "var(--design-editor-accent-color)";
2722
+ insertionGuide.style.border = "0";
2723
+ insertionGuide.style.borderRadius = "999px";
2724
+ insertionGuide.style.boxShadow =
2725
+ "0 0 0 1px var(--design-editor-accent-color)";
2726
+ if (target.placement === "inside") {
2727
+ insertionGuide.style.left = rect.left + "px";
2728
+ insertionGuide.style.top = rect.top + "px";
2729
+ insertionGuide.style.width = rect.width + "px";
2730
+ insertionGuide.style.height = rect.height + "px";
2731
+ insertionGuide.style.background =
2732
+ "color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)";
2733
+ insertionGuide.style.border =
2734
+ "2px solid var(--design-editor-accent-color)";
2735
+ insertionGuide.style.borderRadius = "2px";
2736
+ insertionGuide.style.boxShadow = "none";
2737
+ return;
2738
+ }
2739
+ if (target.axis === "x") {
2740
+ var x = target.placement === "before" ? rect.left : rect.right;
2741
+ insertionGuide.style.left = x + "px";
2742
+ insertionGuide.style.top = rect.top + "px";
2743
+ insertionGuide.style.width = "2px";
2744
+ insertionGuide.style.height = rect.height + "px";
2745
+ } else {
2746
+ var y = target.placement === "before" ? rect.top : rect.bottom;
2747
+ insertionGuide.style.left = rect.left + "px";
2748
+ insertionGuide.style.top = y + "px";
2749
+ insertionGuide.style.width = rect.width + "px";
2750
+ insertionGuide.style.height = "2px";
2751
+ }
2752
+ }
2753
+
2754
+ function applyRuntimeReorder(el, target) {
2755
+ if (!el || !target || !target.anchor || !target.anchor.parentElement)
2756
+ return;
2757
+ if (target.placement === "inside") {
2758
+ target.anchor.appendChild(el);
2759
+ return;
2760
+ }
2761
+ var parent = target.anchor.parentElement;
2762
+ if (target.placement === "before") {
2763
+ parent.insertBefore(el, target.anchor);
2764
+ } else {
2765
+ parent.insertBefore(el, target.anchor.nextSibling);
2766
+ }
2767
+ }
2768
+
2769
+ function postVisualStructureChange(el, target, origin) {
2770
+ if (!el || !target || !target.anchor) return;
2771
+ var requestId =
2772
+ "move-" + Date.now() + "-" + Math.random().toString(16).slice(2);
2773
+ pendingStructureMoves[requestId] = {
2774
+ requestId: requestId,
2775
+ el: el,
2776
+ target: target,
2777
+ origin: origin || null,
2778
+ };
2779
+ (window.parent as Window).postMessage(
2780
+ {
2781
+ type: "visual-structure-change",
2782
+ requestId: requestId,
2783
+ selector: getSelector(el),
2784
+ sourceId: getSourceId(el),
2785
+ anchorSelector: getSelector(target.anchor),
2786
+ anchorSourceId: getSourceId(target.anchor),
2787
+ placement: target.placement,
2788
+ payload: getElementInfo(el),
2789
+ },
2790
+ "*",
2791
+ );
2792
+ }
2793
+
2794
+ function postVisualDuplicateChange(originalEl, cloneEl, target) {
2795
+ if (!originalEl || !cloneEl) return;
2796
+ (window.parent as Window).postMessage(
2797
+ {
2798
+ type: "visual-duplicate-change",
2799
+ selector: getSelector(originalEl),
2800
+ sourceId: getSourceId(originalEl),
2801
+ anchorSelector:
2802
+ target && target.anchor ? getSelector(target.anchor) : "",
2803
+ anchorSourceId:
2804
+ target && target.anchor ? getSourceId(target.anchor) : "",
2805
+ placement: target && target.placement ? target.placement : "after",
2806
+ cloneHtml: cloneEl.outerHTML,
2807
+ payload: getElementInfo(cloneEl),
2808
+ },
2809
+ "*",
2810
+ );
2811
+ }
2812
+
2813
+ function startMove(e) {
2814
+ if (!selectedEl) return;
2815
+ if (isLayerInteractionBlocked(selectedEl)) return;
2816
+ e.preventDefault();
2817
+ e.stopPropagation();
2818
+ if (e.stopImmediatePropagation) e.stopImmediatePropagation();
2819
+ var events = dragEventNames(e);
2820
+ var originalSelectedEl = selectedEl;
2821
+ var duplicatedForDrag = false;
2822
+ if (
2823
+ e.altKey &&
2824
+ selectedEl !== document.body &&
2825
+ selectedEl !== document.documentElement
2826
+ ) {
2827
+ var clone = selectedEl.cloneNode(true);
2828
+ resetRuntimeStableIds(clone);
2829
+ selectedEl.parentElement.insertBefore(clone, selectedEl.nextSibling);
2830
+ selectedEl = clone;
2831
+ duplicatedForDrag = true;
2832
+ positionOverlay(selectionOverlay, selectedEl);
2833
+ (window.parent as Window).postMessage(
2834
+ { type: "element-select", payload: getElementInfo(selectedEl) },
2835
+ "*",
2836
+ );
2837
+ }
2838
+ if (isFlowReorderCandidate(selectedEl)) {
2839
+ // Snapshot the element being reordered so a concurrent select-element or
2840
+ // clear-selection postMessage cannot mutate the wrong element mid-drag.
2841
+ var reorderEl = selectedEl;
2842
+ var currentTarget = reorderTargetForPoint(
2843
+ reorderEl,
2844
+ e.clientX,
2845
+ e.clientY,
2846
+ );
2847
+ showInsertionGuideFor(currentTarget);
2848
+ // Cross-screen drag state: true when the pointer is outside this iframe's
2849
+ // viewport bounds. The host frame renders the ghost + highlight and owns
2850
+ // the drop when this is true; the bridge suppresses its in-iframe reorder.
2851
+ var pointerOutsideIframe = false;
2852
+ var reorderSelector = getSelector(reorderEl);
2853
+ var reorderSourceId = getSourceId(reorderEl);
2854
+ function onReorderMove(ev) {
2855
+ var vw = window.innerWidth;
2856
+ var vh = window.innerHeight;
2857
+ var cx = ev.clientX;
2858
+ var cy = ev.clientY;
2859
+ var outside = cx < 0 || cy < 0 || cx > vw || cy > vh;
2860
+ pointerOutsideIframe = outside;
2861
+ // Always notify the host frame so it can track the cursor position,
2862
+ // render the ghost, and highlight the target screen.
2863
+ (window.parent as Window).postMessage(
2864
+ {
2865
+ type: "agent-native:cross-screen-drag",
2866
+ phase: "move",
2867
+ selector: reorderSelector,
2868
+ sourceId: reorderSourceId,
2869
+ iframeX: cx,
2870
+ iframeY: cy,
2871
+ viewportW: vw,
2872
+ viewportH: vh,
2873
+ },
2874
+ "*",
2875
+ );
2876
+ if (outside) {
2877
+ // Cursor left this iframe — hide the in-iframe insertion guide so
2878
+ // it does not render while the host shows a cross-screen drop target.
2879
+ hideInsertionGuide();
2880
+ showTransformBadge("Move layer", cx, cy);
2881
+ } else {
2882
+ // Cursor is inside this iframe — use existing in-iframe behavior.
2883
+ currentTarget = reorderTargetForPoint(reorderEl, cx, cy);
2884
+ showInsertionGuideFor(currentTarget);
2885
+ showTransformBadge(currentTarget ? "Move layer" : "Move", cx, cy);
2886
+ }
2887
+ }
2888
+ function onReorderEscape() {
2889
+ document.removeEventListener(events.move, onReorderMove, true);
2890
+ document.removeEventListener(events.up, onReorderUp, true);
2891
+ document.removeEventListener("keydown", onReorderKeyDown, true);
2892
+ hideTransformBadge();
2893
+ hideInsertionGuide();
2894
+ (window.parent as Window).postMessage(
2895
+ { type: "agent-native:cross-screen-drag", phase: "cancel" },
2896
+ "*",
2897
+ );
2898
+ // Revert any clone that was inserted for alt-drag.
2899
+ if (
2900
+ duplicatedForDrag &&
2901
+ reorderEl &&
2902
+ reorderEl !== originalSelectedEl
2903
+ ) {
2904
+ if (reorderEl.parentElement)
2905
+ reorderEl.parentElement.removeChild(reorderEl);
2906
+ selectedEl = originalSelectedEl;
2907
+ positionOverlay(selectionOverlay, selectedEl);
2908
+ (window.parent as Window).postMessage(
2909
+ { type: "element-select", payload: getElementInfo(selectedEl) },
2910
+ "*",
2911
+ );
2912
+ }
2913
+ }
2914
+ function onReorderKeyDown(ev) {
2915
+ if (ev.key === "Escape") {
2916
+ ev.preventDefault();
2917
+ ev.stopPropagation();
2918
+ if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();
2919
+ onReorderEscape();
2920
+ }
2921
+ }
2922
+ function onReorderUp(ev) {
2923
+ document.removeEventListener(events.move, onReorderMove, true);
2924
+ document.removeEventListener(events.up, onReorderUp, true);
2925
+ document.removeEventListener("keydown", onReorderKeyDown, true);
2926
+ hideTransformBadge();
2927
+ hideInsertionGuide();
2928
+ var vw = window.innerWidth;
2929
+ var vh = window.innerHeight;
2930
+ var cx = ev ? ev.clientX : 0;
2931
+ var cy = ev ? ev.clientY : 0;
2932
+ var outsideOnDrop = cx < 0 || cy < 0 || cx > vw || cy > vh;
2933
+ // Post the end message so the host can finalize a cross-screen drop.
2934
+ (window.parent as Window).postMessage(
2935
+ {
2936
+ type: "agent-native:cross-screen-drag",
2937
+ phase: "end",
2938
+ selector: reorderSelector,
2939
+ sourceId: reorderSourceId,
2940
+ iframeX: cx,
2941
+ iframeY: cy,
2942
+ viewportW: vw,
2943
+ viewportH: vh,
2944
+ },
2945
+ "*",
2946
+ );
2947
+ // When the pointer is outside this iframe at release, the host owns the
2948
+ // move (cross-screen drop). Do NOT apply the in-iframe reorder so we
2949
+ // avoid a ghost element left in screen A's DOM.
2950
+ // Use outsideOnDrop only — pointerOutsideIframe is stale when the user
2951
+ // briefly exits the iframe and re-enters before releasing. The host
2952
+ // already clears cross-screen state on re-entry so checking the
2953
+ // momentary excursion flag here would wrongly drop the element nowhere.
2954
+ if (outsideOnDrop) return;
2955
+ if (!currentTarget) {
2956
+ // No valid drop target — clean up the clone if one was inserted so
2957
+ // no ghost element is left in the DOM.
2958
+ if (
2959
+ duplicatedForDrag &&
2960
+ reorderEl &&
2961
+ reorderEl !== originalSelectedEl
2962
+ ) {
2963
+ if (reorderEl.parentElement)
2964
+ reorderEl.parentElement.removeChild(reorderEl);
2965
+ selectedEl = originalSelectedEl;
2966
+ positionOverlay(selectionOverlay, selectedEl);
2967
+ (window.parent as Window).postMessage(
2968
+ { type: "element-select", payload: getElementInfo(selectedEl) },
2969
+ "*",
2970
+ );
2971
+ }
2972
+ return;
2973
+ }
2974
+ if (duplicatedForDrag) {
2975
+ applyRuntimeReorder(reorderEl, currentTarget);
2976
+ postVisualDuplicateChange(
2977
+ originalSelectedEl,
2978
+ reorderEl,
2979
+ currentTarget,
2980
+ );
2981
+ } else {
2982
+ // Capture the pre-drag DOM anchor so we can revert if the parent
2983
+ // reports applied===false on the structure-ack.
2984
+ var prevParent = reorderEl.parentElement;
2985
+ var prevNextSibling = reorderEl.nextSibling;
2986
+ // Optimistically apply the reorder in the DOM for immediate
2987
+ // visual feedback; the visual-structure-ack handler will confirm
2988
+ // or revert once the parent processes the change.
2989
+ applyRuntimeReorder(reorderEl, currentTarget);
2990
+ postVisualStructureChange(reorderEl, currentTarget, {
2991
+ prevParent: prevParent,
2992
+ prevNextSibling: prevNextSibling,
2993
+ });
2994
+ }
2995
+ }
2996
+ document.addEventListener(events.move, onReorderMove, true);
2997
+ document.addEventListener(events.up, onReorderUp, true);
2998
+ document.addEventListener("keydown", onReorderKeyDown, true);
2999
+ return;
3000
+ }
3001
+ ensurePositionable(selectedEl);
3002
+ var cs = window.getComputedStyle(selectedEl);
3003
+ var originLeft = readPx((selectedEl as HTMLElement).style.left || cs.left);
3004
+ var originTop = readPx(selectedEl.style.top || cs.top);
3005
+ var startX = e.clientX;
3006
+ var startY = e.clientY;
3007
+ // Snapshot the element being moved so that a concurrent select-element or
3008
+ // clear-selection postMessage cannot swap selectedEl mid-drag and cause
3009
+ // mutations on the wrong element or a null-deref in onUp.
3010
+ var dragEl = selectedEl;
3011
+ var moved = false;
3012
+ var DRAG_THRESHOLD = 3;
3013
+ function onMove(ev) {
3014
+ if (
3015
+ !moved &&
3016
+ Math.hypot(ev.clientX - startX, ev.clientY - startY) > DRAG_THRESHOLD
3017
+ ) {
3018
+ moved = true;
3019
+ }
3020
+ var nextLeft = originLeft + ev.clientX - startX;
3021
+ var nextTop = originTop + ev.clientY - startY;
3022
+ (dragEl as HTMLElement).style.left = Math.round(nextLeft) + "px";
3023
+ (dragEl as HTMLElement).style.top = Math.round(nextTop) + "px";
3024
+ showTransformBadge(
3025
+ "X " + Math.round(nextLeft) + " Y " + Math.round(nextTop),
3026
+ ev.clientX,
3027
+ ev.clientY,
3028
+ );
3029
+ refreshOverlays();
3030
+ }
3031
+ function onUp() {
3032
+ document.removeEventListener(events.move, onMove, true);
3033
+ document.removeEventListener(events.up, onUp, true);
3034
+ hideTransformBadge();
3035
+ if (!dragEl) return;
3036
+ if (duplicatedForDrag && !moved) {
3037
+ // Alt-click with no real drag — remove the premature clone and restore the original selection.
3038
+ if (dragEl.parentElement) dragEl.parentElement.removeChild(dragEl);
3039
+ selectedEl = originalSelectedEl;
3040
+ positionOverlay(selectionOverlay, selectedEl);
3041
+ (window.parent as Window).postMessage(
3042
+ { type: "element-select", payload: getElementInfo(selectedEl) },
3043
+ "*",
3044
+ );
3045
+ return;
3046
+ }
3047
+ if (duplicatedForDrag) {
3048
+ postVisualDuplicateChange(originalSelectedEl, dragEl);
3049
+ } else {
3050
+ (window.parent as Window).postMessage(
3051
+ {
3052
+ type: "visual-style-change",
3053
+ selector: getSelector(dragEl),
3054
+ styles: {
3055
+ position: (dragEl as HTMLElement).style.position,
3056
+ left: (dragEl as HTMLElement).style.left,
3057
+ top: (dragEl as HTMLElement).style.top,
3058
+ },
3059
+ payload: getElementInfo(dragEl),
3060
+ },
3061
+ "*",
3062
+ );
3063
+ }
3064
+ }
3065
+ document.addEventListener(events.move, onMove, true);
3066
+ document.addEventListener(events.up, onUp, true);
3067
+ }
3068
+
3069
+ function startResize(handle, e) {
3070
+ if (!selectedEl) return;
3071
+ if (isLayerInteractionBlocked(selectedEl)) return;
3072
+ e.preventDefault();
3073
+ e.stopPropagation();
3074
+ var events = dragEventNames(e);
3075
+ ensurePositionable(selectedEl);
3076
+ var cs = window.getComputedStyle(selectedEl);
3077
+ // Bug fix: use CSS width/height (not getBoundingClientRect) for the resize
3078
+ // origin dimensions so that rotated elements don't use the inflated
3079
+ // axis-aligned bounding box as the starting size.
3080
+ var originW = readPx(selectedEl.style.width || cs.width);
3081
+ var originH = readPx(selectedEl.style.height || cs.height);
3082
+ var origin = {
3083
+ left: readPx(selectedEl.style.left || cs.left),
3084
+ top: readPx(selectedEl.style.top || cs.top),
3085
+ width: originW,
3086
+ height: originH,
3087
+ ratio: originW / Math.max(1, originH),
3088
+ };
3089
+ var startX = e.clientX;
3090
+ var startY = e.clientY;
3091
+ // Snapshot the element so a concurrent clear-selection postMessage cannot
3092
+ // cause a null-deref in onMove/onUp.
3093
+ var resizeEl = selectedEl;
3094
+ // Capture the element rotation once at drag-start so per-move projection is
3095
+ // cheap and consistent even if the transform changes during the drag.
3096
+ var resizeTheta = (currentRotation(resizeEl) * Math.PI) / 180;
3097
+ function nextRect(ev) {
3098
+ var screenDx = ev.clientX - startX;
3099
+ var screenDy = ev.clientY - startY;
3100
+ // Project the screen-space pointer delta into the element's local
3101
+ // (un-rotated) coordinate frame so handles behave relative to the
3102
+ // visible rotated box rather than screen axes.
3103
+ var cosT = Math.cos(resizeTheta);
3104
+ var sinT = Math.sin(resizeTheta);
3105
+ var dx = screenDx * cosT + screenDy * sinT;
3106
+ var dy = -screenDx * sinT + screenDy * cosT;
3107
+ var left = origin.left;
3108
+ var top = origin.top;
3109
+ var width = origin.width;
3110
+ var height = origin.height;
3111
+ if (handle.indexOf("w") !== -1) {
3112
+ left = origin.left + dx;
3113
+ width = origin.width - dx;
3114
+ }
3115
+ if (handle.indexOf("e") !== -1) width = origin.width + dx;
3116
+ if (handle.indexOf("n") !== -1) {
3117
+ top = origin.top + dy;
3118
+ height = origin.height - dy;
3119
+ }
3120
+ if (handle.indexOf("s") !== -1) height = origin.height + dy;
3121
+ // Apply Shift / scaleToolEnabled aspect-ratio lock BEFORE the min-size
3122
+ // clamp so the ratio is computed from unclamped values (bug fix).
3123
+ if (ev.shiftKey) {
3124
+ // Shift locks aspect ratio for ALL 8 handles (corners and edges).
3125
+ if (handle === "e" || handle === "w") {
3126
+ height = width / origin.ratio;
3127
+ } else if (handle === "n" || handle === "s") {
3128
+ width = height * origin.ratio;
3129
+ } else if (handle.length === 2) {
3130
+ if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
3131
+ else width = height * origin.ratio;
3132
+ }
3133
+ }
3134
+ if (scaleToolEnabled) {
3135
+ // Scale tool: enforce aspect ratio on all 8 handles, not just corners.
3136
+ if (handle === "e" || handle === "w") {
3137
+ height = width / origin.ratio;
3138
+ } else if (handle === "n" || handle === "s") {
3139
+ width = height * origin.ratio;
3140
+ } else if (handle.length === 2) {
3141
+ if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
3142
+ else width = height * origin.ratio;
3143
+ }
3144
+ }
3145
+ // Clamp to minimum size.
3146
+ var clampedW = Math.max(8, width);
3147
+ var clampedH = Math.max(8, height);
3148
+ // After clamping, re-apply the ratio if Shift or scale tool is active so
3149
+ // the clamped dimension doesn't silently break the locked aspect ratio.
3150
+ if (ev.shiftKey || scaleToolEnabled) {
3151
+ if (clampedW !== width) {
3152
+ // Width was clamped; re-derive height from the clamped width.
3153
+ clampedH = Math.max(8, clampedW / origin.ratio);
3154
+ } else if (clampedH !== height) {
3155
+ // Height was clamped; re-derive width from the clamped height.
3156
+ clampedW = Math.max(8, clampedH * origin.ratio);
3157
+ }
3158
+ }
3159
+ width = clampedW;
3160
+ height = clampedH;
3161
+ // Re-anchor the pinned edge for w/n handles after aspect-ratio lock and
3162
+ // clamping so the opposite (e/s) edge stays fixed regardless of whether
3163
+ // the dimension change was driven by raw dx/dy or by the ratio lock.
3164
+ if (handle.indexOf("w") !== -1)
3165
+ left = origin.left + (origin.width - width);
3166
+ if (handle.indexOf("n") !== -1)
3167
+ top = origin.top + (origin.height - height);
3168
+ if (ev.altKey) {
3169
+ if (handle.indexOf("w") !== -1 || handle.indexOf("e") !== -1)
3170
+ left = origin.left - (width - origin.width) / 2;
3171
+ if (handle.indexOf("n") !== -1 || handle.indexOf("s") !== -1)
3172
+ top = origin.top - (height - origin.height) / 2;
3173
+ }
3174
+ return { left: left, top: top, width: width, height: height };
3175
+ }
3176
+ function onMove(ev) {
3177
+ if (!resizeEl) return;
3178
+ var rect = nextRect(ev);
3179
+ resizeEl.style.left = Math.round(rect.left) + "px";
3180
+ resizeEl.style.top = Math.round(rect.top) + "px";
3181
+ resizeEl.style.width = Math.round(rect.width) + "px";
3182
+ resizeEl.style.height = Math.round(rect.height) + "px";
3183
+ showTransformBadge(
3184
+ Math.round(rect.width) + " x " + Math.round(rect.height),
3185
+ ev.clientX,
3186
+ ev.clientY,
3187
+ );
3188
+ refreshOverlays();
3189
+ }
3190
+ function onUp() {
3191
+ document.removeEventListener(events.move, onMove, true);
3192
+ document.removeEventListener(events.up, onUp, true);
3193
+ hideTransformBadge();
3194
+ if (!resizeEl) return;
3195
+ (window.parent as Window).postMessage(
3196
+ {
3197
+ type: "visual-style-change",
3198
+ selector: getSelector(resizeEl),
3199
+ styles: {
3200
+ position: resizeEl.style.position,
3201
+ left: resizeEl.style.left,
3202
+ top: resizeEl.style.top,
3203
+ width: resizeEl.style.width,
3204
+ height: resizeEl.style.height,
3205
+ },
3206
+ payload: getElementInfo(resizeEl),
3207
+ },
3208
+ "*",
3209
+ );
3210
+ }
3211
+ document.addEventListener(events.move, onMove, true);
3212
+ document.addEventListener(events.up, onUp, true);
3213
+ }
3214
+
3215
+ function startRotate(e) {
3216
+ if (!selectedEl) return;
3217
+ if (isLayerInteractionBlocked(selectedEl)) return;
3218
+ e.preventDefault();
3219
+ e.stopPropagation();
3220
+ var events = dragEventNames(e);
3221
+ // getBoundingClientRect is correct here — we only need the element center
3222
+ // for angle math, and the element's visual position is what we want.
3223
+ var rect = selectedEl.getBoundingClientRect();
3224
+ var center = {
3225
+ x: rect.left + rect.width / 2,
3226
+ y: rect.top + rect.height / 2,
3227
+ };
3228
+ var originAngle =
3229
+ (Math.atan2(e.clientY - center.y, e.clientX - center.x) * 180) / Math.PI;
3230
+ var originRotation = currentRotation(selectedEl);
3231
+ // Snapshot so a concurrent clear-selection postMessage cannot cause a
3232
+ // null-deref in onMove/onUp.
3233
+ var rotateEl = selectedEl;
3234
+ function onMove(ev) {
3235
+ if (!rotateEl) return;
3236
+ var pointerAngle =
3237
+ (Math.atan2(ev.clientY - center.y, ev.clientX - center.x) * 180) /
3238
+ Math.PI;
3239
+ var next = originRotation + pointerAngle - originAngle;
3240
+ if (ev.shiftKey) next = Math.round(next / 15) * 15;
3241
+ next = Math.round(next);
3242
+ rotateEl.style.transform = mergeRotation(rotateEl, next);
3243
+ showTransformBadge(next + "deg", ev.clientX, ev.clientY);
3244
+ refreshOverlays();
3245
+ }
3246
+ function onUp() {
3247
+ document.removeEventListener(events.move, onMove, true);
3248
+ document.removeEventListener(events.up, onUp, true);
3249
+ hideTransformBadge();
3250
+ if (!rotateEl) return;
3251
+ (window.parent as Window).postMessage(
3252
+ {
3253
+ type: "visual-style-change",
3254
+ selector: getSelector(rotateEl),
3255
+ styles: { transform: rotateEl.style.transform },
3256
+ payload: getElementInfo(rotateEl),
3257
+ },
3258
+ "*",
3259
+ );
3260
+ }
3261
+ document.addEventListener(events.move, onMove, true);
3262
+ document.addEventListener(events.up, onUp, true);
3263
+ }
3264
+
3265
+ function clearPendingShieldDrag() {
3266
+ if (!pendingShieldDrag) return;
3267
+ document.removeEventListener(
3268
+ pendingShieldDrag.move,
3269
+ pendingShieldDrag.onMove,
3270
+ true,
3271
+ );
3272
+ document.removeEventListener(
3273
+ pendingShieldDrag.up,
3274
+ pendingShieldDrag.onUp,
3275
+ true,
3276
+ );
3277
+ pendingShieldDrag = null;
3278
+ }
3279
+
3280
+ function beginPotentialShieldDrag(e) {
3281
+ stopNativeInteraction(e);
3282
+ if (e.button !== 0 || activeTextEditEl) return;
3283
+ var events = dragEventNames(e);
3284
+ var hit = elementFromEditorPoint(e.clientX, e.clientY);
3285
+ if (!hit || hit === document.body || hit === document.documentElement)
3286
+ return;
3287
+ var dragTarget =
3288
+ selectedEl &&
3289
+ document.documentElement.contains(selectedEl) &&
3290
+ selectedEl.contains(hit)
3291
+ ? selectedEl
3292
+ : selectionTargetForHit(hit);
3293
+ var clickTarget = selectionTargetForHit(hit);
3294
+ if (
3295
+ !dragTarget ||
3296
+ dragTarget === document.body ||
3297
+ dragTarget === document.documentElement ||
3298
+ isLayerInteractionBlocked(dragTarget)
3299
+ ) {
3300
+ return;
3301
+ }
3302
+ var startX = e.clientX;
3303
+ var startY = e.clientY;
3304
+ var didStartDrag = false;
3305
+ function selectTarget(target) {
3306
+ selectedEl = target;
3307
+ positionOverlay(selectionOverlay, selectedEl);
3308
+ (window.parent as Window).postMessage(
3309
+ { type: "element-select", payload: getElementInfo(selectedEl) },
3310
+ "*",
3311
+ );
3312
+ }
3313
+ function onMove(ev) {
3314
+ if (Math.hypot(ev.clientX - startX, ev.clientY - startY) <= 3) return;
3315
+ clearPendingShieldDrag();
3316
+ didStartDrag = true;
3317
+ selectTarget(dragTarget);
3318
+ suppressNextShieldClickBriefly();
3319
+ startMove(ev);
3320
+ }
3321
+ function onUp(ev) {
3322
+ clearPendingShieldDrag();
3323
+ if (didStartDrag) return;
3324
+ if (ev) stopNativeInteraction(ev);
3325
+ selectTarget(clickTarget || dragTarget);
3326
+ suppressNextShieldClickBriefly();
3327
+ }
3328
+ clearPendingShieldDrag();
3329
+ pendingShieldDrag = {
3330
+ move: events.move,
3331
+ up: events.up,
3332
+ onMove: onMove,
3333
+ onUp: onUp,
3334
+ };
3335
+ document.addEventListener(events.move, onMove, true);
3336
+ document.addEventListener(events.up, onUp, true);
3337
+ }
3338
+
3339
+ selectionOverlay.addEventListener(
3340
+ "mousedown",
3341
+ function (e) {
3342
+ var spacingKey =
3343
+ e.target &&
3344
+ e.target.getAttribute &&
3345
+ e.target.getAttribute("data-spacing-key");
3346
+ if (spacingKey) {
3347
+ startSpacingDrag(spacingKey, e);
3348
+ return;
3349
+ }
3350
+ var resizeHandle =
3351
+ e.target &&
3352
+ e.target.getAttribute &&
3353
+ e.target.getAttribute("data-agent-native-edit-handle");
3354
+ if (!resizeHandle && e.target && e.target.getAttribute) {
3355
+ resizeHandle = e.target.getAttribute("data-agent-native-edge-handle");
3356
+ }
3357
+ if (resizeHandle) {
3358
+ startResize(resizeHandle, e);
3359
+ return;
3360
+ }
3361
+ var rotateHandle =
3362
+ e.target &&
3363
+ e.target.getAttribute &&
3364
+ e.target.getAttribute("data-agent-native-rotate-handle");
3365
+ if (rotateHandle) {
3366
+ startRotate(e);
3367
+ return;
3368
+ }
3369
+ startMove(e);
3370
+ },
3371
+ true,
3372
+ );
3373
+
3374
+ shieldOverlay.addEventListener("pointerdown", beginPotentialShieldDrag, true);
3375
+
3376
+ ["pointerdown", "pointerup", "mousedown", "mouseup", "auxclick"].forEach(
3377
+ function (type) {
3378
+ shieldOverlay.addEventListener(type, stopNativeInteraction, true);
3379
+ },
3380
+ );
3381
+
3382
+ function stopBlockedLayerInteraction(e) {
3383
+ if (isOverlayElement(e.target)) return;
3384
+ var target = e.target && e.target.nodeType === 1 ? e.target : null;
3385
+ if (!target || !isLayerInteractionBlocked(target)) return;
3386
+ stopNativeInteraction(e);
3387
+ }
3388
+
3389
+ [
3390
+ "pointerdown",
3391
+ "pointerup",
3392
+ "mousedown",
3393
+ "mouseup",
3394
+ "click",
3395
+ "auxclick",
3396
+ ].forEach(function (type) {
3397
+ document.addEventListener(type, stopBlockedLayerInteraction, true);
3398
+ });
3399
+
3400
+ shieldOverlay.addEventListener("click", selectElementAtEvent, true);
3401
+ shieldOverlay.addEventListener("contextmenu", openContextMenuAtEvent, true);
3402
+ selectionOverlay.addEventListener(
3403
+ "contextmenu",
3404
+ openContextMenuAtEvent,
3405
+ true,
3406
+ );
3407
+ document.addEventListener(
3408
+ "contextmenu",
3409
+ function (e) {
3410
+ if (isOverlayElement(e.target)) return;
3411
+ openContextMenuAtEvent(e);
3412
+ },
3413
+ true,
3414
+ );
3415
+
3416
+ document.addEventListener(
3417
+ "keydown",
3418
+ function (e) {
3419
+ if (!shouldForwardDesignHotkey(e)) return;
3420
+ stopNativeInteraction(e);
3421
+ if (e.key === "Escape") clearRuntimeSelection();
3422
+ (window.parent as Window).postMessage(
3423
+ {
3424
+ type: "design-hotkey",
3425
+ key: e.key,
3426
+ code: e.code,
3427
+ metaKey: !!e.metaKey,
3428
+ ctrlKey: !!e.ctrlKey,
3429
+ shiftKey: !!e.shiftKey,
3430
+ altKey: !!e.altKey,
3431
+ repeat: !!e.repeat,
3432
+ },
3433
+ "*",
3434
+ );
3435
+ },
3436
+ true,
3437
+ );
3438
+
3439
+ function placeTextCaretFromPoint(target, clientX, clientY) {
3440
+ try {
3441
+ var range = null;
3442
+ if (document.caretRangeFromPoint) {
3443
+ range = document.caretRangeFromPoint(clientX, clientY);
3444
+ } else if (document.caretPositionFromPoint) {
3445
+ var position = document.caretPositionFromPoint(clientX, clientY);
3446
+ if (position) {
3447
+ range = document.createRange();
3448
+ range.setStart(position.offsetNode, position.offset);
3449
+ }
3450
+ }
3451
+ if (!range) {
3452
+ range = document.createRange();
3453
+ range.selectNodeContents(target);
3454
+ range.collapse(false);
3455
+ }
3456
+ var selection = window.getSelection();
3457
+ selection.removeAllRanges();
3458
+ selection.addRange(range);
3459
+ } catch (err) {
3460
+ try {
3461
+ var fallbackRange = document.createRange();
3462
+ fallbackRange.selectNodeContents(target);
3463
+ fallbackRange.collapse(false);
3464
+ var fallbackSelection = window.getSelection();
3465
+ fallbackSelection.removeAllRanges();
3466
+ fallbackSelection.addRange(fallbackRange);
3467
+ } catch (_err) {}
3468
+ }
3469
+ }
3470
+
3471
+ function beginTextEditingFromEvent(e) {
3472
+ if (activeTextEditEl && e.target && activeTextEditEl.contains(e.target))
3473
+ return;
3474
+ if (!textEditingEnabled) {
3475
+ stopNativeInteraction(e);
3476
+ return;
3477
+ }
3478
+ stopNativeInteraction(e);
3479
+ var target = findTextEditTarget(
3480
+ elementFromEditorPoint(e.clientX, e.clientY),
3481
+ );
3482
+ if (!target || target.nodeType !== 1) return;
3483
+ // Anchor the selection identity to the nearest source-backed element. Text
3484
+ // editing still operates on the actual target text node, but a later
3485
+ // style edit posts from selectedEl, so it must point at a patchable
3486
+ // code-layer node rather than a runtime-only descendant (which would emit a
3487
+ // brittle body > div:nth-of-type(...) selector that never resolves).
3488
+ selectedEl = selectionTargetForHit(target) || target;
3489
+ var originalText = target.textContent || "";
3490
+ var originalHtml = target.innerHTML || "";
3491
+ var committed = false;
3492
+ activeTextEditEl = target;
3493
+ target.setAttribute("contenteditable", "true");
3494
+ target.setAttribute("data-agent-native-text-editing", "true");
3495
+ target.style.cursor = "text";
3496
+ target.style.outline = "1.5px solid var(--design-editor-accent-color)";
3497
+ target.style.outlineOffset = "2px";
3498
+ setTextEditingPointerPassthrough(true);
3499
+ positionOverlay(selectionOverlay, target);
3500
+ (window.parent as Window).postMessage(
3501
+ { type: "element-select", payload: getElementInfo(target) },
3502
+ "*",
3503
+ );
3504
+ (window.parent as Window).postMessage(
3505
+ { type: "element-dblclick-text", payload: getElementInfo(target) },
3506
+ "*",
3507
+ );
3508
+ postTextEditingState(target, true);
3509
+
3510
+ function finish(commit) {
3511
+ if (committed) return;
3512
+ committed = true;
3513
+ target.removeEventListener("blur", onBlur, true);
3514
+ target.removeEventListener("keydown", onKeyDown, true);
3515
+ target.removeEventListener("paste", onPaste, true);
3516
+ target.removeEventListener("keyup", onSelectionChange, true);
3517
+ target.removeEventListener("mouseup", onSelectionChange, true);
3518
+ document.removeEventListener("selectionchange", onSelectionChange);
3519
+ target.removeAttribute("contenteditable");
3520
+ target.removeAttribute("data-agent-native-text-editing");
3521
+ target.style.cursor = "";
3522
+ target.style.outline = "";
3523
+ target.style.outlineOffset = "";
3524
+ setTextEditingPointerPassthrough(false);
3525
+ if (activeTextEditEl === target) activeTextEditEl = null;
3526
+ postTextEditingState(target, false);
3527
+ if (!commit) {
3528
+ target.innerHTML = originalHtml;
3529
+ refreshOverlays();
3530
+ return;
3531
+ }
3532
+ var next = target.textContent || "";
3533
+ var nextHtml = target.innerHTML || "";
3534
+ refreshOverlays();
3535
+ if (next !== originalText || nextHtml !== originalHtml) {
3536
+ postTextContentChange(target, next, nextHtml);
3537
+ }
3538
+ }
3539
+
3540
+ function onBlur() {
3541
+ finish(true);
3542
+ }
3543
+
3544
+ function onKeyDown(ev) {
3545
+ if (ev.key === "Escape") {
3546
+ ev.preventDefault();
3547
+ finish(false);
3548
+ target.blur();
3549
+ return;
3550
+ }
3551
+ if (ev.key === "Enter" && !ev.shiftKey) {
3552
+ ev.preventDefault();
3553
+ finish(true);
3554
+ target.blur();
3555
+ }
3556
+ }
3557
+
3558
+ function onPaste(ev) {
3559
+ ev.preventDefault();
3560
+ insertPlainTextAtSelection(
3561
+ (ev.clipboardData && ev.clipboardData.getData("text/plain")) || "",
3562
+ );
3563
+ }
3564
+
3565
+ function onSelectionChange() {
3566
+ postTextEditingState(target, true);
3567
+ }
3568
+
3569
+ target.addEventListener("blur", onBlur, true);
3570
+ target.addEventListener("keydown", onKeyDown, true);
3571
+ target.addEventListener("paste", onPaste, true);
3572
+ target.addEventListener("keyup", onSelectionChange, true);
3573
+ target.addEventListener("mouseup", onSelectionChange, true);
3574
+ document.addEventListener("selectionchange", onSelectionChange);
3575
+ target.focus();
3576
+ placeTextCaretFromPoint(target, e.clientX, e.clientY);
3577
+ }
3578
+
3579
+ shieldOverlay.addEventListener("dblclick", beginTextEditingFromEvent, true);
3580
+ selectionOverlay.addEventListener(
3581
+ "dblclick",
3582
+ beginTextEditingFromEvent,
3583
+ true,
3584
+ );
3585
+ document.addEventListener(
3586
+ "dblclick",
3587
+ function (e) {
3588
+ if (isOverlayElement(e.target)) return;
3589
+ beginTextEditingFromEvent(e);
3590
+ },
3591
+ true,
3592
+ );
3593
+
3594
+ shieldOverlay.addEventListener(
3595
+ "pointermove",
3596
+ function (e) {
3597
+ stopNativeInteraction(e);
3598
+ hoveredEl = elementFromEditorPoint(e.clientX, e.clientY);
3599
+ if (!hoveredEl) {
3600
+ highlightOverlay.style.display = "none";
3601
+ if (!spacingDrag) {
3602
+ selectedSpacingHovered = false;
3603
+ hoveredSpacingHandleKey = "";
3604
+ updateSpacingOverlay(selectedEl);
3605
+ }
3606
+ hideMeasurements();
3607
+ return;
3608
+ }
3609
+ if (hoveredEl && hoveredEl.closest("[data-agent-native-text-editing]"))
3610
+ return;
3611
+ if (!spacingDrag) {
3612
+ selectedSpacingHovered = Boolean(
3613
+ selectedEl &&
3614
+ hoveredEl &&
3615
+ (hoveredEl === selectedEl ||
3616
+ (selectedEl.contains && selectedEl.contains(hoveredEl))),
3617
+ );
3618
+ if (!selectedSpacingHovered) hoveredSpacingHandleKey = "";
3619
+ updateSpacingOverlay(selectedEl);
3620
+ }
3621
+ if (hoveredEl === selectedEl) {
3622
+ highlightOverlay.style.display = "none";
3623
+ } else {
3624
+ positionOverlay(highlightOverlay, hoveredEl);
3625
+ }
3626
+ if (e.altKey && selectedEl && hoveredEl && selectedEl !== hoveredEl) {
3627
+ showMeasurements(selectedEl, hoveredEl);
3628
+ } else {
3629
+ hideMeasurements();
3630
+ }
3631
+ var info = getElementInfo(hoveredEl);
3632
+ (window.parent as Window).postMessage(
3633
+ { type: "element-hover", payload: info },
3634
+ "*",
3635
+ );
3636
+ },
3637
+ true,
3638
+ );
3639
+
3640
+ selectionOverlay.addEventListener(
3641
+ "pointermove",
3642
+ handleSpacingOverlayPointerMove,
3643
+ true,
3644
+ );
3645
+
3646
+ selectionOverlay.addEventListener(
3647
+ "pointerleave",
3648
+ function (e) {
3649
+ stopNativeInteraction(e);
3650
+ if (shouldKeepSpacingOverlayForLeave(e)) {
3651
+ updateSpacingOverlay(selectedEl);
3652
+ return;
3653
+ }
3654
+ if (!spacingDrag) {
3655
+ selectedSpacingHovered = false;
3656
+ hoveredSpacingHandleKey = "";
3657
+ updateSpacingOverlay(selectedEl);
3658
+ }
3659
+ },
3660
+ true,
3661
+ );
3662
+
3663
+ shieldOverlay.addEventListener(
3664
+ "pointerleave",
3665
+ function (e) {
3666
+ stopNativeInteraction(e);
3667
+ if (shouldKeepSpacingOverlayForLeave(e)) {
3668
+ updateSpacingOverlay(selectedEl);
3669
+ return;
3670
+ }
3671
+ hoveredEl = null;
3672
+ if (!spacingDrag) {
3673
+ selectedSpacingHovered = false;
3674
+ hoveredSpacingHandleKey = "";
3675
+ updateSpacingOverlay(selectedEl);
3676
+ }
3677
+ highlightOverlay.style.display = "none";
3678
+ hideMeasurements();
3679
+ (window.parent as Window).postMessage(
3680
+ { type: "element-hover", payload: null },
3681
+ "*",
3682
+ );
3683
+ },
3684
+ true,
3685
+ );
3686
+
3687
+ window.addEventListener(
3688
+ "keyup",
3689
+ function (e) {
3690
+ if (e.key === "Alt") hideMeasurements();
3691
+ },
3692
+ true,
3693
+ );
3694
+
3695
+ window.addEventListener("message", function (e) {
3696
+ if (e.source !== window.parent) return;
3697
+ if (!e.data) return;
3698
+ if (e.data.payload && typeof e.data.payload === "object") {
3699
+ Object.keys(e.data.payload).forEach(function (key) {
3700
+ if (e.data[key] === undefined) e.data[key] = e.data.payload[key];
3701
+ });
3702
+ }
3703
+ // set-read-only: toggle the bridge's readOnly state in-place without a reload.
3704
+ // When readOnly becomes true the shield/selection/drag/edit entry points are
3705
+ // gated so the surface is safe for background/inactive display use.
3706
+ if (e.data.type === "set-read-only") {
3707
+ var nextReadOnly = !!e.data.readOnly;
3708
+ if (readOnly === nextReadOnly) return;
3709
+ readOnly = nextReadOnly;
3710
+ textEditingEnabled = !readOnly && __TEXT_EDITING_ENABLED__;
3711
+ if (readOnly) {
3712
+ // Leave the text editor gracefully before going read-only.
3713
+ if (activeTextEditEl) {
3714
+ activeTextEditEl.blur();
3715
+ }
3716
+ clearRuntimeSelection();
3717
+ shieldOverlay.style.pointerEvents = "none";
3718
+ } else {
3719
+ shieldOverlay.style.pointerEvents = "auto";
3720
+ }
3721
+ return;
3722
+ }
3723
+ // begin-text-edit: enter text-editing mode for the element identified by
3724
+ // nodeId immediately (no double-click needed). Used after programmatic
3725
+ // text-element creation so the user can type right away and the autosize
3726
+ // CSS (width:max-content or similar) takes effect from the first keystroke.
3727
+ if (e.data.type === "begin-text-edit") {
3728
+ if (readOnly || !textEditingEnabled) return;
3729
+ var nodeId: string =
3730
+ typeof e.data.nodeId === "string" ? e.data.nodeId : "";
3731
+ if (!nodeId) return;
3732
+ var nodeTarget: Element | null = document.querySelector(
3733
+ '[data-agent-native-node-id="' +
3734
+ nodeId.replace(/\\/g, "\\\\").replace(/"/g, '\\"') +
3735
+ '"]',
3736
+ );
3737
+ if (!nodeTarget || nodeTarget.nodeType !== 1) return;
3738
+ // Resolve the actual editable text leaf (same logic as findTextEditTarget).
3739
+ var textTarget: HTMLElement | null =
3740
+ (findTextEditTarget(nodeTarget) as HTMLElement | null) ||
3741
+ (nodeTarget as HTMLElement);
3742
+ if (!textTarget || textTarget.nodeType !== 1) return;
3743
+ // If we are already editing this element, do nothing.
3744
+ if (activeTextEditEl && activeTextEditEl === textTarget) return;
3745
+ // Synthesise coordinates at the end of the element content so the caret
3746
+ // lands at the insertion point (right after any placeholder text).
3747
+ var bteRect = textTarget.getBoundingClientRect();
3748
+ var bteCenterX = bteRect.right - 2;
3749
+ var bteCenterY = bteRect.top + bteRect.height / 2;
3750
+ // Delegate to the canonical path so all state, events, and postMessages
3751
+ // stay consistent with a normal double-click text edit.
3752
+ beginTextEditingFromEvent({
3753
+ clientX: bteCenterX,
3754
+ clientY: bteCenterY,
3755
+ target: textTarget,
3756
+ preventDefault: function () {},
3757
+ stopPropagation: function () {},
3758
+ stopImmediatePropagation: function () {},
3759
+ } as unknown as MouseEvent);
3760
+ return;
3761
+ }
3762
+ if (e.data.type === "set-editor-chrome-scale") {
3763
+ // Live-update the constant-size chrome scale WITHOUT rebuilding srcdoc.
3764
+ // Rebuilding srcdoc reloads the iframe and flashes the content white.
3765
+ editorChromeScaleX = Math.max(0.05, Number(e.data.scaleX) || 1);
3766
+ editorChromeScaleY = Math.max(
3767
+ 0.05,
3768
+ Number(e.data.scaleY) || editorChromeScaleX,
3769
+ );
3770
+ applyEditorChromeScale();
3771
+ if (selectedEl || hoveredEl) refreshOverlays();
3772
+ return;
3773
+ }
3774
+ if (e.data.type === "scale-tool-mode") {
3775
+ scaleToolEnabled = !!e.data.enabled;
3776
+ return;
3777
+ }
3778
+ if (e.data.type === "clear-selection") {
3779
+ clearRuntimeSelection();
3780
+ return;
3781
+ }
3782
+ if (e.data.type === "select-element") {
3783
+ var candidates: string[] = [];
3784
+ if (Array.isArray(e.data.selectorCandidates)) {
3785
+ e.data.selectorCandidates.forEach(function (selector) {
3786
+ if (
3787
+ typeof selector === "string" &&
3788
+ selector &&
3789
+ candidates.indexOf(selector) === -1
3790
+ ) {
3791
+ candidates.push(selector);
3792
+ }
3793
+ });
3794
+ }
3795
+ if (
3796
+ e.data.selector &&
3797
+ candidates.indexOf(String(e.data.selector)) === -1
3798
+ ) {
3799
+ candidates.push(String(e.data.selector));
3800
+ }
3801
+ var target =
3802
+ selectedEl &&
3803
+ document.documentElement.contains(selectedEl) &&
3804
+ matchesExactSelectorList(selectedEl, candidates)
3805
+ ? selectedEl
3806
+ : null;
3807
+ for (var i = 0; i < candidates.length && !target; i += 1) {
3808
+ try {
3809
+ var matches = document.querySelectorAll(candidates[i]);
3810
+ for (var j = 0; j < matches.length; j += 1) {
3811
+ if (!isLayerInteractionBlocked(matches[j])) {
3812
+ target = matches[j];
3813
+ break;
3814
+ }
3815
+ }
3816
+ } catch (_err) {}
3817
+ }
3818
+ if (!target) return;
3819
+ selectedSpacingHovered = false;
3820
+ hoveredSpacingHandleKey = "";
3821
+ selectedEl = target;
3822
+ positionOverlay(selectionOverlay, target);
3823
+ if (hoveredEl === selectedEl) highlightOverlay.style.display = "none";
3824
+ return;
3825
+ }
3826
+ if (e.data.type === "hover-element") {
3827
+ var hoverCandidates: string[] = [];
3828
+ if (Array.isArray(e.data.selectorCandidates)) {
3829
+ e.data.selectorCandidates.forEach(function (selector) {
3830
+ if (
3831
+ typeof selector === "string" &&
3832
+ selector &&
3833
+ hoverCandidates.indexOf(selector) === -1
3834
+ ) {
3835
+ hoverCandidates.push(selector);
3836
+ }
3837
+ });
3838
+ }
3839
+ if (
3840
+ e.data.selector &&
3841
+ hoverCandidates.indexOf(String(e.data.selector)) === -1
3842
+ ) {
3843
+ hoverCandidates.push(String(e.data.selector));
3844
+ }
3845
+ if (hoverCandidates.length === 0) {
3846
+ hoveredEl = null;
3847
+ highlightOverlay.style.display = "none";
3848
+ hideMeasurements();
3849
+ return;
3850
+ }
3851
+ var hoverTarget = findRuntimeTarget(
3852
+ String(e.data.selector || ""),
3853
+ hoverCandidates,
3854
+ );
3855
+ hoveredEl = hoverTarget;
3856
+ if (
3857
+ hoveredEl &&
3858
+ !isLayerInteractionBlocked(hoveredEl) &&
3859
+ hoveredEl !== selectedEl
3860
+ ) {
3861
+ positionOverlay(highlightOverlay, hoveredEl);
3862
+ } else {
3863
+ highlightOverlay.style.display = "none";
3864
+ hideMeasurements();
3865
+ }
3866
+ return;
3867
+ }
3868
+ if (e.data.type === "layer-states") {
3869
+ lockedSelectors = Array.isArray(e.data.lockedSelectors)
3870
+ ? e.data.lockedSelectors.filter(function (item) {
3871
+ return typeof item === "string";
3872
+ })
3873
+ : [];
3874
+ hiddenSelectors = Array.isArray(e.data.hiddenSelectors)
3875
+ ? e.data.hiddenSelectors.filter(function (item) {
3876
+ return typeof item === "string";
3877
+ })
3878
+ : [];
3879
+ if (selectedEl && isLayerInteractionBlocked(selectedEl)) {
3880
+ selectedEl = null;
3881
+ selectionOverlay.style.display = "none";
3882
+ clearComponentTag();
3883
+ }
3884
+ if (hoveredEl && isLayerInteractionBlocked(hoveredEl)) {
3885
+ hoveredEl = null;
3886
+ highlightOverlay.style.display = "none";
3887
+ }
3888
+ applyHiddenSelectors();
3889
+ return;
3890
+ }
3891
+ if (e.data.type === "visual-structure-ack") {
3892
+ var move = pendingStructureMoves[e.data.requestId];
3893
+ if (!move) return;
3894
+ delete pendingStructureMoves[e.data.requestId];
3895
+ if (e.data.applied) {
3896
+ if (move.el && move.el.isConnected) {
3897
+ applyRuntimeReorder(move.el, move.target);
3898
+ selectedEl = move.el;
3899
+ positionOverlay(selectionOverlay, selectedEl);
3900
+ (window.parent as Window).postMessage(
3901
+ { type: "element-select", payload: getElementInfo(selectedEl) },
3902
+ "*",
3903
+ );
3904
+ }
3905
+ } else {
3906
+ // Revert the optimistic reorder to its pre-drag position.
3907
+ if (
3908
+ move.el &&
3909
+ move.el.isConnected &&
3910
+ move.origin &&
3911
+ move.origin.prevParent &&
3912
+ move.origin.prevParent.isConnected
3913
+ ) {
3914
+ move.origin.prevParent.insertBefore(
3915
+ move.el,
3916
+ move.origin.prevNextSibling,
3917
+ );
3918
+ selectedEl = move.el;
3919
+ positionOverlay(selectionOverlay, selectedEl);
3920
+ (window.parent as Window).postMessage(
3921
+ { type: "element-select", payload: getElementInfo(selectedEl) },
3922
+ "*",
3923
+ );
3924
+ }
3925
+ }
3926
+ return;
3927
+ }
3928
+ if (e.data.type === "replace-document-content") {
3929
+ replaceRuntimeDocument(
3930
+ e.data.content,
3931
+ e.data.forceFullDocument ? "" : e.data.selectedSelector,
3932
+ e.data.forceFullDocument ? [] : e.data.selectorCandidates,
3933
+ );
3934
+ return;
3935
+ }
3936
+ if (e.data.type === "delete-element") {
3937
+ removeRuntimeTarget(e.data.selector, e.data.selectorCandidates);
3938
+ return;
3939
+ }
3940
+ if (e.data.type !== "style-change") return;
3941
+ var sel = e.data.selector;
3942
+ var prop = e.data.property;
3943
+ var val = e.data.value;
3944
+ var candidatesForStyle = Array.isArray(e.data.selectorCandidates)
3945
+ ? e.data.selectorCandidates
3946
+ : [];
3947
+ if (sel && candidatesForStyle.indexOf(String(sel)) === -1)
3948
+ candidatesForStyle.push(String(sel));
3949
+ if (e.data.nodeId) {
3950
+ candidatesForStyle.push(
3951
+ '[data-agent-native-node-id="' +
3952
+ String(e.data.nodeId).replace(/"/g, '\\"') +
3953
+ '"]',
3954
+ );
3955
+ }
3956
+ var el = findRuntimeTarget(String(sel || ""), candidatesForStyle);
3957
+ if (
3958
+ prop &&
3959
+ activeTextEditEl &&
3960
+ el === activeTextEditEl &&
3961
+ applyTextRangeStyle(prop, val)
3962
+ ) {
3963
+ postTextContentChange(
3964
+ activeTextEditEl,
3965
+ activeTextEditEl.textContent || "",
3966
+ activeTextEditEl.innerHTML || "",
3967
+ );
3968
+ refreshOverlays();
3969
+ return;
3970
+ }
3971
+ if (!el) return;
3972
+ var didPatchDom = false;
3973
+ var attributeOverrides = e.data.attributeOverrides;
3974
+ if (
3975
+ attributeOverrides &&
3976
+ typeof attributeOverrides === "object" &&
3977
+ !Array.isArray(attributeOverrides)
3978
+ ) {
3979
+ Object.keys(attributeOverrides).forEach(function (name) {
3980
+ if (!/^(?!on)[a-zA-Z][a-zA-Z0-9:_.-]*$/i.test(name)) return;
3981
+ var nextValue = attributeOverrides[name];
3982
+ if (
3983
+ nextValue === null ||
3984
+ nextValue === undefined ||
3985
+ nextValue === false
3986
+ ) {
3987
+ el.removeAttribute(name);
3988
+ } else {
3989
+ el.setAttribute(name, String(nextValue));
3990
+ }
3991
+ didPatchDom = true;
3992
+ });
3993
+ }
3994
+ var classEdit = e.data.classEdit;
3995
+ if (classEdit && typeof classEdit === "object") {
3996
+ var currentClass = (el.getAttribute("class") || "")
3997
+ .trim()
3998
+ .split(/\s+/)
3999
+ .filter(Boolean);
4000
+ var nextClass = currentClass.slice();
4001
+ if (classEdit.operation === "replace" && classEdit.from && classEdit.to) {
4002
+ var replaced = false;
4003
+ nextClass = currentClass.map(function (token) {
4004
+ if (token === classEdit.from) {
4005
+ replaced = true;
4006
+ return String(classEdit.to);
4007
+ }
4008
+ return token;
4009
+ });
4010
+ if (!replaced && nextClass.indexOf(String(classEdit.to)) === -1)
4011
+ nextClass.push(String(classEdit.to));
4012
+ didPatchDom = true;
4013
+ } else if (classEdit.operation === "add" && classEdit.className) {
4014
+ if (nextClass.indexOf(String(classEdit.className)) === -1)
4015
+ nextClass.push(String(classEdit.className));
4016
+ didPatchDom = true;
4017
+ } else if (classEdit.operation === "remove" && classEdit.className) {
4018
+ nextClass = currentClass.filter(function (token) {
4019
+ return token !== String(classEdit.className);
4020
+ });
4021
+ didPatchDom = true;
4022
+ }
4023
+ if (didPatchDom) el.setAttribute("class", nextClass.join(" "));
4024
+ }
4025
+ if (prop && typeof prop === "string") {
4026
+ applyInlineStyleProperty(el, prop, val);
4027
+ didPatchDom = true;
4028
+ }
4029
+ if (didPatchDom) {
4030
+ refreshOverlays();
4031
+ }
4032
+ });
4033
+
4034
+ window.addEventListener("scroll", refreshOverlays, true);
4035
+ window.addEventListener("resize", refreshOverlays);
4036
+ applyEditorChromeScale();
4037
+ })();