@agent-native/core 0.80.8 → 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 (281) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +12 -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/error-format.ts +25 -0
  18. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +13 -11
  19. package/corpus/core/src/client/guided-questions.tsx +32 -4
  20. package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +33 -1
  21. package/corpus/core/src/client/resources/BuiltinCapabilityDetail.tsx +8 -0
  22. package/corpus/core/src/client/resources/use-builtin-capabilities.ts +4 -2
  23. package/corpus/core/src/client/session-replay.ts +34 -0
  24. package/corpus/core/src/client/sharing/ShareButton.tsx +106 -21
  25. package/corpus/core/src/coding-tools/index.ts +1 -1
  26. package/corpus/core/src/mcp/index.ts +2 -0
  27. package/corpus/core/src/mcp/screen-memory-stdio.ts +290 -0
  28. package/corpus/core/src/mcp-client/builtin-capabilities.ts +13 -1
  29. package/corpus/core/src/mcp-client/index.ts +9 -0
  30. package/corpus/core/src/mcp-client/screen-memory-local.ts +461 -0
  31. package/corpus/core/src/onboarding/types.ts +7 -0
  32. package/corpus/core/src/server/agent-chat-plugin.ts +25 -0
  33. package/corpus/core/src/server/agents-bundle.ts +35 -7
  34. package/corpus/core/src/styles/agent-native.css +5 -0
  35. package/corpus/core/src/templates/default/app/global.css +35 -35
  36. package/corpus/templates/analytics/AGENTS.md +6 -0
  37. package/corpus/templates/analytics/actions/github-repo-files.ts +9 -0
  38. package/corpus/templates/analytics/app/components/layout/Layout.tsx +4 -4
  39. package/corpus/templates/analytics/app/global.css +144 -40
  40. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +1 -1
  41. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +1 -1
  42. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
  43. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +4 -4
  44. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -9
  45. package/corpus/templates/analytics/changelog/2026-06-29-agents-can-search-and-read-connected-github-repositories-whe.md +6 -0
  46. package/corpus/templates/analytics/changelog/2026-06-29-sessions-and-explorer-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  47. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -1
  48. package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +1 -1
  49. package/corpus/templates/assets/app/global.css +99 -35
  50. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  51. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +4 -4
  52. package/corpus/templates/assets/app/routes/library.tsx +4 -4
  53. package/corpus/templates/assets/changelog/2026-06-29-asset-library-and-detail-layouts-adapt-when-the-agent-sidebar.md +6 -0
  54. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  55. package/corpus/templates/brain/app/global.css +63 -35
  56. package/corpus/templates/brain/app/routes/search.tsx +1 -1
  57. package/corpus/templates/brain/app/routes/settings.tsx +1 -1
  58. package/corpus/templates/brain/app/routes/sources.tsx +2 -2
  59. package/corpus/templates/brain/changelog/2026-06-29-search-settings-and-source-layouts-adapt-when-the-agent-sidebar.md +6 -0
  60. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +4 -4
  61. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +3 -2
  62. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -2
  63. package/corpus/templates/calendar/app/global.css +69 -35
  64. package/corpus/templates/calendar/changelog/2026-06-29-event-and-find-time-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  65. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  66. package/corpus/templates/chat/app/global.css +35 -35
  67. package/corpus/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  68. package/corpus/templates/clips/AGENTS.md +13 -0
  69. package/corpus/templates/clips/actions/get-screen-memory-status.ts +12 -0
  70. package/corpus/templates/clips/actions/query-screen-memory-context.ts +29 -0
  71. package/corpus/templates/clips/actions/view-screen.ts +2 -2
  72. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  73. package/corpus/templates/clips/app/global.css +47 -35
  74. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +3 -3
  75. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +45 -18
  76. package/corpus/templates/clips/changelog/2026-06-29-meeting-detail-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  77. package/corpus/templates/clips/changelog/2026-06-29-screen-memory-keeps-a-local-rolling-screen-buffer-for-agent-context.md +5 -0
  78. package/corpus/templates/clips/changelog/2026-06-29-video-chat-is-easier-to-find-on-recording-pages-and-s3-setup.md +6 -0
  79. package/corpus/templates/clips/chrome-extension/src/styles.css +39 -39
  80. package/corpus/templates/clips/desktop/src/app.tsx +290 -1
  81. package/corpus/templates/clips/desktop/src/shared/config.ts +83 -0
  82. package/corpus/templates/clips/desktop/src/styles.css +67 -52
  83. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +50 -0
  84. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +17 -0
  85. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +13 -13
  86. package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +990 -0
  87. package/corpus/templates/clips/server/plugins/onboarding.ts +2 -0
  88. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +3 -3
  89. package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
  90. package/corpus/templates/content/app/global.css +29 -15
  91. package/corpus/templates/content/changelog/2026-06-29-database-gallery-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  92. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +25 -20
  93. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
  94. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +8 -0
  95. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +13 -0
  96. package/corpus/templates/design/AGENTS.md +20 -12
  97. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1054 -0
  98. package/corpus/templates/design/actions/apply-visual-edit.ts +24 -4
  99. package/corpus/templates/design/actions/export-coding-handoff.ts +12 -4
  100. package/corpus/templates/design/actions/generate-design.ts +6 -21
  101. package/corpus/templates/design/actions/generate-screens.ts +43 -2
  102. package/corpus/templates/design/actions/get-design.ts +1 -0
  103. package/corpus/templates/design/actions/present-design-variants.ts +355 -30
  104. package/corpus/templates/design/actions/view-screen.ts +75 -31
  105. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +329 -69
  106. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
  107. package/corpus/templates/design/app/components/design/EditPanel.tsx +479 -77
  108. package/corpus/templates/design/app/components/design/LayersPanel.tsx +380 -216
  109. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1050 -196
  110. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +83 -206
  111. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +1 -1
  112. package/corpus/templates/design/app/components/design/index.ts +0 -1
  113. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +225 -133
  114. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +122 -11
  115. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +24 -5
  116. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -1
  117. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +37 -12
  118. package/corpus/templates/design/app/components/design/inspector/index.ts +1 -0
  119. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +12 -5
  120. package/corpus/templates/design/app/components/design/types.ts +9 -0
  121. package/corpus/templates/design/app/components/layout/Layout.tsx +23 -5
  122. package/corpus/templates/design/app/global.css +77 -49
  123. package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
  124. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +8 -1
  125. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
  126. package/corpus/templates/design/app/i18n-data.ts +45 -3
  127. package/corpus/templates/design/app/pages/DesignEditor.tsx +2602 -657
  128. package/corpus/templates/design/app/root.tsx +1 -1
  129. package/corpus/templates/design/changelog/2026-06-29-containers-no-longer-block-selecting-nested-elements-and-nor.md +6 -0
  130. package/corpus/templates/design/changelog/2026-06-29-design-agents-now-generate-and-refine-variants-from-stronger.md +6 -0
  131. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +6 -0
  132. package/corpus/templates/design/changelog/2026-06-29-design-questions-now-appear-as-a-cleaner-canvas-intake-with-.md +6 -0
  133. package/corpus/templates/design/changelog/2026-06-29-design-variant-directions-now-appear-as-board-screens-with-chat-buttons.md +5 -0
  134. package/corpus/templates/design/changelog/2026-06-29-element-selection-no-longer-snaps-back-to-the-previous-layer.md +6 -0
  135. package/corpus/templates/design/changelog/2026-06-29-frame-resize-measurements-now-stay-beside-the-cursor-while-d.md +6 -0
  136. package/corpus/templates/design/changelog/2026-06-29-inspector-number-fields-have-cleaner-tooltips-and-draggable-.md +6 -0
  137. package/corpus/templates/design/changelog/2026-06-29-layer-locking-layer-nesting-generated-question-intake-and-im.md +6 -0
  138. package/corpus/templates/design/changelog/2026-06-29-layer-names-in-the-sidebar-can-scroll-horizontally-instead-o.md +6 -0
  139. package/corpus/templates/design/changelog/2026-06-29-layer-nesting-in-the-sidebar-now-uses-tighter-spacing-so-nam.md +6 -0
  140. package/corpus/templates/design/changelog/2026-06-29-layers-no-longer-repeat-document-wrapper-rows-when-a-screen-.md +6 -0
  141. package/corpus/templates/design/changelog/2026-06-29-overview-previews-now-keep-element-hover-and-selection-tied-.md +6 -0
  142. package/corpus/templates/design/changelog/2026-06-29-pressing-escape-now-cancels-a-stuck-screen-drag-in-the-overv.md +6 -0
  143. package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +6 -0
  144. package/corpus/templates/design/changelog/2026-06-29-screen-overview-only-highlights-screens-when-the-frame-or-ti.md +6 -0
  145. package/corpus/templates/design/changelog/2026-06-29-screen-overview-previews-now-resize-to-match-the-selected-de.md +6 -0
  146. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-now-uses-a-pointer-cursor-instead-.md +6 -0
  147. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-outlines-now-stay-locked-to-the-se.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zoom-now-reports-real-screen-scale-separatel.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-feels-smoother-and-selection-outline.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-now-stays-on-the-overview-canvas-ins.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-29-selecting-a-nested-layer-from-all-screens-now-keeps-you-in-a.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-29-share-now-groups-link-sharing-exports-and-agent-handoff.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-loading-skeleton-now-uses-a-softer-charcoa.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-29-the-device-preview-menu-now-sits-next-to-the-preview-button-.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-29-the-inspector-stays-read-only-while-an-empty-design-is-still.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-29-the-missing-design-view-now-matches-the-rest-of-the-editor-c.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-icon-and-tool-option-menus-better-match-the-edi.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-29-typography-controls-now-keep-the-font-name-visible-and-move-.md +6 -0
  159. package/corpus/templates/design/e2e/README.md +3 -3
  160. package/corpus/templates/design/e2e/global-setup.ts +11 -22
  161. package/corpus/templates/design/e2e/helpers.ts +122 -11
  162. package/corpus/templates/design/playwright.config.ts +9 -3
  163. package/corpus/templates/design/server/lib/coding-handoff.ts +126 -2
  164. package/corpus/templates/design/server/plugins/core-routes.ts +1 -2
  165. package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
  166. package/corpus/templates/design/shared/code-layer.ts +125 -22
  167. package/corpus/templates/design/shared/generation-session.ts +41 -0
  168. package/corpus/templates/dispatch/app/global.css +79 -35
  169. package/corpus/templates/dispatch/app/routes/integrations.tsx +8 -7
  170. package/corpus/templates/dispatch/changelog/2026-06-29-integration-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  171. package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -2
  172. package/corpus/templates/forms/app/global.css +64 -35
  173. package/corpus/templates/forms/app/pages/FormsListPage.tsx +2 -2
  174. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +3 -3
  175. package/corpus/templates/forms/changelog/2026-06-29-form-list-and-insight-layouts-adapt-when-the-agent-sidebar.md +6 -0
  176. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +3 -1
  177. package/corpus/templates/macros/app/global.css +25 -9
  178. package/corpus/templates/macros/changelog/2026-06-29-macro-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  179. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -2
  180. package/corpus/templates/mail/app/global.css +42 -36
  181. package/corpus/templates/mail/app/pages/InboxPage.tsx +1 -1
  182. package/corpus/templates/mail/changelog/2026-06-29-the-contact-panel-now-adapts-to-the-available-mail-pane-width.md +6 -0
  183. package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -1
  184. package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +6 -7
  185. package/corpus/templates/plan/app/global.css +74 -42
  186. package/corpus/templates/plan/changelog/2026-06-29-plan-document-blocks-adapt-to-the-available-document-width.md +6 -0
  187. package/corpus/templates/plan/changelog/2026-06-29-plan-titles-and-summaries-can-be-edited-inline-without-focus.md +6 -0
  188. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -1
  189. package/corpus/templates/slides/app/global.css +49 -33
  190. package/corpus/templates/slides/changelog/2026-06-29-slide-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  191. package/corpus/templates/videos/app/components/layout/Layout.tsx +3 -1
  192. package/corpus/templates/videos/app/global.css +35 -35
  193. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  194. package/dist/cli/mcp.d.ts.map +1 -1
  195. package/dist/cli/mcp.js +10 -0
  196. package/dist/cli/mcp.js.map +1 -1
  197. package/dist/cli/skills.d.ts.map +1 -1
  198. package/dist/cli/skills.js +58 -12
  199. package/dist/cli/skills.js.map +1 -1
  200. package/dist/client/error-format.d.ts.map +1 -1
  201. package/dist/client/error-format.js +17 -0
  202. package/dist/client/error-format.js.map +1 -1
  203. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  204. package/dist/client/extensions/ExtensionViewer.js +3 -4
  205. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  206. package/dist/client/guided-questions.d.ts +4 -0
  207. package/dist/client/guided-questions.d.ts.map +1 -1
  208. package/dist/client/guided-questions.js +16 -7
  209. package/dist/client/guided-questions.js.map +1 -1
  210. package/dist/client/onboarding/OnboardingPanel.js +26 -1
  211. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  212. package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -1
  213. package/dist/client/resources/BuiltinCapabilityDetail.js +1 -1
  214. package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -1
  215. package/dist/client/resources/use-builtin-capabilities.d.ts +1 -1
  216. package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -1
  217. package/dist/client/resources/use-builtin-capabilities.js +2 -1
  218. package/dist/client/resources/use-builtin-capabilities.js.map +1 -1
  219. package/dist/client/session-replay.d.ts.map +1 -1
  220. package/dist/client/session-replay.js +31 -0
  221. package/dist/client/session-replay.js.map +1 -1
  222. package/dist/client/sharing/ShareButton.d.ts +14 -0
  223. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  224. package/dist/client/sharing/ShareButton.js +36 -7
  225. package/dist/client/sharing/ShareButton.js.map +1 -1
  226. package/dist/coding-tools/index.js +1 -1
  227. package/dist/coding-tools/index.js.map +1 -1
  228. package/dist/collab/routes.d.ts +1 -1
  229. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  230. package/dist/mcp/index.d.ts +2 -0
  231. package/dist/mcp/index.d.ts.map +1 -1
  232. package/dist/mcp/index.js +1 -0
  233. package/dist/mcp/index.js.map +1 -1
  234. package/dist/mcp/screen-memory-stdio.d.ts +6 -0
  235. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -0
  236. package/dist/mcp/screen-memory-stdio.js +213 -0
  237. package/dist/mcp/screen-memory-stdio.js.map +1 -0
  238. package/dist/mcp-client/builtin-capabilities.d.ts +1 -1
  239. package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -1
  240. package/dist/mcp-client/builtin-capabilities.js +10 -0
  241. package/dist/mcp-client/builtin-capabilities.js.map +1 -1
  242. package/dist/mcp-client/index.d.ts +1 -0
  243. package/dist/mcp-client/index.d.ts.map +1 -1
  244. package/dist/mcp-client/index.js +1 -0
  245. package/dist/mcp-client/index.js.map +1 -1
  246. package/dist/mcp-client/screen-memory-local.d.ts +61 -0
  247. package/dist/mcp-client/screen-memory-local.d.ts.map +1 -0
  248. package/dist/mcp-client/screen-memory-local.js +336 -0
  249. package/dist/mcp-client/screen-memory-local.js.map +1 -0
  250. package/dist/notifications/routes.d.ts +2 -2
  251. package/dist/observability/routes.d.ts +8 -8
  252. package/dist/onboarding/types.d.ts +7 -0
  253. package/dist/onboarding/types.d.ts.map +1 -1
  254. package/dist/onboarding/types.js.map +1 -1
  255. package/dist/resources/handlers.d.ts +2 -2
  256. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  257. package/dist/server/agent-chat-plugin.js +26 -0
  258. package/dist/server/agent-chat-plugin.js.map +1 -1
  259. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  260. package/dist/server/agents-bundle.d.ts.map +1 -1
  261. package/dist/server/agents-bundle.js +22 -7
  262. package/dist/server/agents-bundle.js.map +1 -1
  263. package/dist/server/transcribe-voice.d.ts +1 -1
  264. package/dist/styles/agent-native.css +5 -0
  265. package/dist/templates/default/app/global.css +35 -35
  266. package/docs/content/locales/ar-SA/template-design.mdx +6 -0
  267. package/docs/content/locales/de-DE/template-design.mdx +8 -0
  268. package/docs/content/locales/es-ES/template-design.mdx +10 -2
  269. package/docs/content/locales/fr-FR/template-design.mdx +10 -2
  270. package/docs/content/locales/hi-IN/template-design.mdx +7 -0
  271. package/docs/content/locales/ja-JP/template-design.mdx +7 -0
  272. package/docs/content/locales/ko-KR/template-design.mdx +7 -0
  273. package/docs/content/locales/pt-BR/template-design.mdx +9 -2
  274. package/docs/content/locales/zh-CN/template-design.mdx +6 -0
  275. package/docs/content/locales/zh-TW/template-design.mdx +6 -0
  276. package/docs/content/template-design.mdx +9 -2
  277. package/package.json +1 -1
  278. package/src/templates/default/app/global.css +35 -35
  279. package/corpus/templates/design/app/components/design/VariantGrid.tsx +0 -189
  280. package/corpus/templates/design/app/components/design/VariantHandoffCard.tsx +0 -115
  281. package/corpus/templates/design/app/hooks/use-variant-flow.ts +0 -342
@@ -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,
@@ -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,6 +867,49 @@ 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';
@@ -793,23 +919,26 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
793
919
  // the handles hug the rotated element rather than its inflated AABB.
794
920
  if (overlay === selectionOverlay) {
795
921
  var elCs = window.getComputedStyle(el);
796
- var elLeft = readPx(el.style.left || elCs.left);
797
- var elTop = readPx(el.style.top || elCs.top);
798
- var elW = readPx(el.style.width || elCs.width);
799
- var elH = readPx(el.style.height || elCs.height);
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);
800
926
  var elRot = currentRotation(el);
927
+ var canUseLocalBox = Math.abs(elRot) > 0.01 && elLeft !== null && elTop !== null && elW !== null && elH !== null;
801
928
  // Convert element-local left/top to viewport coords by walking to the
802
929
  // nearest positioned ancestor (same reference frame as getBoundingClientRect).
803
- var parentRect = (el.offsetParent || document.documentElement).getBoundingClientRect();
804
- overlay.style.display = 'block';
805
- overlay.style.left = (parentRect.left + elLeft) + 'px';
806
- overlay.style.top = (parentRect.top + elTop) + 'px';
807
- overlay.style.width = elW + 'px';
808
- overlay.style.height = elH + 'px';
809
- overlay.style.transform = elRot ? 'rotate(' + elRot + 'deg)' : '';
810
- overlay.style.transformOrigin = '0 0';
811
- updatePaddingOverlay(el);
812
- return;
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
+ }
813
942
  }
814
943
  var rect = el.getBoundingClientRect();
815
944
  overlay.style.display = 'block';
@@ -817,10 +946,16 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
817
946
  overlay.style.left = rect.left + 'px';
818
947
  overlay.style.width = rect.width + 'px';
819
948
  overlay.style.height = rect.height + 'px';
949
+ overlay.style.transform = '';
950
+ if (overlay === selectionOverlay) updatePaddingOverlay(el);
820
951
  }
821
952
 
822
953
  function refreshOverlays() {
823
- if (hoveredEl) positionOverlay(highlightOverlay, hoveredEl);
954
+ if (hoveredEl && hoveredEl !== selectedEl) {
955
+ positionOverlay(highlightOverlay, hoveredEl);
956
+ } else {
957
+ highlightOverlay.style.display = 'none';
958
+ }
824
959
  if (selectedEl) positionOverlay(selectionOverlay, selectedEl);
825
960
  }
826
961
 
@@ -1067,7 +1202,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1067
1202
  window.parent.postMessage({ type: 'clear-selection' }, '*');
1068
1203
  return;
1069
1204
  }
1070
- selectedEl = target;
1205
+ selectedEl = selectionTargetForHit(target);
1071
1206
  var info = getElementInfo(selectedEl);
1072
1207
  positionOverlay(selectionOverlay, selectedEl);
1073
1208
  window.parent.postMessage({ type: 'element-select', payload: info }, '*');
@@ -1079,9 +1214,9 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1079
1214
  var target = elementFromEditorPoint(e.clientX, e.clientY);
1080
1215
  var info = null;
1081
1216
  if (target) {
1082
- selectedEl = target;
1083
- info = getElementInfo(target);
1084
- positionOverlay(selectionOverlay, target);
1217
+ selectedEl = selectionTargetForHit(target);
1218
+ info = getElementInfo(selectedEl);
1219
+ positionOverlay(selectionOverlay, selectedEl);
1085
1220
  window.parent.postMessage({ type: 'element-select', payload: info }, '*');
1086
1221
  }
1087
1222
  window.parent.postMessage({
@@ -1138,6 +1273,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1138
1273
  return Number.isFinite(num) ? num : 0;
1139
1274
  }
1140
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
+
1141
1282
  function currentRotation(el) {
1142
1283
  var transform = el.style.transform || window.getComputedStyle(el).transform || '';
1143
1284
  var match = transform.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/);
@@ -1602,9 +1743,16 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1602
1743
  if (handle.indexOf('s') !== -1) height = origin.height + dy;
1603
1744
  // Apply Shift / scaleToolEnabled aspect-ratio lock BEFORE the min-size
1604
1745
  // clamp so the ratio is computed from unclamped values (bug fix).
1605
- if (ev.shiftKey && handle.length === 2) {
1606
- if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
1607
- 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
+ }
1608
1756
  }
1609
1757
  if (scaleToolEnabled) {
1610
1758
  // Scale tool: enforce aspect ratio on all 8 handles, not just corners.
@@ -1617,9 +1765,22 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1617
1765
  else width = height * origin.ratio;
1618
1766
  }
1619
1767
  }
1620
- // Clamp after ratio correction.
1621
- width = Math.max(8, width);
1622
- height = Math.max(8, 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;
1623
1784
  // Re-anchor the pinned edge for w/n handles after aspect-ratio lock and
1624
1785
  // clamping so the opposite (e/s) edge stays fixed regardless of whether
1625
1786
  // the dimension change was driven by raw dx/dy or by the ratio lock.
@@ -1735,6 +1896,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1735
1896
  document.addEventListener('keydown', function(e) {
1736
1897
  if (!shouldForwardDesignHotkey(e)) return;
1737
1898
  stopNativeInteraction(e);
1899
+ if (e.key === 'Escape') clearRuntimeSelection();
1738
1900
  window.parent.postMessage({
1739
1901
  type: 'design-hotkey',
1740
1902
  key: e.key,
@@ -1788,7 +1950,12 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1788
1950
  stopNativeInteraction(e);
1789
1951
  var target = findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY));
1790
1952
  if (!target || target.nodeType !== 1) return;
1791
- 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;
1792
1959
  var originalText = target.textContent || '';
1793
1960
  var originalHtml = target.innerHTML || '';
1794
1961
  var committed = false;
@@ -1801,6 +1968,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1801
1968
  setTextEditingPointerPassthrough(true);
1802
1969
  positionOverlay(selectionOverlay, target);
1803
1970
  window.parent.postMessage({ type: 'element-select', payload: getElementInfo(target) }, '*');
1971
+ window.parent.postMessage({ type: 'element-dblclick-text', payload: getElementInfo(target) }, '*');
1804
1972
  postTextEditingState(target, true);
1805
1973
 
1806
1974
  function finish(commit) {
@@ -1886,7 +2054,11 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1886
2054
  return;
1887
2055
  }
1888
2056
  if (hoveredEl && hoveredEl.closest('[data-agent-native-text-editing]')) return;
1889
- positionOverlay(highlightOverlay, hoveredEl);
2057
+ if (hoveredEl === selectedEl) {
2058
+ highlightOverlay.style.display = 'none';
2059
+ } else {
2060
+ positionOverlay(highlightOverlay, hoveredEl);
2061
+ }
1890
2062
  if (e.altKey && selectedEl && hoveredEl && selectedEl !== hoveredEl) {
1891
2063
  showMeasurements(selectedEl, hoveredEl);
1892
2064
  } else {
@@ -1901,6 +2073,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1901
2073
  hoveredEl = null;
1902
2074
  highlightOverlay.style.display = 'none';
1903
2075
  hideMeasurements();
2076
+ window.parent.postMessage({ type: 'element-hover', payload: null }, '*');
1904
2077
  }, true);
1905
2078
 
1906
2079
  window.addEventListener('keyup', function(e) {
@@ -1910,16 +2083,21 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1910
2083
  window.addEventListener('message', function(e) {
1911
2084
  if (e.source !== window.parent) return;
1912
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
+ }
1913
2095
  if (e.data.type === 'scale-tool-mode') {
1914
2096
  scaleToolEnabled = !!e.data.enabled;
1915
2097
  return;
1916
2098
  }
1917
2099
  if (e.data.type === 'clear-selection') {
1918
- selectedEl = null;
1919
- hoveredEl = null;
1920
- selectionOverlay.style.display = 'none';
1921
- highlightOverlay.style.display = 'none';
1922
- hideMeasurements();
2100
+ clearRuntimeSelection();
1923
2101
  return;
1924
2102
  }
1925
2103
  if (e.data.type === 'select-element') {
@@ -1953,9 +2131,8 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1953
2131
  }
1954
2132
  if (!target) return;
1955
2133
  selectedEl = target;
1956
- var selectedInfo = getElementInfo(target);
1957
2134
  positionOverlay(selectionOverlay, target);
1958
- window.parent.postMessage({ type: 'element-select', payload: selectedInfo }, '*');
2135
+ if (hoveredEl === selectedEl) highlightOverlay.style.display = 'none';
1959
2136
  return;
1960
2137
  }
1961
2138
  if (e.data.type === 'hover-element') {
@@ -1978,7 +2155,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
1978
2155
  }
1979
2156
  var hoverTarget = findRuntimeTarget(String(e.data.selector || ''), hoverCandidates);
1980
2157
  hoveredEl = hoverTarget;
1981
- if (hoveredEl && !isLayerInteractionBlocked(hoveredEl)) {
2158
+ if (hoveredEl && !isLayerInteractionBlocked(hoveredEl) && hoveredEl !== selectedEl) {
1982
2159
  positionOverlay(highlightOverlay, hoveredEl);
1983
2160
  } else {
1984
2161
  highlightOverlay.style.display = 'none';
@@ -2045,6 +2222,7 @@ const EDITOR_CHROME_BRIDGE_SCRIPT = `
2045
2222
 
2046
2223
  window.addEventListener('scroll', refreshOverlays, true);
2047
2224
  window.addEventListener('resize', refreshOverlays);
2225
+ applyEditorChromeScale();
2048
2226
  })();
2049
2227
  </script>
2050
2228
  `;
@@ -2060,12 +2238,17 @@ interface DesignCanvasProps {
2060
2238
  viewportHeight: number;
2061
2239
  displayWidth: number;
2062
2240
  displayHeight: number;
2241
+ fluid?: boolean;
2063
2242
  };
2243
+ editorChromeScaleX?: number;
2244
+ editorChromeScaleY?: number;
2064
2245
  editMode: boolean;
2065
2246
  interactMode: boolean;
2247
+ readOnly?: boolean;
2066
2248
  scaleMode?: boolean;
2067
2249
  onElementSelect: (info: ElementInfo) => void;
2068
- onElementHover: (info: ElementInfo) => void;
2250
+ onElementHover: (info: ElementInfo | null) => void;
2251
+ onClearSelection?: () => void;
2069
2252
  onVisualStyleChange?: (
2070
2253
  selector: string,
2071
2254
  styles: Record<string, string>,
@@ -2082,6 +2265,7 @@ interface DesignCanvasProps {
2082
2265
  selector?: string;
2083
2266
  hasRange?: boolean;
2084
2267
  }) => void;
2268
+ onElementDblClickText?: (info: ElementInfo) => void;
2085
2269
  onIframeHotkey?: (event: IframeHotkeyPayload) => void;
2086
2270
  onIframeContextMenu?: (event: IframeContextMenuPayload) => void;
2087
2271
  onVisualStructureChange?: (
@@ -2120,6 +2304,7 @@ interface DesignCanvasProps {
2120
2304
  lockedSelectors?: string[];
2121
2305
  hiddenSelectors?: string[];
2122
2306
  clearSelectionRequest?: number;
2307
+ registerRuntimeBridge?: boolean;
2123
2308
  /** Called when the user exits pin mode. */
2124
2309
  onExitPinMode?: () => void;
2125
2310
  /** Stable id of the open design (used for pin scoping + agent prompt). */
@@ -2176,15 +2361,20 @@ export function DesignCanvas({
2176
2361
  onZoomChange,
2177
2362
  deviceFrame,
2178
2363
  embeddedFrame,
2364
+ editorChromeScaleX = 1,
2365
+ editorChromeScaleY = editorChromeScaleX,
2179
2366
  editMode,
2180
2367
  interactMode,
2368
+ readOnly = false,
2181
2369
  scaleMode = false,
2182
2370
  clearSelectionRequest,
2183
2371
  onElementSelect,
2184
2372
  onElementHover,
2373
+ onClearSelection,
2185
2374
  onVisualStyleChange,
2186
2375
  onTextContentChange,
2187
2376
  onTextEditingStateChange,
2377
+ onElementDblClickText,
2188
2378
  onIframeHotkey,
2189
2379
  onIframeContextMenu,
2190
2380
  onVisualStructureChange,
@@ -2200,6 +2390,7 @@ export function DesignCanvas({
2200
2390
  lockedSelectors = [],
2201
2391
  hiddenSelectors = [],
2202
2392
  onExitPinMode,
2393
+ registerRuntimeBridge = true,
2203
2394
  designId,
2204
2395
  designTitle,
2205
2396
  commentContextId,
@@ -2255,13 +2446,17 @@ export function DesignCanvas({
2255
2446
  // outside Edit mode. The editor chrome bridge is omitted only for Interact.
2256
2447
  const srcdoc = useMemo(() => {
2257
2448
  if (externalPreviewUrl) return undefined;
2258
- const editorChromeBridge = interactMode
2259
- ? ""
2260
- : createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
2261
- EDITOR_CHROME_BRIDGE_SCRIPT.replace(
2262
- "__TEXT_EDITING_ENABLED__",
2263
- editMode ? "true" : "false",
2264
- );
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));
2265
2460
  const embeddedWheelBridge = EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
2266
2461
  "__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
2267
2462
  isEmbeddedFrame ? "true" : "false",
@@ -2280,11 +2475,17 @@ export function DesignCanvas({
2280
2475
  }
2281
2476
  // No body/html tags — wrap it
2282
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
2283
2483
  }, [
2284
2484
  editMode,
2285
2485
  externalPreviewUrl,
2286
2486
  interactMode,
2287
2487
  isEmbeddedFrame,
2488
+ readOnly,
2288
2489
  renderedContent,
2289
2490
  ]);
2290
2491
 
@@ -2302,6 +2503,10 @@ export function DesignCanvas({
2302
2503
  return;
2303
2504
  }
2304
2505
  if (!e.data || !e.data.type) return;
2506
+ if (e.data.type === "clear-selection") {
2507
+ onClearSelection?.();
2508
+ return;
2509
+ }
2305
2510
  if (e.data.type === "element-select") {
2306
2511
  onElementSelect(e.data.payload);
2307
2512
  }
@@ -2409,6 +2614,10 @@ export function DesignCanvas({
2409
2614
  });
2410
2615
  return;
2411
2616
  }
2617
+ if (e.data.type === "element-dblclick-text") {
2618
+ onElementDblClickText?.(e.data.payload);
2619
+ return;
2620
+ }
2412
2621
  if (e.data.type === "design-hotkey") {
2413
2622
  onIframeHotkey?.({
2414
2623
  key: String(e.data.key || ""),
@@ -2527,9 +2736,11 @@ export function DesignCanvas({
2527
2736
  }, [
2528
2737
  onElementSelect,
2529
2738
  onElementHover,
2739
+ onClearSelection,
2530
2740
  onVisualStyleChange,
2531
2741
  onTextContentChange,
2532
2742
  onTextEditingStateChange,
2743
+ onElementDblClickText,
2533
2744
  onIframeHotkey,
2534
2745
  onIframeContextMenu,
2535
2746
  onVisualStructureChange,
@@ -2605,6 +2816,21 @@ export function DesignCanvas({
2605
2816
  );
2606
2817
  }, [clearSelectionRequest]);
2607
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
+
2608
2834
  const sendStyleChange = useCallback(
2609
2835
  (selector: string, property: string, value: string) => {
2610
2836
  const iframe = iframeRef.current;
@@ -2654,15 +2880,31 @@ export function DesignCanvas({
2654
2880
 
2655
2881
  // Expose iframe runtime mutations for the editor orchestrator.
2656
2882
  useEffect(() => {
2883
+ if (!registerRuntimeBridge) return;
2657
2884
  (window as any).__designCanvasSendStyle = sendStyleChange;
2658
2885
  (window as any).__designCanvasReplaceContent = replacePreviewContent;
2659
2886
  (window as any).__designCanvasDeleteElement = deleteRuntimeElement;
2660
2887
  return () => {
2661
- delete (window as any).__designCanvasSendStyle;
2662
- delete (window as any).__designCanvasReplaceContent;
2663
- 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
+ }
2664
2901
  };
2665
- }, [deleteRuntimeElement, replacePreviewContent, sendStyleChange]);
2902
+ }, [
2903
+ deleteRuntimeElement,
2904
+ registerRuntimeBridge,
2905
+ replacePreviewContent,
2906
+ sendStyleChange,
2907
+ ]);
2666
2908
 
2667
2909
  // Device dimensions match real-world devices. iframes are replaced elements
2668
2910
  // with an intrinsic 300×150 size, so `aspect-ratio` + `height: auto` doesn't
@@ -2679,6 +2921,7 @@ export function DesignCanvas({
2679
2921
 
2680
2922
  const { width: iframeWidth, height: iframeHeight } =
2681
2923
  deviceDimensions[deviceFrame];
2924
+ const embeddedFrameFluid = embeddedFrame?.fluid === true;
2682
2925
 
2683
2926
  // Wrap the iframe in a positioned container so DrawOverlay /
2684
2927
  // CanvasCommentPins can absolutely-position themselves on top of the
@@ -2691,9 +2934,15 @@ export function DesignCanvas({
2691
2934
  <div
2692
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)]"
2693
2936
  style={{
2694
- width: embeddedFrame ? embeddedFrame.viewportWidth : iframeWidth,
2937
+ width: embeddedFrame
2938
+ ? embeddedFrameFluid
2939
+ ? "100%"
2940
+ : embeddedFrame.viewportWidth
2941
+ : iframeWidth,
2695
2942
  height: embeddedFrame
2696
- ? embeddedFrame.viewportHeight
2943
+ ? embeddedFrameFluid
2944
+ ? "100%"
2945
+ : embeddedFrame.viewportHeight
2697
2946
  : deviceFrame === "none"
2698
2947
  ? "100%"
2699
2948
  : (iframeHeight ?? undefined),
@@ -2771,6 +3020,17 @@ export function DesignCanvas({
2771
3020
  );
2772
3021
 
2773
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
+
2774
3034
  const scaleX =
2775
3035
  embeddedFrame.displayWidth / Math.max(1, embeddedFrame.viewportWidth);
2776
3036
  const scaleY =