@agent-native/core 0.80.7 → 0.80.9

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 (302) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +6 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +8 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +10 -2
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +10 -2
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +7 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +7 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +7 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +9 -2
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +6 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +6 -0
  13. package/corpus/core/docs/content/template-design.mdx +9 -2
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/cli/mcp.ts +10 -0
  16. package/corpus/core/src/cli/skills.ts +58 -12
  17. package/corpus/core/src/client/AssistantChat.tsx +70 -27
  18. package/corpus/core/src/client/analytics.ts +3 -1
  19. package/corpus/core/src/client/error-format.ts +25 -0
  20. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +13 -11
  21. package/corpus/core/src/client/guided-questions.tsx +32 -4
  22. package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +33 -1
  23. package/corpus/core/src/client/resources/BuiltinCapabilityDetail.tsx +8 -0
  24. package/corpus/core/src/client/resources/use-builtin-capabilities.ts +4 -2
  25. package/corpus/core/src/client/session-replay.ts +34 -0
  26. package/corpus/core/src/client/sharing/ShareButton.tsx +106 -21
  27. package/corpus/core/src/coding-tools/index.ts +1 -1
  28. package/corpus/core/src/mcp/index.ts +2 -0
  29. package/corpus/core/src/mcp/screen-memory-stdio.ts +290 -0
  30. package/corpus/core/src/mcp-client/builtin-capabilities.ts +13 -1
  31. package/corpus/core/src/mcp-client/index.ts +9 -0
  32. package/corpus/core/src/mcp-client/screen-memory-local.ts +461 -0
  33. package/corpus/core/src/onboarding/types.ts +7 -0
  34. package/corpus/core/src/server/agent-chat-plugin.ts +25 -0
  35. package/corpus/core/src/server/agents-bundle.ts +35 -7
  36. package/corpus/core/src/styles/agent-native.css +5 -0
  37. package/corpus/core/src/templates/default/app/global.css +35 -35
  38. package/corpus/templates/analytics/AGENTS.md +6 -0
  39. package/corpus/templates/analytics/actions/github-repo-files.ts +9 -0
  40. package/corpus/templates/analytics/app/components/layout/Layout.tsx +4 -4
  41. package/corpus/templates/analytics/app/global.css +144 -40
  42. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +1 -1
  43. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +1 -1
  44. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
  45. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +4 -4
  46. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -9
  47. package/corpus/templates/analytics/changelog/2026-06-29-agents-can-search-and-read-connected-github-repositories-whe.md +6 -0
  48. package/corpus/templates/analytics/changelog/2026-06-29-sessions-and-explorer-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  49. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -1
  50. package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +1 -1
  51. package/corpus/templates/assets/app/global.css +99 -35
  52. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  53. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +4 -4
  54. package/corpus/templates/assets/app/routes/library.tsx +4 -4
  55. package/corpus/templates/assets/changelog/2026-06-29-asset-library-and-detail-layouts-adapt-when-the-agent-sidebar.md +6 -0
  56. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  57. package/corpus/templates/brain/app/global.css +63 -35
  58. package/corpus/templates/brain/app/routes/search.tsx +1 -1
  59. package/corpus/templates/brain/app/routes/settings.tsx +1 -1
  60. package/corpus/templates/brain/app/routes/sources.tsx +2 -2
  61. package/corpus/templates/brain/changelog/2026-06-29-search-settings-and-source-layouts-adapt-when-the-agent-sidebar.md +6 -0
  62. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +4 -4
  63. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +3 -2
  64. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -2
  65. package/corpus/templates/calendar/app/global.css +69 -35
  66. package/corpus/templates/calendar/changelog/2026-06-29-event-and-find-time-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  67. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  68. package/corpus/templates/chat/app/global.css +35 -35
  69. package/corpus/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  70. package/corpus/templates/clips/AGENTS.md +13 -0
  71. package/corpus/templates/clips/actions/get-screen-memory-status.ts +12 -0
  72. package/corpus/templates/clips/actions/query-screen-memory-context.ts +29 -0
  73. package/corpus/templates/clips/actions/view-screen.ts +2 -2
  74. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  75. package/corpus/templates/clips/app/global.css +47 -35
  76. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +3 -3
  77. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +45 -18
  78. package/corpus/templates/clips/changelog/2026-06-29-meeting-detail-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-06-29-screen-memory-keeps-a-local-rolling-screen-buffer-for-agent-context.md +5 -0
  80. package/corpus/templates/clips/changelog/2026-06-29-video-chat-is-easier-to-find-on-recording-pages-and-s3-setup.md +6 -0
  81. package/corpus/templates/clips/chrome-extension/src/styles.css +39 -39
  82. package/corpus/templates/clips/desktop/src/app.tsx +290 -1
  83. package/corpus/templates/clips/desktop/src/shared/config.ts +83 -0
  84. package/corpus/templates/clips/desktop/src/styles.css +67 -52
  85. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +50 -0
  86. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +17 -0
  87. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +13 -13
  88. package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +990 -0
  89. package/corpus/templates/clips/server/plugins/onboarding.ts +2 -0
  90. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +69 -16
  91. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +3 -3
  92. package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
  93. package/corpus/templates/content/app/global.css +29 -15
  94. package/corpus/templates/content/changelog/2026-06-29-database-gallery-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  95. package/corpus/templates/content/changelog/2026-06-29-read-only-pages-no-longer-try-to-autosave-or-open-editor-onl.md +6 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +25 -20
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +8 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +13 -0
  100. package/corpus/templates/design/AGENTS.md +24 -14
  101. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1054 -0
  102. package/corpus/templates/design/actions/apply-visual-edit.ts +24 -4
  103. package/corpus/templates/design/actions/edit-design.ts +6 -3
  104. package/corpus/templates/design/actions/export-coding-handoff.ts +12 -4
  105. package/corpus/templates/design/actions/generate-design.ts +6 -21
  106. package/corpus/templates/design/actions/generate-screens.ts +48 -5
  107. package/corpus/templates/design/actions/get-design.ts +1 -0
  108. package/corpus/templates/design/actions/present-design-variants.ts +355 -30
  109. package/corpus/templates/design/actions/update-design.ts +47 -10
  110. package/corpus/templates/design/actions/update-file.ts +33 -8
  111. package/corpus/templates/design/actions/view-screen.ts +89 -31
  112. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +389 -71
  113. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
  114. package/corpus/templates/design/app/components/design/EditPanel.tsx +596 -95
  115. package/corpus/templates/design/app/components/design/LayersPanel.tsx +428 -221
  116. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1197 -228
  117. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +94 -217
  118. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +28 -14
  119. package/corpus/templates/design/app/components/design/index.ts +0 -1
  120. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +228 -170
  121. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +140 -13
  122. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +39 -8
  123. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +4 -2
  124. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +75 -5
  125. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +37 -12
  126. package/corpus/templates/design/app/components/design/inspector/index.ts +1 -0
  127. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +17 -4
  128. package/corpus/templates/design/app/components/design/types.ts +9 -0
  129. package/corpus/templates/design/app/components/layout/Layout.tsx +23 -5
  130. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +448 -259
  131. package/corpus/templates/design/app/global.css +77 -49
  132. package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
  133. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +8 -1
  134. package/corpus/templates/design/app/i18n/zh-TW.ts +4 -0
  135. package/corpus/templates/design/app/i18n-data.ts +55 -3
  136. package/corpus/templates/design/app/pages/DesignEditor.tsx +2783 -658
  137. package/corpus/templates/design/app/root.tsx +1 -1
  138. package/corpus/templates/design/changelog/2026-06-29-agent-edits-to-a-design-are-no-longer-occasionally-dropped-o.md +6 -0
  139. package/corpus/templates/design/changelog/2026-06-29-box-shadow-and-gradient-colors-keep-named-css-colors-like-re.md +6 -0
  140. package/corpus/templates/design/changelog/2026-06-29-containers-no-longer-block-selecting-nested-elements-and-nor.md +6 -0
  141. package/corpus/templates/design/changelog/2026-06-29-cut-cmd-ctrl-x-now-removes-the-selected-element-to-the-clipb.md +6 -0
  142. package/corpus/templates/design/changelog/2026-06-29-deleting-with-multiple-layers-selected-in-the-layers-panel-n.md +6 -0
  143. package/corpus/templates/design/changelog/2026-06-29-design-agents-now-generate-and-refine-variants-from-stronger.md +6 -0
  144. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +6 -0
  145. package/corpus/templates/design/changelog/2026-06-29-design-questions-now-appear-as-a-cleaner-canvas-intake-with-.md +6 -0
  146. package/corpus/templates/design/changelog/2026-06-29-design-variant-directions-now-appear-as-board-screens-with-chat-buttons.md +5 -0
  147. package/corpus/templates/design/changelog/2026-06-29-duplicating-a-screen-now-gives-the-copy-its-own-layer-ids-so.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-29-element-selection-no-longer-snaps-back-to-the-previous-layer.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-29-frame-resize-measurements-now-stay-beside-the-cursor-while-d.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-29-hiding-and-re-showing-a-gradient-fill-now-preserves-each-sto.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-29-inspector-number-fields-have-cleaner-tooltips-and-draggable-.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-29-layer-locking-layer-nesting-generated-question-intake-and-im.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-29-layer-names-in-the-sidebar-can-scroll-horizontally-instead-o.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-29-layer-nesting-in-the-sidebar-now-uses-tighter-spacing-so-nam.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-29-layers-no-longer-repeat-document-wrapper-rows-when-a-screen-.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-29-overview-previews-now-keep-element-hover-and-selection-tied-.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-29-picking-a-design-direction-now-submits-to-the-agent-right-aw.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-29-pressing-escape-now-cancels-a-stuck-screen-drag-in-the-overv.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +6 -0
  160. package/corpus/templates/design/changelog/2026-06-29-screen-overview-only-highlights-screens-when-the-frame-or-ti.md +6 -0
  161. package/corpus/templates/design/changelog/2026-06-29-screen-overview-previews-now-resize-to-match-the-selected-de.md +6 -0
  162. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-now-uses-a-pointer-cursor-instead-.md +6 -0
  163. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-outlines-now-stay-locked-to-the-se.md +6 -0
  164. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zoom-now-reports-real-screen-scale-separatel.md +6 -0
  165. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-feels-smoother-and-selection-outline.md +6 -0
  166. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-now-stays-on-the-overview-canvas-ins.md +6 -0
  167. package/corpus/templates/design/changelog/2026-06-29-selecting-a-nested-layer-from-all-screens-now-keeps-you-in-a.md +6 -0
  168. package/corpus/templates/design/changelog/2026-06-29-share-now-groups-link-sharing-exports-and-agent-handoff.md +6 -0
  169. package/corpus/templates/design/changelog/2026-06-29-style-changes-that-set-several-properties-at-once-fixed-size.md +6 -0
  170. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-loading-skeleton-now-uses-a-softer-charcoa.md +6 -0
  171. package/corpus/templates/design/changelog/2026-06-29-the-device-preview-menu-now-sits-next-to-the-preview-button-.md +6 -0
  172. package/corpus/templates/design/changelog/2026-06-29-the-editor-no-longer-crashes-when-a-screen-ends-up-with-dupl.md +6 -0
  173. package/corpus/templates/design/changelog/2026-06-29-the-inspector-stays-read-only-while-an-empty-design-is-still.md +6 -0
  174. package/corpus/templates/design/changelog/2026-06-29-the-missing-design-view-now-matches-the-rest-of-the-editor-c.md +6 -0
  175. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-icon-and-tool-option-menus-better-match-the-edi.md +6 -0
  176. package/corpus/templates/design/changelog/2026-06-29-typography-controls-now-keep-the-font-name-visible-and-move-.md +6 -0
  177. package/corpus/templates/design/e2e/README.md +4 -3
  178. package/corpus/templates/design/e2e/global-setup.ts +24 -23
  179. package/corpus/templates/design/e2e/helpers.ts +122 -11
  180. package/corpus/templates/design/playwright.config.ts +19 -4
  181. package/corpus/templates/design/server/lib/coding-handoff.ts +126 -2
  182. package/corpus/templates/design/server/plugins/core-routes.ts +1 -2
  183. package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
  184. package/corpus/templates/design/shared/code-layer.ts +125 -22
  185. package/corpus/templates/design/shared/color-utils.ts +159 -0
  186. package/corpus/templates/design/shared/generation-session.ts +42 -0
  187. package/corpus/templates/dispatch/app/global.css +79 -35
  188. package/corpus/templates/dispatch/app/routes/integrations.tsx +8 -7
  189. package/corpus/templates/dispatch/changelog/2026-06-29-integration-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  190. package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -2
  191. package/corpus/templates/forms/app/global.css +64 -35
  192. package/corpus/templates/forms/app/pages/FormsListPage.tsx +2 -2
  193. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +3 -3
  194. package/corpus/templates/forms/changelog/2026-06-29-form-list-and-insight-layouts-adapt-when-the-agent-sidebar.md +6 -0
  195. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +3 -1
  196. package/corpus/templates/macros/app/global.css +25 -9
  197. package/corpus/templates/macros/changelog/2026-06-29-macro-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  198. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -2
  199. package/corpus/templates/mail/app/global.css +42 -36
  200. package/corpus/templates/mail/app/pages/InboxPage.tsx +1 -1
  201. package/corpus/templates/mail/changelog/2026-06-29-the-contact-panel-now-adapts-to-the-available-mail-pane-width.md +6 -0
  202. package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -1
  203. package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +6 -7
  204. package/corpus/templates/plan/app/global.css +74 -42
  205. package/corpus/templates/plan/changelog/2026-06-29-plan-document-blocks-adapt-to-the-available-document-width.md +6 -0
  206. package/corpus/templates/plan/changelog/2026-06-29-plan-titles-and-summaries-can-be-edited-inline-without-focus.md +6 -0
  207. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -1
  208. package/corpus/templates/slides/app/global.css +49 -33
  209. package/corpus/templates/slides/changelog/2026-06-29-slide-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  210. package/corpus/templates/videos/app/components/layout/Layout.tsx +3 -1
  211. package/corpus/templates/videos/app/global.css +35 -35
  212. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  213. package/dist/cli/mcp.d.ts.map +1 -1
  214. package/dist/cli/mcp.js +10 -0
  215. package/dist/cli/mcp.js.map +1 -1
  216. package/dist/cli/skills.d.ts.map +1 -1
  217. package/dist/cli/skills.js +58 -12
  218. package/dist/cli/skills.js.map +1 -1
  219. package/dist/client/AssistantChat.d.ts.map +1 -1
  220. package/dist/client/AssistantChat.js +54 -24
  221. package/dist/client/AssistantChat.js.map +1 -1
  222. package/dist/client/analytics.d.ts.map +1 -1
  223. package/dist/client/analytics.js +2 -1
  224. package/dist/client/analytics.js.map +1 -1
  225. package/dist/client/error-format.d.ts.map +1 -1
  226. package/dist/client/error-format.js +17 -0
  227. package/dist/client/error-format.js.map +1 -1
  228. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  229. package/dist/client/extensions/ExtensionViewer.js +3 -4
  230. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  231. package/dist/client/guided-questions.d.ts +4 -0
  232. package/dist/client/guided-questions.d.ts.map +1 -1
  233. package/dist/client/guided-questions.js +16 -7
  234. package/dist/client/guided-questions.js.map +1 -1
  235. package/dist/client/onboarding/OnboardingPanel.js +26 -1
  236. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  237. package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -1
  238. package/dist/client/resources/BuiltinCapabilityDetail.js +1 -1
  239. package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -1
  240. package/dist/client/resources/use-builtin-capabilities.d.ts +1 -1
  241. package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -1
  242. package/dist/client/resources/use-builtin-capabilities.js +2 -1
  243. package/dist/client/resources/use-builtin-capabilities.js.map +1 -1
  244. package/dist/client/session-replay.d.ts.map +1 -1
  245. package/dist/client/session-replay.js +31 -0
  246. package/dist/client/session-replay.js.map +1 -1
  247. package/dist/client/sharing/ShareButton.d.ts +14 -0
  248. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  249. package/dist/client/sharing/ShareButton.js +36 -7
  250. package/dist/client/sharing/ShareButton.js.map +1 -1
  251. package/dist/coding-tools/index.js +1 -1
  252. package/dist/coding-tools/index.js.map +1 -1
  253. package/dist/collab/routes.d.ts +1 -1
  254. package/dist/mcp/index.d.ts +2 -0
  255. package/dist/mcp/index.d.ts.map +1 -1
  256. package/dist/mcp/index.js +1 -0
  257. package/dist/mcp/index.js.map +1 -1
  258. package/dist/mcp/screen-memory-stdio.d.ts +6 -0
  259. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -0
  260. package/dist/mcp/screen-memory-stdio.js +213 -0
  261. package/dist/mcp/screen-memory-stdio.js.map +1 -0
  262. package/dist/mcp-client/builtin-capabilities.d.ts +1 -1
  263. package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -1
  264. package/dist/mcp-client/builtin-capabilities.js +10 -0
  265. package/dist/mcp-client/builtin-capabilities.js.map +1 -1
  266. package/dist/mcp-client/index.d.ts +1 -0
  267. package/dist/mcp-client/index.d.ts.map +1 -1
  268. package/dist/mcp-client/index.js +1 -0
  269. package/dist/mcp-client/index.js.map +1 -1
  270. package/dist/mcp-client/screen-memory-local.d.ts +61 -0
  271. package/dist/mcp-client/screen-memory-local.d.ts.map +1 -0
  272. package/dist/mcp-client/screen-memory-local.js +336 -0
  273. package/dist/mcp-client/screen-memory-local.js.map +1 -0
  274. package/dist/onboarding/types.d.ts +7 -0
  275. package/dist/onboarding/types.d.ts.map +1 -1
  276. package/dist/onboarding/types.js.map +1 -1
  277. package/dist/resources/handlers.d.ts +2 -2
  278. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  279. package/dist/server/agent-chat-plugin.js +26 -0
  280. package/dist/server/agent-chat-plugin.js.map +1 -1
  281. package/dist/server/agents-bundle.d.ts.map +1 -1
  282. package/dist/server/agents-bundle.js +22 -7
  283. package/dist/server/agents-bundle.js.map +1 -1
  284. package/dist/server/transcribe-voice.d.ts +1 -1
  285. package/dist/styles/agent-native.css +5 -0
  286. package/dist/templates/default/app/global.css +35 -35
  287. package/docs/content/locales/ar-SA/template-design.mdx +6 -0
  288. package/docs/content/locales/de-DE/template-design.mdx +8 -0
  289. package/docs/content/locales/es-ES/template-design.mdx +10 -2
  290. package/docs/content/locales/fr-FR/template-design.mdx +10 -2
  291. package/docs/content/locales/hi-IN/template-design.mdx +7 -0
  292. package/docs/content/locales/ja-JP/template-design.mdx +7 -0
  293. package/docs/content/locales/ko-KR/template-design.mdx +7 -0
  294. package/docs/content/locales/pt-BR/template-design.mdx +9 -2
  295. package/docs/content/locales/zh-CN/template-design.mdx +6 -0
  296. package/docs/content/locales/zh-TW/template-design.mdx +6 -0
  297. package/docs/content/template-design.mdx +9 -2
  298. package/package.json +1 -1
  299. package/src/templates/default/app/global.css +35 -35
  300. package/corpus/templates/design/app/components/design/VariantGrid.tsx +0 -189
  301. package/corpus/templates/design/app/components/design/VariantHandoffCard.tsx +0 -115
  302. package/corpus/templates/design/app/hooks/use-variant-flow.ts +0 -258
@@ -247,8 +247,40 @@ function createEditorBridgeThemeScript(vars: Record<string, string>) {
247
247
  const EDITOR_CHROME_BRIDGE_SCRIPT = `
248
248
  <script data-agent-native-editor-chrome-bridge>
249
249
  (function() {
250
- var textEditingEnabled = __TEXT_EDITING_ENABLED__;
250
+ var readOnly = __READ_ONLY__;
251
+ var textEditingEnabled = !readOnly && __TEXT_EDITING_ENABLED__;
251
252
  var scaleToolEnabled = false;
253
+ var editorChromeScaleX = Math.max(0.05, Number(__EDITOR_CHROME_SCALE_X__) || 1);
254
+ var editorChromeScaleY = Math.max(0.05, Number(__EDITOR_CHROME_SCALE_Y__) || editorChromeScaleX);
255
+
256
+ // Ease the constant-size selection chrome to its new size when overview zoom
257
+ // settles (parent posts set-editor-chrome-scale), matching the canvas chrome.
258
+ // Only chrome-scale-driven props animate; the overlay's live position is excluded.
259
+ (function () {
260
+ var chromeTransitionStyle = document.createElement('style');
261
+ chromeTransitionStyle.textContent =
262
+ '[data-agent-native-edit-overlay="selection"]{transition:border-width 150ms ease-out}' +
263
+ '[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}';
264
+ (document.head || document.documentElement).appendChild(chromeTransitionStyle);
265
+ })();
266
+
267
+ function chromeScaleX() {
268
+ return 1 / Math.max(0.05, editorChromeScaleX);
269
+ }
270
+
271
+ function chromeScaleY() {
272
+ return 1 / Math.max(0.05, editorChromeScaleY);
273
+ }
274
+
275
+ function chromeLineScale() {
276
+ return 1 / Math.max(0.05, Math.max(editorChromeScaleX, editorChromeScaleY));
277
+ }
278
+
279
+ function syncEditorChromeScaleVars() {
280
+ document.documentElement.style.setProperty('--agent-native-editor-chrome-scale-x', String(chromeScaleX()));
281
+ document.documentElement.style.setProperty('--agent-native-editor-chrome-scale-y', String(chromeScaleY()));
282
+ document.documentElement.style.setProperty('--agent-native-editor-chrome-line-scale', String(chromeLineScale()));
283
+ }
252
284
 
253
285
  function escapeIdent(value) {
254
286
  if (window.CSS && typeof window.CSS.escape === 'function') {
@@ -321,6 +353,32 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
321
353
  );
322
354
  }
323
355
 
356
+ function isDocumentRootElement(el) {
357
+ return el === document.body || el === document.documentElement;
358
+ }
359
+
360
+ function closestStableSourceElement(el) {
361
+ if (!el || !el.closest) return null;
362
+ var stable = el.closest('[data-agent-native-node-id],[data-code-layer-id],[data-layer-id],[data-builder-id],[data-loc]');
363
+ if (!stable || isDocumentRootElement(stable)) return null;
364
+ return stable;
365
+ }
366
+
367
+ function hasStableOwnSource(el) {
368
+ return !!(
369
+ el &&
370
+ !isDocumentRootElement(el) &&
371
+ getSourceId(el)
372
+ );
373
+ }
374
+
375
+ function selectionTargetForHit(hit) {
376
+ if (!hit || isDocumentRootElement(hit)) return hit;
377
+ if (selectedEl && hit !== selectedEl && selectedEl.contains(hit)) return hit;
378
+ if (hasStableOwnSource(hit)) return hit;
379
+ return closestStableSourceElement(hit) || hit;
380
+ }
381
+
324
382
  function freshRuntimeNodeId(prefix) {
325
383
  var random = '';
326
384
  try {
@@ -356,7 +414,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
356
414
  if (stableOwnSelector) return stableOwnSelector;
357
415
 
358
416
  if (el.id) return '#' + escapeIdent(el.id);
359
- var stableAncestor = el.closest('[data-agent-native-node-id],[data-code-layer-id],[data-layer-id],[data-builder-id],[data-loc]');
417
+ var stableAncestor = closestStableSourceElement(el);
360
418
  if (stableAncestor && stableAncestor !== el) {
361
419
  var stableAncestorSelector = selectorPart(stableAncestor);
362
420
  if (stableAncestorSelector) {
@@ -379,7 +437,8 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
379
437
  ? window.getComputedStyle(el.parentElement)
380
438
  : null;
381
439
  var parentDisplay = parentStyles ? parentStyles.display : undefined;
382
- var sourceId = getSourceId(el) || getSelector(el);
440
+ var sourceBacked = hasStableOwnSource(el) || !!closestStableSourceElement(el);
441
+ var sourceId = sourceBacked ? (getSourceId(el) || getSelector(el)) : '';
383
442
  var parentLayout = parentStyles
384
443
  ? {
385
444
  display: parentStyles.display,
@@ -392,15 +451,24 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
392
451
  position: parentStyles.position,
393
452
  }
394
453
  : undefined;
395
- var capabilities = [
396
- {
397
- kind: 'deterministic-style-edit',
398
- label: 'deterministic-style-edit',
399
- confidence: 0.92,
400
- reason: 'Inline style can be patched and replayed through HMR/collab.',
401
- },
402
- ];
403
- if (el.classList && el.classList.length > 0) {
454
+ var capabilities = sourceBacked
455
+ ? [
456
+ {
457
+ kind: 'deterministic-style-edit',
458
+ label: 'deterministic-style-edit',
459
+ confidence: 0.92,
460
+ reason: 'Inline style can be patched and replayed through HMR/collab.',
461
+ },
462
+ ]
463
+ : [
464
+ {
465
+ kind: 'unsupported',
466
+ label: 'runtime-only-element',
467
+ confidence: 0.3,
468
+ reason: 'This runtime node is not anchored to a source code layer.',
469
+ },
470
+ ];
471
+ if (sourceBacked && el.classList && el.classList.length > 0) {
404
472
  capabilities.push({
405
473
  kind: 'deterministic-class-edit',
406
474
  label: 'deterministic-class-edit',
@@ -408,7 +476,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
408
476
  reason: 'Class tokens are visible on the selected element.',
409
477
  });
410
478
  }
411
- if (parentDisplay === 'flex' || parentDisplay === 'inline-flex' || parentDisplay === 'grid' || parentDisplay === 'inline-grid') {
479
+ if (sourceBacked && (parentDisplay === 'flex' || parentDisplay === 'inline-flex' || parentDisplay === 'grid' || parentDisplay === 'inline-grid')) {
412
480
  capabilities.push({
413
481
  kind: 'agent-structural-edit',
414
482
  label: 'agent-structural-edit',
@@ -433,9 +501,9 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
433
501
  lineHeight: cs.lineHeight,
434
502
  letterSpacing: cs.letterSpacing,
435
503
  textAlign: cs.textAlign,
436
- display: cs.display,
437
- overflow: cs.overflow,
438
- flexDirection: cs.flexDirection,
504
+ display: cs.display,
505
+ overflow: cs.overflow,
506
+ flexDirection: cs.flexDirection,
439
507
  justifyContent: cs.justifyContent,
440
508
  alignItems: cs.alignItems,
441
509
  alignSelf: cs.alignSelf,
@@ -480,7 +548,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
480
548
  mixBlendMode: cs.mixBlendMode,
481
549
  zIndex: cs.zIndex,
482
550
  },
483
- boundingRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
551
+ boundingRect: { x: rect.x + (window.scrollX || window.pageXOffset || 0), y: rect.y + (window.scrollY || window.pageYOffset || 0), width: rect.width, height: rect.height },
484
552
  textContent: el.textContent ? el.textContent.slice(0, 200) : undefined,
485
553
  htmlContent: el.innerHTML && el.innerHTML !== el.textContent ? el.innerHTML.slice(0, 4000) : undefined,
486
554
  isFlexContainer: cs.display === 'flex' || cs.display === 'inline-flex',
@@ -501,12 +569,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
501
569
 
502
570
  var highlightOverlay = document.createElement('div');
503
571
  highlightOverlay.setAttribute('data-agent-native-edit-overlay', 'highlight');
504
- highlightOverlay.style.cssText = 'position:fixed;pointer-events:none;z-index:99999;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;';
572
+ highlightOverlay.style.cssText = '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;';
505
573
  document.body.appendChild(highlightOverlay);
506
574
 
507
575
  var selectionOverlay = document.createElement('div');
508
576
  selectionOverlay.setAttribute('data-agent-native-edit-overlay', 'selection');
509
- selectionOverlay.style.cssText = 'position:fixed;pointer-events:auto;z-index:99998;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;cursor:move;';
577
+ selectionOverlay.style.cssText = '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;';
510
578
  ['n','e','s','w'].forEach(function(pos) {
511
579
  var edge = document.createElement('span');
512
580
  edge.setAttribute('data-agent-native-edge-handle', pos);
@@ -542,7 +610,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
542
610
  var handle = document.createElement('span');
543
611
  handle.setAttribute('data-agent-native-edit-handle', pos);
544
612
  var cursor = pos === 'n' || pos === 's' ? 'ns-resize' : pos === 'e' || pos === 'w' ? 'ew-resize' : pos === 'nw' || pos === 'se' ? 'nwse-resize' : 'nesw-resize';
545
- handle.style.cssText = 'position:absolute;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:' + cursor + ';';
613
+ handle.style.cssText = '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:' + cursor + ';';
546
614
  if (pos.indexOf('n') !== -1) handle.style.top = '-4px';
547
615
  if (pos.indexOf('s') !== -1) handle.style.bottom = '-4px';
548
616
  if (pos.indexOf('w') !== -1) handle.style.left = '-4px';
@@ -596,6 +664,14 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
596
664
  var lockedSelectors = [];
597
665
  var hiddenSelectors = [];
598
666
 
667
+ function clearRuntimeSelection() {
668
+ selectedEl = null;
669
+ hoveredEl = null;
670
+ selectionOverlay.style.display = 'none';
671
+ highlightOverlay.style.display = 'none';
672
+ hideMeasurements();
673
+ }
674
+
599
675
  function matchesSelectorList(el, selectors) {
600
676
  if (!el || !selectors || selectors.length === 0) return false;
601
677
  for (var i = 0; i < selectors.length; i += 1) {
@@ -752,7 +828,14 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
752
828
  for (var i = 0; i < activeCandidates.length && !selectedEl; i += 1) {
753
829
  try {
754
830
  var match = document.querySelector(activeCandidates[i]);
755
- if (match && !isLayerInteractionBlocked(match)) selectedEl = match;
831
+ // Skip the editor's own injected overlay chrome and re-anchor to a
832
+ // source-backed element. A stale positional candidate like
833
+ // body > div:nth-of-type(6) can otherwise re-match an overlay div
834
+ // (the only direct div children of body at runtime), which then has
835
+ // no code-layer node and fails every edit.
836
+ if (match && !isLayerInteractionBlocked(match) && !isOverlayElement(match)) {
837
+ selectedEl = selectionTargetForHit(match) || match;
838
+ }
756
839
  } catch (_err) {}
757
840
  }
758
841
  if (selectedEl) {
@@ -784,22 +867,95 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
784
867
  paddingOverlay.style.left = left + 'px';
785
868
  }
786
869
 
870
+ function applyEditorChromeScale() {
871
+ syncEditorChromeScaleVars();
872
+ var sx = chromeScaleX();
873
+ var sy = chromeScaleY();
874
+ var line = chromeLineScale();
875
+ highlightOverlay.style.borderWidth = (1.5 * line) + 'px';
876
+ selectionOverlay.style.borderWidth = (1.5 * line) + 'px';
877
+ paddingOverlay.style.borderWidth = Math.max(1, 1 * line) + 'px';
878
+
879
+ selectionOverlay.querySelectorAll('[data-agent-native-edge-handle]').forEach(function(edge) {
880
+ var pos = edge.getAttribute('data-agent-native-edge-handle');
881
+ if (pos === 'n' || pos === 's') {
882
+ edge.style.height = (10 * sy) + 'px';
883
+ edge.style[pos === 'n' ? 'top' : 'bottom'] = (-5 * sy) + 'px';
884
+ }
885
+ if (pos === 'e' || pos === 'w') {
886
+ edge.style.width = (10 * sx) + 'px';
887
+ edge.style[pos === 'w' ? 'left' : 'right'] = (-5 * sx) + 'px';
888
+ }
889
+ });
890
+
891
+ selectionOverlay.querySelectorAll('[data-agent-native-edit-handle]').forEach(function(handle) {
892
+ var pos = handle.getAttribute('data-agent-native-edit-handle') || '';
893
+ handle.style.width = (7 * sx) + 'px';
894
+ handle.style.height = (7 * sy) + 'px';
895
+ handle.style.borderWidth = Math.max(1, 1 * line) + 'px';
896
+ if (pos.indexOf('n') !== -1) handle.style.top = (-4 * sy) + 'px';
897
+ if (pos.indexOf('s') !== -1) handle.style.bottom = (-4 * sy) + 'px';
898
+ if (pos.indexOf('w') !== -1) handle.style.left = (-4 * sx) + 'px';
899
+ if (pos.indexOf('e') !== -1) handle.style.right = (-4 * sx) + 'px';
900
+ });
901
+
902
+ selectionOverlay.querySelectorAll('[data-agent-native-rotate-handle]').forEach(function(handle) {
903
+ var pos = handle.getAttribute('data-agent-native-rotate-handle') || '';
904
+ handle.style.width = (18 * sx) + 'px';
905
+ handle.style.height = (18 * sy) + 'px';
906
+ if (pos.indexOf('n') !== -1) handle.style.top = (-26 * sy) + 'px';
907
+ if (pos.indexOf('s') !== -1) handle.style.bottom = (-26 * sy) + 'px';
908
+ if (pos.indexOf('w') !== -1) handle.style.left = (-26 * sx) + 'px';
909
+ if (pos.indexOf('e') !== -1) handle.style.right = (-26 * sx) + 'px';
910
+ });
911
+ }
912
+
787
913
  function positionOverlay(overlay, el) {
788
914
  if (!el || !document.documentElement.contains(el)) {
789
915
  overlay.style.display = 'none';
790
916
  return;
791
917
  }
918
+ // For the selection overlay, prefer the CSS box + rotation transform so
919
+ // the handles hug the rotated element rather than its inflated AABB.
920
+ if (overlay === selectionOverlay) {
921
+ var elCs = window.getComputedStyle(el);
922
+ var elLeft = readFinitePx(el.style.left || elCs.left);
923
+ var elTop = readFinitePx(el.style.top || elCs.top);
924
+ var elW = readFinitePx(el.style.width || elCs.width);
925
+ var elH = readFinitePx(el.style.height || elCs.height);
926
+ var elRot = currentRotation(el);
927
+ var canUseLocalBox = Math.abs(elRot) > 0.01 && elLeft !== null && elTop !== null && elW !== null && elH !== null;
928
+ // Convert element-local left/top to viewport coords by walking to the
929
+ // nearest positioned ancestor (same reference frame as getBoundingClientRect).
930
+ if (canUseLocalBox) {
931
+ var parentRect = (el.offsetParent || document.documentElement).getBoundingClientRect();
932
+ overlay.style.display = 'block';
933
+ overlay.style.left = (parentRect.left + elLeft) + 'px';
934
+ overlay.style.top = (parentRect.top + elTop) + 'px';
935
+ overlay.style.width = elW + 'px';
936
+ overlay.style.height = elH + 'px';
937
+ overlay.style.transform = 'rotate(' + elRot + 'deg)';
938
+ overlay.style.transformOrigin = '0 0';
939
+ updatePaddingOverlay(el);
940
+ return;
941
+ }
942
+ }
792
943
  var rect = el.getBoundingClientRect();
793
944
  overlay.style.display = 'block';
794
945
  overlay.style.top = rect.top + 'px';
795
946
  overlay.style.left = rect.left + 'px';
796
947
  overlay.style.width = rect.width + 'px';
797
948
  overlay.style.height = rect.height + 'px';
949
+ overlay.style.transform = '';
798
950
  if (overlay === selectionOverlay) updatePaddingOverlay(el);
799
951
  }
800
952
 
801
953
  function refreshOverlays() {
802
- if (hoveredEl) positionOverlay(highlightOverlay, hoveredEl);
954
+ if (hoveredEl && hoveredEl !== selectedEl) {
955
+ positionOverlay(highlightOverlay, hoveredEl);
956
+ } else {
957
+ highlightOverlay.style.display = 'none';
958
+ }
803
959
  if (selectedEl) positionOverlay(selectionOverlay, selectedEl);
804
960
  }
805
961
 
@@ -1046,7 +1202,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1046
1202
  window.parent.postMessage({ type: 'clear-selection' }, '*');
1047
1203
  return;
1048
1204
  }
1049
- selectedEl = target;
1205
+ selectedEl = selectionTargetForHit(target);
1050
1206
  var info = getElementInfo(selectedEl);
1051
1207
  positionOverlay(selectionOverlay, selectedEl);
1052
1208
  window.parent.postMessage({ type: 'element-select', payload: info }, '*');
@@ -1058,9 +1214,9 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1058
1214
  var target = elementFromEditorPoint(e.clientX, e.clientY);
1059
1215
  var info = null;
1060
1216
  if (target) {
1061
- selectedEl = target;
1062
- info = getElementInfo(target);
1063
- positionOverlay(selectionOverlay, target);
1217
+ selectedEl = selectionTargetForHit(target);
1218
+ info = getElementInfo(selectedEl);
1219
+ positionOverlay(selectionOverlay, selectedEl);
1064
1220
  window.parent.postMessage({ type: 'element-select', payload: info }, '*');
1065
1221
  }
1066
1222
  window.parent.postMessage({
@@ -1117,6 +1273,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1117
1273
  return Number.isFinite(num) ? num : 0;
1118
1274
  }
1119
1275
 
1276
+ function readFinitePx(value) {
1277
+ if (!value || value === 'auto') return null;
1278
+ var num = parseFloat(value);
1279
+ return Number.isFinite(num) ? num : null;
1280
+ }
1281
+
1120
1282
  function currentRotation(el) {
1121
1283
  var transform = el.style.transform || window.getComputedStyle(el).transform || '';
1122
1284
  var match = transform.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/);
@@ -1381,10 +1543,10 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1381
1543
  }
1382
1544
  }
1383
1545
 
1384
- function postVisualStructureChange(el, target) {
1546
+ function postVisualStructureChange(el, target, origin) {
1385
1547
  if (!el || !target || !target.anchor) return;
1386
1548
  var requestId = 'move-' + Date.now() + '-' + Math.random().toString(16).slice(2);
1387
- pendingStructureMove = { requestId: requestId, el: el, target: target };
1549
+ pendingStructureMove = { requestId: requestId, el: el, target: target, origin: origin || null };
1388
1550
  window.parent.postMessage({
1389
1551
  type: 'visual-structure-change',
1390
1552
  requestId: requestId,
@@ -1458,11 +1620,15 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1458
1620
  applyRuntimeReorder(reorderEl, currentTarget);
1459
1621
  postVisualDuplicateChange(originalSelectedEl, reorderEl, currentTarget);
1460
1622
  } else {
1623
+ // Capture the pre-drag DOM anchor so we can revert if the parent
1624
+ // reports applied===false on the structure-ack.
1625
+ var prevParent = reorderEl.parentElement;
1626
+ var prevNextSibling = reorderEl.nextSibling;
1461
1627
  // Optimistically apply the reorder in the DOM for immediate
1462
1628
  // visual feedback; the visual-structure-ack handler will confirm
1463
1629
  // or revert once the parent processes the change.
1464
1630
  applyRuntimeReorder(reorderEl, currentTarget);
1465
- postVisualStructureChange(reorderEl, currentTarget);
1631
+ postVisualStructureChange(reorderEl, currentTarget, { prevParent: prevParent, prevNextSibling: prevNextSibling });
1466
1632
  }
1467
1633
  }
1468
1634
  document.addEventListener('mousemove', onReorderMove, true);
@@ -1479,7 +1645,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1479
1645
  // clear-selection postMessage cannot swap selectedEl mid-drag and cause
1480
1646
  // mutations on the wrong element or a null-deref in onUp.
1481
1647
  var dragEl = selectedEl;
1648
+ var moved = false;
1649
+ var DRAG_THRESHOLD = 3;
1482
1650
  function onMove(ev) {
1651
+ if (!moved && Math.hypot(ev.clientX - startX, ev.clientY - startY) > DRAG_THRESHOLD) {
1652
+ moved = true;
1653
+ }
1483
1654
  var nextLeft = originLeft + ev.clientX - startX;
1484
1655
  var nextTop = originTop + ev.clientY - startY;
1485
1656
  dragEl.style.left = Math.round(nextLeft) + 'px';
@@ -1492,6 +1663,14 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1492
1663
  document.removeEventListener('mouseup', onUp, true);
1493
1664
  hideTransformBadge();
1494
1665
  if (!dragEl) return;
1666
+ if (duplicatedForDrag && !moved) {
1667
+ // Alt-click with no real drag — remove the premature clone and restore the original selection.
1668
+ if (dragEl.parentElement) dragEl.parentElement.removeChild(dragEl);
1669
+ selectedEl = originalSelectedEl;
1670
+ positionOverlay(selectionOverlay, selectedEl);
1671
+ window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
1672
+ return;
1673
+ }
1495
1674
  if (duplicatedForDrag) {
1496
1675
  postVisualDuplicateChange(originalSelectedEl, dragEl);
1497
1676
  } else {
@@ -1535,9 +1714,19 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1535
1714
  // Snapshot the element so a concurrent clear-selection postMessage cannot
1536
1715
  // cause a null-deref in onMove/onUp.
1537
1716
  var resizeEl = selectedEl;
1717
+ // Capture the element rotation once at drag-start so per-move projection is
1718
+ // cheap and consistent even if the transform changes during the drag.
1719
+ var resizeTheta = currentRotation(resizeEl) * Math.PI / 180;
1538
1720
  function nextRect(ev) {
1539
- var dx = ev.clientX - startX;
1540
- var dy = ev.clientY - startY;
1721
+ var screenDx = ev.clientX - startX;
1722
+ var screenDy = ev.clientY - startY;
1723
+ // Project the screen-space pointer delta into the element's local
1724
+ // (un-rotated) coordinate frame so handles behave relative to the
1725
+ // visible rotated box rather than screen axes.
1726
+ var cosT = Math.cos(resizeTheta);
1727
+ var sinT = Math.sin(resizeTheta);
1728
+ var dx = screenDx * cosT + screenDy * sinT;
1729
+ var dy = -screenDx * sinT + screenDy * cosT;
1541
1730
  var left = origin.left;
1542
1731
  var top = origin.top;
1543
1732
  var width = origin.width;
@@ -1554,9 +1743,16 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1554
1743
  if (handle.indexOf('s') !== -1) height = origin.height + dy;
1555
1744
  // Apply Shift / scaleToolEnabled aspect-ratio lock BEFORE the min-size
1556
1745
  // clamp so the ratio is computed from unclamped values (bug fix).
1557
- if (ev.shiftKey && handle.length === 2) {
1558
- if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
1559
- else width = height * origin.ratio;
1746
+ if (ev.shiftKey) {
1747
+ // Shift locks aspect ratio for ALL 8 handles (corners and edges).
1748
+ if (handle === 'e' || handle === 'w') {
1749
+ height = width / origin.ratio;
1750
+ } else if (handle === 'n' || handle === 's') {
1751
+ width = height * origin.ratio;
1752
+ } else if (handle.length === 2) {
1753
+ if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
1754
+ else width = height * origin.ratio;
1755
+ }
1560
1756
  }
1561
1757
  if (scaleToolEnabled) {
1562
1758
  // Scale tool: enforce aspect ratio on all 8 handles, not just corners.
@@ -1569,15 +1765,27 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1569
1765
  else width = height * origin.ratio;
1570
1766
  }
1571
1767
  }
1572
- // Clamp after ratio correction.
1573
- var rawWidth = width;
1574
- var rawHeight = height;
1575
- width = Math.max(8, width);
1576
- height = Math.max(8, height);
1577
- // Re-anchor the pinned edge for w/n handles after clamping so the
1578
- // opposite edge doesn't drift when the element hits minimum size.
1579
- if (handle.indexOf('w') !== -1) left += (rawWidth - width);
1580
- if (handle.indexOf('n') !== -1) top += (rawHeight - height);
1768
+ // Clamp to minimum size.
1769
+ var clampedW = Math.max(8, width);
1770
+ var clampedH = Math.max(8, height);
1771
+ // After clamping, re-apply the ratio if Shift or scale tool is active so
1772
+ // the clamped dimension doesn't silently break the locked aspect ratio.
1773
+ if (ev.shiftKey || scaleToolEnabled) {
1774
+ if (clampedW !== width) {
1775
+ // Width was clamped; re-derive height from the clamped width.
1776
+ clampedH = Math.max(8, clampedW / origin.ratio);
1777
+ } else if (clampedH !== height) {
1778
+ // Height was clamped; re-derive width from the clamped height.
1779
+ clampedW = Math.max(8, clampedH * origin.ratio);
1780
+ }
1781
+ }
1782
+ width = clampedW;
1783
+ height = clampedH;
1784
+ // Re-anchor the pinned edge for w/n handles after aspect-ratio lock and
1785
+ // clamping so the opposite (e/s) edge stays fixed regardless of whether
1786
+ // the dimension change was driven by raw dx/dy or by the ratio lock.
1787
+ if (handle.indexOf('w') !== -1) left = origin.left + (origin.width - width);
1788
+ if (handle.indexOf('n') !== -1) top = origin.top + (origin.height - height);
1581
1789
  if (ev.altKey) {
1582
1790
  if (handle.indexOf('w') !== -1 || handle.indexOf('e') !== -1) left = origin.left - (width - origin.width) / 2;
1583
1791
  if (handle.indexOf('n') !== -1 || handle.indexOf('s') !== -1) top = origin.top - (height - origin.height) / 2;
@@ -1688,6 +1896,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1688
1896
  document.addEventListener('keydown', function(e) {
1689
1897
  if (!shouldForwardDesignHotkey(e)) return;
1690
1898
  stopNativeInteraction(e);
1899
+ if (e.key === 'Escape') clearRuntimeSelection();
1691
1900
  window.parent.postMessage({
1692
1901
  type: 'design-hotkey',
1693
1902
  key: e.key,
@@ -1741,7 +1950,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1741
1950
  stopNativeInteraction(e);
1742
1951
  var target = findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY));
1743
1952
  if (!target || target.nodeType !== 1) return;
1744
- selectedEl = target;
1953
+ // Anchor the selection identity to the nearest source-backed element. Text
1954
+ // editing still operates on the actual target text node, but a later
1955
+ // style edit posts from selectedEl, so it must point at a patchable
1956
+ // code-layer node rather than a runtime-only descendant (which would emit a
1957
+ // brittle body > div:nth-of-type(...) selector that never resolves).
1958
+ selectedEl = selectionTargetForHit(target) || target;
1745
1959
  var originalText = target.textContent || '';
1746
1960
  var originalHtml = target.innerHTML || '';
1747
1961
  var committed = false;
@@ -1754,6 +1968,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1754
1968
  setTextEditingPointerPassthrough(true);
1755
1969
  positionOverlay(selectionOverlay, target);
1756
1970
  window.parent.postMessage({ type: 'element-select', payload: getElementInfo(target) }, '*');
1971
+ window.parent.postMessage({ type: 'element-dblclick-text', payload: getElementInfo(target) }, '*');
1757
1972
  postTextEditingState(target, true);
1758
1973
 
1759
1974
  function finish(commit) {
@@ -1839,7 +2054,11 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1839
2054
  return;
1840
2055
  }
1841
2056
  if (hoveredEl && hoveredEl.closest('[data-agent-native-text-editing]')) return;
1842
- positionOverlay(highlightOverlay, hoveredEl);
2057
+ if (hoveredEl === selectedEl) {
2058
+ highlightOverlay.style.display = 'none';
2059
+ } else {
2060
+ positionOverlay(highlightOverlay, hoveredEl);
2061
+ }
1843
2062
  if (e.altKey && selectedEl && hoveredEl && selectedEl !== hoveredEl) {
1844
2063
  showMeasurements(selectedEl, hoveredEl);
1845
2064
  } else {
@@ -1854,6 +2073,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1854
2073
  hoveredEl = null;
1855
2074
  highlightOverlay.style.display = 'none';
1856
2075
  hideMeasurements();
2076
+ window.parent.postMessage({ type: 'element-hover', payload: null }, '*');
1857
2077
  }, true);
1858
2078
 
1859
2079
  window.addEventListener('keyup', function(e) {
@@ -1863,16 +2083,21 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1863
2083
  window.addEventListener('message', function(e) {
1864
2084
  if (e.source !== window.parent) return;
1865
2085
  if (!e.data) return;
2086
+ if (e.data.type === 'set-editor-chrome-scale') {
2087
+ // Live-update the constant-size chrome scale WITHOUT rebuilding srcdoc.
2088
+ // Rebuilding srcdoc reloads the iframe and flashes the content white.
2089
+ editorChromeScaleX = Math.max(0.05, Number(e.data.scaleX) || 1);
2090
+ editorChromeScaleY = Math.max(0.05, Number(e.data.scaleY) || editorChromeScaleX);
2091
+ applyEditorChromeScale();
2092
+ if (selectedEl || hoveredEl) refreshOverlays();
2093
+ return;
2094
+ }
1866
2095
  if (e.data.type === 'scale-tool-mode') {
1867
2096
  scaleToolEnabled = !!e.data.enabled;
1868
2097
  return;
1869
2098
  }
1870
2099
  if (e.data.type === 'clear-selection') {
1871
- selectedEl = null;
1872
- hoveredEl = null;
1873
- selectionOverlay.style.display = 'none';
1874
- highlightOverlay.style.display = 'none';
1875
- hideMeasurements();
2100
+ clearRuntimeSelection();
1876
2101
  return;
1877
2102
  }
1878
2103
  if (e.data.type === 'select-element') {
@@ -1906,9 +2131,8 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1906
2131
  }
1907
2132
  if (!target) return;
1908
2133
  selectedEl = target;
1909
- var selectedInfo = getElementInfo(target);
1910
2134
  positionOverlay(selectionOverlay, target);
1911
- window.parent.postMessage({ type: 'element-select', payload: selectedInfo }, '*');
2135
+ if (hoveredEl === selectedEl) highlightOverlay.style.display = 'none';
1912
2136
  return;
1913
2137
  }
1914
2138
  if (e.data.type === 'hover-element') {
@@ -1931,7 +2155,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1931
2155
  }
1932
2156
  var hoverTarget = findRuntimeTarget(String(e.data.selector || ''), hoverCandidates);
1933
2157
  hoveredEl = hoverTarget;
1934
- if (hoveredEl && !isLayerInteractionBlocked(hoveredEl)) {
2158
+ if (hoveredEl && !isLayerInteractionBlocked(hoveredEl) && hoveredEl !== selectedEl) {
1935
2159
  positionOverlay(highlightOverlay, hoveredEl);
1936
2160
  } else {
1937
2161
  highlightOverlay.style.display = 'none';
@@ -1958,10 +2182,20 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1958
2182
  var move = pendingStructureMove;
1959
2183
  pendingStructureMove = null;
1960
2184
  if (e.data.applied) {
1961
- applyRuntimeReorder(move.el, move.target);
1962
- selectedEl = move.el;
1963
- positionOverlay(selectionOverlay, selectedEl);
1964
- window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
2185
+ if (move.el && move.el.isConnected) {
2186
+ applyRuntimeReorder(move.el, move.target);
2187
+ selectedEl = move.el;
2188
+ positionOverlay(selectionOverlay, selectedEl);
2189
+ window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
2190
+ }
2191
+ } else {
2192
+ // Revert the optimistic reorder to its pre-drag position.
2193
+ if (move.el && move.el.isConnected && move.origin && move.origin.prevParent && move.origin.prevParent.isConnected) {
2194
+ move.origin.prevParent.insertBefore(move.el, move.origin.prevNextSibling);
2195
+ selectedEl = move.el;
2196
+ positionOverlay(selectionOverlay, selectedEl);
2197
+ window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
2198
+ }
1965
2199
  }
1966
2200
  return;
1967
2201
  }
@@ -1988,6 +2222,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1988
2222
 
1989
2223
  window.addEventListener('scroll', refreshOverlays, true);
1990
2224
  window.addEventListener('resize', refreshOverlays);
2225
+ applyEditorChromeScale();
1991
2226
  })();
1992
2227
  </script>
1993
2228
  `;
@@ -2003,12 +2238,17 @@ interface DesignCanvasProps {
2003
2238
  viewportHeight: number;
2004
2239
  displayWidth: number;
2005
2240
  displayHeight: number;
2241
+ fluid?: boolean;
2006
2242
  };
2243
+ editorChromeScaleX?: number;
2244
+ editorChromeScaleY?: number;
2007
2245
  editMode: boolean;
2008
2246
  interactMode: boolean;
2247
+ readOnly?: boolean;
2009
2248
  scaleMode?: boolean;
2010
2249
  onElementSelect: (info: ElementInfo) => void;
2011
- onElementHover: (info: ElementInfo) => void;
2250
+ onElementHover: (info: ElementInfo | null) => void;
2251
+ onClearSelection?: () => void;
2012
2252
  onVisualStyleChange?: (
2013
2253
  selector: string,
2014
2254
  styles: Record<string, string>,
@@ -2025,6 +2265,7 @@ interface DesignCanvasProps {
2025
2265
  selector?: string;
2026
2266
  hasRange?: boolean;
2027
2267
  }) => void;
2268
+ onElementDblClickText?: (info: ElementInfo) => void;
2028
2269
  onIframeHotkey?: (event: IframeHotkeyPayload) => void;
2029
2270
  onIframeContextMenu?: (event: IframeContextMenuPayload) => void;
2030
2271
  onVisualStructureChange?: (
@@ -2063,6 +2304,7 @@ interface DesignCanvasProps {
2063
2304
  lockedSelectors?: string[];
2064
2305
  hiddenSelectors?: string[];
2065
2306
  clearSelectionRequest?: number;
2307
+ registerRuntimeBridge?: boolean;
2066
2308
  /** Called when the user exits pin mode. */
2067
2309
  onExitPinMode?: () => void;
2068
2310
  /** Stable id of the open design (used for pin scoping + agent prompt). */
@@ -2119,15 +2361,20 @@ export function DesignCanvas({
2119
2361
  onZoomChange,
2120
2362
  deviceFrame,
2121
2363
  embeddedFrame,
2364
+ editorChromeScaleX = 1,
2365
+ editorChromeScaleY = editorChromeScaleX,
2122
2366
  editMode,
2123
2367
  interactMode,
2368
+ readOnly = false,
2124
2369
  scaleMode = false,
2125
2370
  clearSelectionRequest,
2126
2371
  onElementSelect,
2127
2372
  onElementHover,
2373
+ onClearSelection,
2128
2374
  onVisualStyleChange,
2129
2375
  onTextContentChange,
2130
2376
  onTextEditingStateChange,
2377
+ onElementDblClickText,
2131
2378
  onIframeHotkey,
2132
2379
  onIframeContextMenu,
2133
2380
  onVisualStructureChange,
@@ -2143,6 +2390,7 @@ export function DesignCanvas({
2143
2390
  lockedSelectors = [],
2144
2391
  hiddenSelectors = [],
2145
2392
  onExitPinMode,
2393
+ registerRuntimeBridge = true,
2146
2394
  designId,
2147
2395
  designTitle,
2148
2396
  commentContextId,
@@ -2198,13 +2446,17 @@ export function DesignCanvas({
2198
2446
  // outside Edit mode. The editor chrome bridge is omitted only for Interact.
2199
2447
  const srcdoc = useMemo(() => {
2200
2448
  if (externalPreviewUrl) return undefined;
2201
- const editorChromeBridge = interactMode
2202
- ? ""
2203
- : createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
2204
- EDITOR_CHROME_BRIDGE_SCRIPT.replace(
2205
- "__TEXT_EDITING_ENABLED__",
2206
- editMode ? "true" : "false",
2207
- );
2449
+ const editorChromeBridge =
2450
+ interactMode || readOnly
2451
+ ? ""
2452
+ : createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
2453
+ EDITOR_CHROME_BRIDGE_SCRIPT.replace(
2454
+ "__READ_ONLY__",
2455
+ readOnly ? "true" : "false",
2456
+ )
2457
+ .replace("__TEXT_EDITING_ENABLED__", editMode ? "true" : "false")
2458
+ .replace("__EDITOR_CHROME_SCALE_X__", String(editorChromeScaleX))
2459
+ .replace("__EDITOR_CHROME_SCALE_Y__", String(editorChromeScaleY));
2208
2460
  const embeddedWheelBridge = EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
2209
2461
  "__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
2210
2462
  isEmbeddedFrame ? "true" : "false",
@@ -2223,11 +2475,17 @@ export function DesignCanvas({
2223
2475
  }
2224
2476
  // No body/html tags — wrap it
2225
2477
  return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${renderedContent}${bridgeToInject}</body></html>`;
2478
+ // editorChromeScaleX/Y are intentionally NOT deps: they only seed the initial
2479
+ // baked chrome scale. Live zoom updates flow through the set-editor-chrome-scale
2480
+ // postMessage above. Including them here rebuilds srcdoc on every zoom commit,
2481
+ // which reloads the iframe and flashes the screen content white.
2482
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2226
2483
  }, [
2227
2484
  editMode,
2228
2485
  externalPreviewUrl,
2229
2486
  interactMode,
2230
2487
  isEmbeddedFrame,
2488
+ readOnly,
2231
2489
  renderedContent,
2232
2490
  ]);
2233
2491
 
@@ -2245,6 +2503,10 @@ export function DesignCanvas({
2245
2503
  return;
2246
2504
  }
2247
2505
  if (!e.data || !e.data.type) return;
2506
+ if (e.data.type === "clear-selection") {
2507
+ onClearSelection?.();
2508
+ return;
2509
+ }
2248
2510
  if (e.data.type === "element-select") {
2249
2511
  onElementSelect(e.data.payload);
2250
2512
  }
@@ -2352,6 +2614,10 @@ export function DesignCanvas({
2352
2614
  });
2353
2615
  return;
2354
2616
  }
2617
+ if (e.data.type === "element-dblclick-text") {
2618
+ onElementDblClickText?.(e.data.payload);
2619
+ return;
2620
+ }
2355
2621
  if (e.data.type === "design-hotkey") {
2356
2622
  onIframeHotkey?.({
2357
2623
  key: String(e.data.key || ""),
@@ -2470,9 +2736,11 @@ export function DesignCanvas({
2470
2736
  }, [
2471
2737
  onElementSelect,
2472
2738
  onElementHover,
2739
+ onClearSelection,
2473
2740
  onVisualStyleChange,
2474
2741
  onTextContentChange,
2475
2742
  onTextEditingStateChange,
2743
+ onElementDblClickText,
2476
2744
  onIframeHotkey,
2477
2745
  onIframeContextMenu,
2478
2746
  onVisualStructureChange,
@@ -2548,6 +2816,21 @@ export function DesignCanvas({
2548
2816
  );
2549
2817
  }, [clearSelectionRequest]);
2550
2818
 
2819
+ // Push the constant-size chrome scale into the iframe LIVE (CSS vars only) when
2820
+ // overview zoom settles. This is intentionally separate from the srcdoc build so
2821
+ // a scale change never rebuilds srcdoc / reloads the iframe (which flashes the
2822
+ // content white). The baked __EDITOR_CHROME_SCALE__ values cover first paint.
2823
+ useEffect(() => {
2824
+ iframeRef.current?.contentWindow?.postMessage(
2825
+ {
2826
+ type: "set-editor-chrome-scale",
2827
+ scaleX: editorChromeScaleX,
2828
+ scaleY: editorChromeScaleY,
2829
+ },
2830
+ "*",
2831
+ );
2832
+ }, [editorChromeScaleX, editorChromeScaleY]);
2833
+
2551
2834
  const sendStyleChange = useCallback(
2552
2835
  (selector: string, property: string, value: string) => {
2553
2836
  const iframe = iframeRef.current;
@@ -2597,15 +2880,31 @@ export function DesignCanvas({
2597
2880
 
2598
2881
  // Expose iframe runtime mutations for the editor orchestrator.
2599
2882
  useEffect(() => {
2883
+ if (!registerRuntimeBridge) return;
2600
2884
  (window as any).__designCanvasSendStyle = sendStyleChange;
2601
2885
  (window as any).__designCanvasReplaceContent = replacePreviewContent;
2602
2886
  (window as any).__designCanvasDeleteElement = deleteRuntimeElement;
2603
2887
  return () => {
2604
- delete (window as any).__designCanvasSendStyle;
2605
- delete (window as any).__designCanvasReplaceContent;
2606
- delete (window as any).__designCanvasDeleteElement;
2888
+ if ((window as any).__designCanvasSendStyle === sendStyleChange) {
2889
+ delete (window as any).__designCanvasSendStyle;
2890
+ }
2891
+ if (
2892
+ (window as any).__designCanvasReplaceContent === replacePreviewContent
2893
+ ) {
2894
+ delete (window as any).__designCanvasReplaceContent;
2895
+ }
2896
+ if (
2897
+ (window as any).__designCanvasDeleteElement === deleteRuntimeElement
2898
+ ) {
2899
+ delete (window as any).__designCanvasDeleteElement;
2900
+ }
2607
2901
  };
2608
- }, [deleteRuntimeElement, replacePreviewContent, sendStyleChange]);
2902
+ }, [
2903
+ deleteRuntimeElement,
2904
+ registerRuntimeBridge,
2905
+ replacePreviewContent,
2906
+ sendStyleChange,
2907
+ ]);
2609
2908
 
2610
2909
  // Device dimensions match real-world devices. iframes are replaced elements
2611
2910
  // with an intrinsic 300×150 size, so `aspect-ratio` + `height: auto` doesn't
@@ -2622,6 +2921,7 @@ export function DesignCanvas({
2622
2921
 
2623
2922
  const { width: iframeWidth, height: iframeHeight } =
2624
2923
  deviceDimensions[deviceFrame];
2924
+ const embeddedFrameFluid = embeddedFrame?.fluid === true;
2625
2925
 
2626
2926
  // Wrap the iframe in a positioned container so DrawOverlay /
2627
2927
  // CanvasCommentPins can absolutely-position themselves on top of the
@@ -2634,9 +2934,15 @@ export function DesignCanvas({
2634
2934
  <div
2635
2935
  className="design-canvas-iframe-wrapper relative inline-block ring-1 ring-border/60 shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.45)]"
2636
2936
  style={{
2637
- width: embeddedFrame ? embeddedFrame.viewportWidth : iframeWidth,
2937
+ width: embeddedFrame
2938
+ ? embeddedFrameFluid
2939
+ ? "100%"
2940
+ : embeddedFrame.viewportWidth
2941
+ : iframeWidth,
2638
2942
  height: embeddedFrame
2639
- ? embeddedFrame.viewportHeight
2943
+ ? embeddedFrameFluid
2944
+ ? "100%"
2945
+ : embeddedFrame.viewportHeight
2640
2946
  : deviceFrame === "none"
2641
2947
  ? "100%"
2642
2948
  : (iframeHeight ?? undefined),
@@ -2660,6 +2966,7 @@ export function DesignCanvas({
2660
2966
  visible={!!drawMode}
2661
2967
  canvasInteractive={!pinMode}
2662
2968
  queuedAnnotationCount={queuedAnnotationPins.length}
2969
+ zoom={zoom}
2663
2970
  onClose={() => onExitDrawMode?.()}
2664
2971
  onSend={(annotations, instruction, canvasSize) => {
2665
2972
  const summary = annotations
@@ -2713,6 +3020,17 @@ export function DesignCanvas({
2713
3020
  );
2714
3021
 
2715
3022
  if (embeddedFrame) {
3023
+ if (embeddedFrameFluid) {
3024
+ return (
3025
+ <div
3026
+ ref={scrollContainerRef}
3027
+ className="relative h-full w-full overflow-hidden"
3028
+ >
3029
+ {iframeElement}
3030
+ </div>
3031
+ );
3032
+ }
3033
+
2716
3034
  const scaleX =
2717
3035
  embeddedFrame.displayWidth / Math.max(1, embeddedFrame.viewportWidth);
2718
3036
  const scaleY =