@agent-native/core 0.88.0 → 0.90.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1866) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +96 -2
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +87 -146
  4. package/corpus/core/docs/content/cloneable-saas.mdx +19 -19
  5. package/corpus/core/docs/content/components.mdx +16 -4
  6. package/corpus/core/docs/content/data-programs.mdx +329 -0
  7. package/corpus/core/docs/content/deployment.mdx +9 -9
  8. package/corpus/core/docs/content/extensions.mdx +8 -7
  9. package/corpus/core/docs/content/generative-ui.mdx +5 -3
  10. package/corpus/core/docs/content/key-concepts.mdx +6 -9
  11. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +100 -70
  12. package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +19 -19
  13. package/corpus/core/docs/content/locales/ar-SA/components.mdx +12 -2
  14. package/corpus/core/docs/content/locales/ar-SA/data-programs.mdx +329 -0
  15. package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +8 -9
  16. package/corpus/core/docs/content/locales/ar-SA/extensions.mdx +6 -4
  17. package/corpus/core/docs/content/locales/ar-SA/key-concepts.mdx +7 -9
  18. package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +9 -10
  19. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +100 -74
  20. package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +19 -19
  21. package/corpus/core/docs/content/locales/de-DE/components.mdx +12 -2
  22. package/corpus/core/docs/content/locales/de-DE/data-programs.mdx +329 -0
  23. package/corpus/core/docs/content/locales/de-DE/deployment.mdx +8 -9
  24. package/corpus/core/docs/content/locales/de-DE/extensions.mdx +6 -4
  25. package/corpus/core/docs/content/locales/de-DE/key-concepts.mdx +8 -10
  26. package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +9 -10
  27. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +100 -74
  28. package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +19 -19
  29. package/corpus/core/docs/content/locales/es-ES/components.mdx +12 -2
  30. package/corpus/core/docs/content/locales/es-ES/data-programs.mdx +329 -0
  31. package/corpus/core/docs/content/locales/es-ES/deployment.mdx +8 -9
  32. package/corpus/core/docs/content/locales/es-ES/extensions.mdx +6 -4
  33. package/corpus/core/docs/content/locales/es-ES/key-concepts.mdx +8 -10
  34. package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +9 -10
  35. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +100 -76
  36. package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +19 -19
  37. package/corpus/core/docs/content/locales/fr-FR/components.mdx +12 -2
  38. package/corpus/core/docs/content/locales/fr-FR/data-programs.mdx +329 -0
  39. package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +8 -9
  40. package/corpus/core/docs/content/locales/fr-FR/extensions.mdx +6 -4
  41. package/corpus/core/docs/content/locales/fr-FR/key-concepts.mdx +8 -10
  42. package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +9 -10
  43. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +100 -71
  44. package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +19 -19
  45. package/corpus/core/docs/content/locales/hi-IN/components.mdx +20 -10
  46. package/corpus/core/docs/content/locales/hi-IN/data-programs.mdx +329 -0
  47. package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +8 -9
  48. package/corpus/core/docs/content/locales/hi-IN/extensions.mdx +6 -4
  49. package/corpus/core/docs/content/locales/hi-IN/key-concepts.mdx +8 -10
  50. package/corpus/core/docs/content/locales/hi-IN/real-time-collaboration.mdx +9 -10
  51. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +100 -64
  52. package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +19 -19
  53. package/corpus/core/docs/content/locales/ja-JP/components.mdx +12 -2
  54. package/corpus/core/docs/content/locales/ja-JP/data-programs.mdx +329 -0
  55. package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +8 -9
  56. package/corpus/core/docs/content/locales/ja-JP/extensions.mdx +6 -4
  57. package/corpus/core/docs/content/locales/ja-JP/key-concepts.mdx +9 -11
  58. package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +9 -10
  59. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +100 -60
  60. package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +19 -19
  61. package/corpus/core/docs/content/locales/ko-KR/components.mdx +20 -10
  62. package/corpus/core/docs/content/locales/ko-KR/data-programs.mdx +329 -0
  63. package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +8 -9
  64. package/corpus/core/docs/content/locales/ko-KR/extensions.mdx +16 -14
  65. package/corpus/core/docs/content/locales/ko-KR/key-concepts.mdx +9 -11
  66. package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +9 -10
  67. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +100 -74
  68. package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +19 -19
  69. package/corpus/core/docs/content/locales/pt-BR/components.mdx +12 -2
  70. package/corpus/core/docs/content/locales/pt-BR/data-programs.mdx +329 -0
  71. package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +8 -9
  72. package/corpus/core/docs/content/locales/pt-BR/extensions.mdx +6 -4
  73. package/corpus/core/docs/content/locales/pt-BR/key-concepts.mdx +8 -11
  74. package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +9 -10
  75. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +100 -58
  76. package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +19 -19
  77. package/corpus/core/docs/content/locales/zh-CN/components.mdx +20 -10
  78. package/corpus/core/docs/content/locales/zh-CN/data-programs.mdx +329 -0
  79. package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +7 -9
  80. package/corpus/core/docs/content/locales/zh-CN/extensions.mdx +16 -14
  81. package/corpus/core/docs/content/locales/zh-CN/key-concepts.mdx +7 -11
  82. package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +10 -11
  83. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +100 -58
  84. package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +19 -19
  85. package/corpus/core/docs/content/locales/zh-TW/components.mdx +20 -10
  86. package/corpus/core/docs/content/locales/zh-TW/data-programs.mdx +329 -0
  87. package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +7 -9
  88. package/corpus/core/docs/content/locales/zh-TW/extensions.mdx +16 -14
  89. package/corpus/core/docs/content/locales/zh-TW/key-concepts.mdx +7 -11
  90. package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +10 -11
  91. package/corpus/core/docs/content/real-time-collaboration.mdx +10 -11
  92. package/corpus/core/docs/content/toolkit-app-adapters.mdx +46 -0
  93. package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +48 -0
  94. package/corpus/core/docs/content/toolkit-sharing-ui.mdx +33 -0
  95. package/corpus/core/docs/content/toolkit-shell-hooks.mdx +27 -0
  96. package/corpus/core/docs/content/toolkit-ui.mdx +69 -0
  97. package/corpus/core/package.json +5 -1
  98. package/corpus/core/src/agent/durable-background.ts +53 -0
  99. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +7 -1
  100. package/corpus/core/src/agent/engine/anthropic-engine.ts +29 -1
  101. package/corpus/core/src/agent/engine/builder-engine.ts +1 -0
  102. package/corpus/core/src/agent/engine/output-tokens.ts +26 -13
  103. package/corpus/core/src/agent/engine/registry.ts +45 -26
  104. package/corpus/core/src/agent/engine/translate-ai-sdk.ts +56 -7
  105. package/corpus/core/src/agent/engine/translate-anthropic.ts +43 -1
  106. package/corpus/core/src/agent/engine/types.ts +16 -0
  107. package/corpus/core/src/agent/model-config.ts +100 -0
  108. package/corpus/core/src/agent/production-agent.ts +492 -241
  109. package/corpus/core/src/agent/runtime-context.ts +50 -12
  110. package/corpus/core/src/agent/tool-result-images.ts +178 -0
  111. package/corpus/core/src/brand-kit/fig/index.ts +8 -4
  112. package/corpus/core/src/client/AgentPanel.tsx +89 -32
  113. package/corpus/core/src/client/AssistantChat.tsx +95 -46
  114. package/corpus/core/src/client/KeepTabOpenNotice.tsx +176 -0
  115. package/corpus/core/src/client/MultiTabAssistantChat.tsx +7 -0
  116. package/corpus/core/src/client/RunStuckBanner.tsx +13 -1
  117. package/corpus/core/src/client/chat/run-recovery.tsx +7 -1
  118. package/corpus/core/src/client/clipboard.ts +17 -13
  119. package/corpus/core/src/client/components/AgentPresenceChip.tsx +4 -69
  120. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +5 -240
  121. package/corpus/core/src/client/components/PresenceBar.tsx +4 -346
  122. package/corpus/core/src/client/components/RecentEditHighlights.tsx +4 -219
  123. package/corpus/core/src/client/components/RemoteSelectionRings.tsx +5 -220
  124. package/corpus/core/src/client/composer/TiptapComposer.tsx +94 -38
  125. package/corpus/core/src/client/db-admin/SqlEditor.tsx +13 -3
  126. package/corpus/core/src/client/dynamic-suggestions.ts +6 -1
  127. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +0 -14
  128. package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +29 -30
  129. package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +2 -1
  130. package/corpus/core/src/client/host-bridge.ts +29 -8
  131. package/corpus/core/src/client/i18n.tsx +7 -1
  132. package/corpus/core/src/client/index.ts +5 -0
  133. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +34 -69
  134. package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +4 -0
  135. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  136. package/corpus/core/src/client/settings/SettingsPanel.tsx +409 -230
  137. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +5 -7
  138. package/corpus/core/src/client/settings/index.ts +5 -1
  139. package/corpus/core/src/client/sharing/VisibilityBadge.tsx +4 -37
  140. package/corpus/core/src/client/use-action.ts +39 -2
  141. package/corpus/core/src/client/use-db-sync.ts +44 -18
  142. package/corpus/core/src/client/use-run-stuck-detection.ts +10 -1
  143. package/corpus/core/src/coding-tools/run-code.ts +473 -74
  144. package/corpus/core/src/coding-tools/sandbox/background.ts +406 -0
  145. package/corpus/core/src/coding-tools/sandbox/executions-store.ts +517 -0
  146. package/corpus/core/src/coding-tools/sandbox/index.ts +53 -13
  147. package/corpus/core/src/coding-tools/sandbox/local-child-process-adapter.ts +61 -4
  148. package/corpus/core/src/collab/client.ts +870 -562
  149. package/corpus/core/src/collab/presence.ts +12 -23
  150. package/corpus/core/src/collab/recent-edits.ts +13 -32
  151. package/corpus/core/src/data-programs/actions.ts +335 -0
  152. package/corpus/core/src/data-programs/contract.ts +284 -0
  153. package/corpus/core/src/data-programs/execute.ts +693 -0
  154. package/corpus/core/src/data-programs/index.ts +72 -0
  155. package/corpus/core/src/data-programs/schema.ts +148 -0
  156. package/corpus/core/src/data-programs/store.ts +755 -0
  157. package/corpus/core/src/db/create-get-db.ts +32 -2
  158. package/corpus/core/src/deploy/build.ts +34 -0
  159. package/corpus/core/src/extensions/actions.ts +2 -2
  160. package/corpus/core/src/file-upload/builder.ts +11 -0
  161. package/corpus/core/src/integrations/webhook-handler.ts +27 -8
  162. package/corpus/core/src/local-artifacts/index.ts +1 -1
  163. package/corpus/core/src/localization/default-messages.ts +1 -2
  164. package/corpus/core/src/mcp-client/index.ts +29 -0
  165. package/corpus/core/src/onboarding/default-steps.ts +5 -2
  166. package/corpus/core/src/resources/store.ts +34 -3
  167. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +4 -8
  168. package/corpus/core/src/scripts/agent-engines/test-agent-engine.ts +9 -2
  169. package/corpus/core/src/scripts/db/tool-mode.ts +2 -2
  170. package/corpus/core/src/scripts/resources/migrate-learnings.ts +9 -13
  171. package/corpus/core/src/server/agent-chat-plugin.ts +154 -62
  172. package/corpus/core/src/server/agent-discovery.ts +67 -7
  173. package/corpus/core/src/server/auth.ts +90 -3
  174. package/corpus/core/src/server/builder-design-systems.ts +2 -2
  175. package/corpus/core/src/server/core-routes-plugin.ts +119 -22
  176. package/corpus/core/src/server/credential-provider.ts +28 -8
  177. package/corpus/core/src/server/csrf.ts +3 -0
  178. package/corpus/core/src/server/framework-request-handler.ts +52 -4
  179. package/corpus/core/src/server/oauth-helpers.ts +11 -1
  180. package/corpus/core/src/server/onboarding-html.ts +80 -48
  181. package/corpus/core/src/server/prompts/shared-rules.ts +2 -2
  182. package/corpus/core/src/server/transcribe-voice.ts +93 -9
  183. package/corpus/core/src/styles/agent-native.css +57 -45
  184. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +55 -0
  185. package/corpus/core/src/templates/default/AGENTS.md +11 -14
  186. package/corpus/core/src/templates/default/actions/navigate.ts +1 -1
  187. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +0 -1
  188. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +0 -1
  189. package/corpus/core/src/templates/default/app/i18n/en-US.ts +0 -1
  190. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +0 -1
  191. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +0 -1
  192. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +0 -1
  193. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +0 -1
  194. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +0 -1
  195. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +0 -1
  196. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +0 -1
  197. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +0 -1
  198. package/corpus/core/src/templates/default/app/routes/settings.tsx +22 -33
  199. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
  200. package/corpus/core/src/templates/headless/AGENTS.md +9 -6
  201. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
  202. package/corpus/core/src/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
  203. package/corpus/core/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
  204. package/corpus/core/src/vite/agents-bundle-plugin.ts +26 -1
  205. package/corpus/core/src/vite/client.ts +12 -0
  206. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +55 -0
  207. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +8 -0
  208. package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +348 -0
  209. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +10 -0
  210. package/corpus/templates/analytics/AGENTS.md +31 -32
  211. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
  212. package/corpus/templates/analytics/actions/delete-data-source-credentials.ts +32 -0
  213. package/corpus/templates/analytics/actions/ensure-risk-meeting-dashboard.ts +247 -0
  214. package/corpus/templates/analytics/actions/update-data-source-credentials.ts +123 -0
  215. package/corpus/templates/analytics/app/components/InlineMetric.tsx +2 -2
  216. package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +2 -7
  217. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +11 -16
  218. package/corpus/templates/analytics/app/components/dashboard/DateRangePicker.tsx +1 -2
  219. package/corpus/templates/analytics/app/components/dashboard/MetricCard.tsx +2 -7
  220. package/corpus/templates/analytics/app/components/dashboard/RecentActivity.tsx +3 -6
  221. package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +1 -6
  222. package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +2 -7
  223. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +11 -15
  224. package/corpus/templates/analytics/app/components/dashboard/StatsCard.tsx +1 -7
  225. package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +2 -7
  226. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +16 -16
  227. package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -2
  228. package/corpus/templates/analytics/app/components/layout/HeaderActions.tsx +2 -1
  229. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -2
  230. package/corpus/templates/analytics/app/components/layout/MobileNav.tsx +33 -17
  231. package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +6 -6
  232. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +4 -3
  233. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +119 -68
  234. package/corpus/templates/analytics/app/components/ui/alert-dialog.tsx +1 -0
  235. package/corpus/templates/analytics/app/components/ui/alert.tsx +1 -0
  236. package/corpus/templates/analytics/app/components/ui/avatar.tsx +1 -0
  237. package/corpus/templates/analytics/app/components/ui/badge.tsx +1 -0
  238. package/corpus/templates/analytics/app/components/ui/button.tsx +1 -0
  239. package/corpus/templates/analytics/app/components/ui/calendar.tsx +1 -1
  240. package/corpus/templates/analytics/app/components/ui/card.tsx +1 -0
  241. package/corpus/templates/analytics/app/components/ui/checkbox.tsx +1 -0
  242. package/corpus/templates/analytics/app/components/ui/collapsible.tsx +1 -0
  243. package/corpus/templates/analytics/app/components/ui/command.tsx +1 -0
  244. package/corpus/templates/analytics/app/components/ui/date-picker.tsx +1 -0
  245. package/corpus/templates/analytics/app/components/ui/dialog.tsx +1 -0
  246. package/corpus/templates/analytics/app/components/ui/dropdown-menu.tsx +1 -0
  247. package/corpus/templates/analytics/app/components/ui/input.tsx +1 -0
  248. package/corpus/templates/analytics/app/components/ui/label.tsx +1 -0
  249. package/corpus/templates/analytics/app/components/ui/popover.tsx +1 -0
  250. package/corpus/templates/analytics/app/components/ui/progress.tsx +1 -0
  251. package/corpus/templates/analytics/app/components/ui/select.tsx +1 -0
  252. package/corpus/templates/analytics/app/components/ui/sheet.tsx +1 -0
  253. package/corpus/templates/analytics/app/components/ui/skeleton.tsx +1 -0
  254. package/corpus/templates/analytics/app/components/ui/sonner.tsx +1 -0
  255. package/corpus/templates/analytics/app/components/ui/spinner.tsx +1 -0
  256. package/corpus/templates/analytics/app/components/ui/switch.tsx +1 -0
  257. package/corpus/templates/analytics/app/components/ui/table.tsx +1 -0
  258. package/corpus/templates/analytics/app/components/ui/tabs.tsx +1 -0
  259. package/corpus/templates/analytics/app/components/ui/textarea.tsx +1 -0
  260. package/corpus/templates/analytics/app/components/ui/toaster.tsx +1 -0
  261. package/corpus/templates/analytics/app/components/ui/toggle-group.tsx +1 -0
  262. package/corpus/templates/analytics/app/components/ui/toolkit-provider.tsx +12 -0
  263. package/corpus/templates/analytics/app/components/ui/tooltip.tsx +1 -0
  264. package/corpus/templates/analytics/app/global.css +26 -3
  265. package/corpus/templates/analytics/app/i18n/zh-TW.ts +27 -4
  266. package/corpus/templates/analytics/app/i18n-data.ts +277 -42
  267. package/corpus/templates/analytics/app/pages/Agents.tsx +19 -19
  268. package/corpus/templates/analytics/app/pages/DataDictionary.tsx +21 -21
  269. package/corpus/templates/analytics/app/pages/DataSources.tsx +31 -54
  270. package/corpus/templates/analytics/app/pages/NotFound.tsx +2 -1
  271. package/corpus/templates/analytics/app/pages/Placeholder.tsx +2 -1
  272. package/corpus/templates/analytics/app/pages/Settings.tsx +19 -37
  273. package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +3 -2
  274. package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +8 -13
  275. package/corpus/templates/analytics/app/pages/adhoc/_shared/components/DateRangeInput.tsx +1 -2
  276. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ChartTypePicker.tsx +2 -4
  277. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/DateRangePicker.tsx +1 -2
  278. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventCombobox.tsx +7 -7
  279. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventPanel.tsx +2 -1
  280. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventRow.tsx +2 -1
  281. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ExplorerChart.tsx +3 -8
  282. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/FilterBuilder.tsx +5 -4
  283. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/GroupByPicker.tsx +2 -1
  284. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +7 -7
  285. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +7 -7
  286. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +1 -1
  287. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +16 -16
  288. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +16 -21
  289. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +32 -32
  290. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +14 -14
  291. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +14 -13
  292. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +200 -52
  293. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +23 -28
  294. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +13 -12
  295. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +15 -15
  296. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +32 -36
  297. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -1
  298. package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +8 -7
  299. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +19 -19
  300. package/corpus/templates/analytics/app/pages/analyses/LegacyFusionAnalysis.tsx +11 -15
  301. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +14 -14
  302. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +7 -12
  303. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +12 -12
  304. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +25 -25
  305. package/corpus/templates/analytics/app/root.tsx +22 -19
  306. package/corpus/templates/analytics/app/routes/catalog.tsx +8 -8
  307. package/corpus/templates/analytics/changelog/2026-07-06-dashboard-lists-and-headers-stay-compact.md +6 -0
  308. package/corpus/templates/analytics/changelog/2026-07-06-dashboards-can-now-chart-data-programs-saved-server-side-scr.md +6 -0
  309. package/corpus/templates/analytics/changelog/2026-07-06-extension-pages-and-mobile-headers-now-share-one-background-shade.md +6 -0
  310. package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +5 -0
  311. package/corpus/templates/analytics/learnings.defaults.md +36 -0
  312. package/corpus/templates/analytics/seeds/data-programs/risk-meeting-cohort.js +254 -0
  313. package/corpus/templates/analytics/seeds/data-programs/risk-meeting-pylon-early-warning.js +184 -0
  314. package/corpus/templates/analytics/server/handlers/sql-query.ts +1 -1
  315. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +109 -1
  316. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +55 -0
  317. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
  318. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
  319. package/corpus/templates/assets/AGENTS.md +23 -4
  320. package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
  321. package/corpus/templates/assets/actions/_helpers.ts +8 -1
  322. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
  323. package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
  324. package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
  325. package/corpus/templates/assets/actions/generate-image.ts +342 -25
  326. package/corpus/templates/assets/actions/get-library.ts +4 -3
  327. package/corpus/templates/assets/actions/navigate.ts +2 -1
  328. package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
  329. package/corpus/templates/assets/actions/update-asset.ts +1 -0
  330. package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
  331. package/corpus/templates/assets/actions/view-screen.ts +8 -1
  332. package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +3 -2
  333. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +25 -11
  334. package/corpus/templates/assets/app/components/layout/Header.tsx +1 -1
  335. package/corpus/templates/assets/app/components/layout/PageShell.tsx +4 -4
  336. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +12 -12
  337. package/corpus/templates/assets/app/components/library/CreateLibraryDialog.tsx +8 -8
  338. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +7 -7
  339. package/corpus/templates/assets/app/components/library/LibraryCard.tsx +8 -8
  340. package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +3 -2
  341. package/corpus/templates/assets/app/components/ui/alert-dialog.tsx +1 -0
  342. package/corpus/templates/assets/app/components/ui/badge.tsx +1 -0
  343. package/corpus/templates/assets/app/components/ui/button.tsx +1 -0
  344. package/corpus/templates/assets/app/components/ui/card.tsx +1 -0
  345. package/corpus/templates/assets/app/components/ui/checkbox.tsx +1 -0
  346. package/corpus/templates/assets/app/components/ui/dialog.tsx +1 -0
  347. package/corpus/templates/assets/app/components/ui/dropdown-menu.tsx +1 -0
  348. package/corpus/templates/assets/app/components/ui/input.tsx +1 -0
  349. package/corpus/templates/assets/app/components/ui/label.tsx +1 -0
  350. package/corpus/templates/assets/app/components/ui/popover.tsx +1 -0
  351. package/corpus/templates/assets/app/components/ui/select.tsx +1 -0
  352. package/corpus/templates/assets/app/components/ui/separator.tsx +1 -0
  353. package/corpus/templates/assets/app/components/ui/sheet.tsx +1 -0
  354. package/corpus/templates/assets/app/components/ui/skeleton.tsx +1 -0
  355. package/corpus/templates/assets/app/components/ui/sonner.tsx +1 -0
  356. package/corpus/templates/assets/app/components/ui/spinner.tsx +1 -0
  357. package/corpus/templates/assets/app/components/ui/tabs.tsx +1 -0
  358. package/corpus/templates/assets/app/components/ui/toolkit-provider.tsx +12 -0
  359. package/corpus/templates/assets/app/components/ui/tooltip.tsx +1 -0
  360. package/corpus/templates/assets/app/global.css +1 -0
  361. package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
  362. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
  363. package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
  364. package/corpus/templates/assets/app/i18n-data.ts +650 -0
  365. package/corpus/templates/assets/app/root.tsx +12 -9
  366. package/corpus/templates/assets/app/routes/asset.$id.tsx +21 -20
  367. package/corpus/templates/assets/app/routes/audit.tsx +15 -14
  368. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +93 -87
  369. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
  370. package/corpus/templates/assets/app/routes/library.tsx +74 -32
  371. package/corpus/templates/assets/app/routes/settings.tsx +21 -35
  372. package/corpus/templates/assets/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
  373. package/corpus/templates/assets/server/lib/generation.ts +366 -44
  374. package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
  375. package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
  376. package/corpus/templates/assets/shared/api.ts +47 -0
  377. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +55 -0
  378. package/corpus/templates/brain/.agents/skills/ask-across-everything/SKILL.md +6 -1
  379. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +63 -5
  380. package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +112 -0
  381. package/corpus/templates/brain/AGENTS.md +93 -8
  382. package/corpus/templates/brain/app/components/brain/CanonicalPreviewSheet.tsx +6 -5
  383. package/corpus/templates/brain/app/components/brain/Surface.tsx +3 -8
  384. package/corpus/templates/brain/app/components/layout/Layout.tsx +7 -7
  385. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +6 -6
  386. package/corpus/templates/brain/app/components/ui/badge.tsx +1 -0
  387. package/corpus/templates/brain/app/components/ui/button.tsx +1 -0
  388. package/corpus/templates/brain/app/components/ui/card.tsx +1 -0
  389. package/corpus/templates/brain/app/components/ui/input.tsx +1 -0
  390. package/corpus/templates/brain/app/components/ui/label.tsx +1 -0
  391. package/corpus/templates/brain/app/components/ui/progress.tsx +1 -0
  392. package/corpus/templates/brain/app/components/ui/select.tsx +1 -0
  393. package/corpus/templates/brain/app/components/ui/separator.tsx +1 -0
  394. package/corpus/templates/brain/app/components/ui/sheet.tsx +1 -0
  395. package/corpus/templates/brain/app/components/ui/skeleton.tsx +1 -0
  396. package/corpus/templates/brain/app/components/ui/switch.tsx +1 -0
  397. package/corpus/templates/brain/app/components/ui/table.tsx +1 -0
  398. package/corpus/templates/brain/app/components/ui/textarea.tsx +1 -0
  399. package/corpus/templates/brain/app/components/ui/toolkit-provider.tsx +12 -0
  400. package/corpus/templates/brain/app/components/ui/tooltip.tsx +1 -0
  401. package/corpus/templates/brain/app/global.css +1 -0
  402. package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +9 -4
  403. package/corpus/templates/brain/app/root.tsx +11 -8
  404. package/corpus/templates/brain/app/routes/knowledge.tsx +19 -19
  405. package/corpus/templates/brain/app/routes/ops.tsx +18 -18
  406. package/corpus/templates/brain/app/routes/review.tsx +22 -27
  407. package/corpus/templates/brain/app/routes/search.tsx +20 -20
  408. package/corpus/templates/brain/app/routes/settings.tsx +25 -40
  409. package/corpus/templates/brain/app/routes/sources.tsx +23 -23
  410. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +55 -0
  411. package/corpus/templates/calendar/actions/find-a-time.ts +29 -16
  412. package/corpus/templates/calendar/actions/list-bookings.ts +33 -2
  413. package/corpus/templates/calendar/actions/list-events.ts +34 -8
  414. package/corpus/templates/calendar/actions/update-calendar-visual-preferences.ts +105 -34
  415. package/corpus/templates/calendar/app/components/ThemeToggle.tsx +5 -5
  416. package/corpus/templates/calendar/app/components/TimezoneCombobox.tsx +6 -6
  417. package/corpus/templates/calendar/app/components/booking/BookingConfirmation.tsx +2 -1
  418. package/corpus/templates/calendar/app/components/booking/BookingForm.tsx +9 -8
  419. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +2 -2
  420. package/corpus/templates/calendar/app/components/booking/TimeSlotPicker.tsx +3 -2
  421. package/corpus/templates/calendar/app/components/calendar/AddCalendarDialog.tsx +10 -15
  422. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +6 -6
  423. package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +73 -3
  424. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +27 -27
  425. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +553 -267
  426. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +9 -12
  427. package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +10 -13
  428. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +7 -7
  429. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +21 -21
  430. package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +11 -11
  431. package/corpus/templates/calendar/app/components/calendar/EventOptionControls.tsx +9 -9
  432. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +9 -9
  433. package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +6 -6
  434. package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +3 -3
  435. package/corpus/templates/calendar/app/components/calendar/GoogleSyncButton.tsx +1 -1
  436. package/corpus/templates/calendar/app/components/calendar/GuestNotificationDialog.tsx +8 -10
  437. package/corpus/templates/calendar/app/components/calendar/IntegrationsSidebar.tsx +12 -12
  438. package/corpus/templates/calendar/app/components/calendar/KeyboardShortcutsHelp.tsx +2 -2
  439. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +93 -41
  440. package/corpus/templates/calendar/app/components/calendar/PeopleSearchDialog.tsx +6 -6
  441. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +530 -299
  442. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +229 -134
  443. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +135 -108
  444. package/corpus/templates/calendar/app/components/ui/alert-dialog.tsx +1 -0
  445. package/corpus/templates/calendar/app/components/ui/badge.tsx +1 -0
  446. package/corpus/templates/calendar/app/components/ui/button.tsx +1 -0
  447. package/corpus/templates/calendar/app/components/ui/card.tsx +1 -0
  448. package/corpus/templates/calendar/app/components/ui/checkbox.tsx +1 -0
  449. package/corpus/templates/calendar/app/components/ui/collapsible.tsx +1 -0
  450. package/corpus/templates/calendar/app/components/ui/command.tsx +1 -0
  451. package/corpus/templates/calendar/app/components/ui/dialog.tsx +1 -0
  452. package/corpus/templates/calendar/app/components/ui/dropdown-menu.tsx +1 -0
  453. package/corpus/templates/calendar/app/components/ui/input.tsx +1 -0
  454. package/corpus/templates/calendar/app/components/ui/label.tsx +1 -0
  455. package/corpus/templates/calendar/app/components/ui/popover.tsx +1 -0
  456. package/corpus/templates/calendar/app/components/ui/radio-group.tsx +1 -0
  457. package/corpus/templates/calendar/app/components/ui/select.tsx +1 -0
  458. package/corpus/templates/calendar/app/components/ui/separator.tsx +1 -0
  459. package/corpus/templates/calendar/app/components/ui/skeleton.tsx +1 -0
  460. package/corpus/templates/calendar/app/components/ui/spinner.tsx +1 -0
  461. package/corpus/templates/calendar/app/components/ui/switch.tsx +1 -0
  462. package/corpus/templates/calendar/app/components/ui/table.tsx +1 -0
  463. package/corpus/templates/calendar/app/components/ui/tabs.tsx +1 -0
  464. package/corpus/templates/calendar/app/components/ui/textarea.tsx +1 -0
  465. package/corpus/templates/calendar/app/components/ui/toolkit-provider.tsx +12 -0
  466. package/corpus/templates/calendar/app/components/ui/tooltip.tsx +1 -0
  467. package/corpus/templates/calendar/app/global.css +1 -0
  468. package/corpus/templates/calendar/app/hooks/use-event-drag.ts +63 -9
  469. package/corpus/templates/calendar/app/hooks/use-google-auth.ts +15 -27
  470. package/corpus/templates/calendar/app/hooks/use-grid-create-drag.ts +254 -0
  471. package/corpus/templates/calendar/app/hooks/use-mobile.ts +1 -0
  472. package/corpus/templates/calendar/app/hooks/use-view-preferences.ts +287 -21
  473. package/corpus/templates/calendar/app/hooks/use-zoom-auth.ts +56 -19
  474. package/corpus/templates/calendar/app/i18n/zh-TW.ts +3 -0
  475. package/corpus/templates/calendar/app/i18n-data.ts +31 -0
  476. package/corpus/templates/calendar/app/lib/event-colors.ts +29 -8
  477. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +11 -11
  478. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +45 -50
  479. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -2
  480. package/corpus/templates/calendar/app/pages/BookingsList.tsx +12 -12
  481. package/corpus/templates/calendar/app/pages/CalendarView.tsx +438 -271
  482. package/corpus/templates/calendar/app/pages/ManageBookingPage.tsx +13 -13
  483. package/corpus/templates/calendar/app/pages/Settings.tsx +15 -29
  484. package/corpus/templates/calendar/app/root.tsx +19 -15
  485. package/corpus/templates/calendar/app/routes/event.tsx +2 -2
  486. package/corpus/templates/calendar/changelog/2026-07-06-calendar-now-loads-view-preferences-much-more-efficiently.md +6 -0
  487. package/corpus/templates/calendar/changelog/2026-07-06-connected-calendars-keep-their-own-color-when-you-customize-.md +6 -0
  488. package/corpus/templates/calendar/changelog/2026-07-06-create-events-from-the-command-menu-with-natural-phrases-lik.md +6 -0
  489. package/corpus/templates/calendar/changelog/2026-07-06-drag-across-empty-week-or-day-slots-to-create-an-event-with-.md +6 -0
  490. package/corpus/templates/calendar/changelog/2026-07-06-dragging-and-resizing-events-is-much-smoother-especially-on-.md +6 -0
  491. package/corpus/templates/calendar/changelog/2026-07-06-each-connected-google-account-can-now-have-its-own-calendar-.md +6 -0
  492. package/corpus/templates/calendar/changelog/2026-07-06-events-spanning-multiple-days-now-show-on-every-day-they-cov.md +6 -0
  493. package/corpus/templates/calendar/changelog/2026-07-06-google-calendar-now-finishes-connecting-correctly-after-sign.md +6 -0
  494. package/corpus/templates/calendar/changelog/2026-07-06-public-booking-pages-respond-faster.md +6 -0
  495. package/corpus/templates/calendar/changelog/2026-07-06-the-app-header-no-longer-shows-the-global-notifications-bell.md +6 -0
  496. package/corpus/templates/calendar/package.json +1 -0
  497. package/corpus/templates/calendar/server/handlers/bookings.ts +46 -52
  498. package/corpus/templates/calendar/server/handlers/google-auth.ts +21 -5
  499. package/corpus/templates/calendar/server/lib/google-calendar.ts +32 -3
  500. package/corpus/templates/calendar/shared/calendar-view-preferences.ts +66 -2
  501. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +55 -0
  502. package/corpus/templates/chat/app/components/layout/Layout.tsx +6 -6
  503. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +12 -12
  504. package/corpus/templates/chat/app/components/ui/button.tsx +1 -0
  505. package/corpus/templates/chat/app/components/ui/card.tsx +1 -0
  506. package/corpus/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
  507. package/corpus/templates/chat/app/components/ui/input.tsx +1 -0
  508. package/corpus/templates/chat/app/components/ui/label.tsx +1 -0
  509. package/corpus/templates/chat/app/components/ui/sheet.tsx +1 -0
  510. package/corpus/templates/chat/app/components/ui/toolkit-provider.tsx +12 -0
  511. package/corpus/templates/chat/app/components/ui/tooltip.tsx +1 -0
  512. package/corpus/templates/chat/app/global.css +1 -0
  513. package/corpus/templates/chat/app/root.tsx +7 -4
  514. package/corpus/templates/chat/app/routes/settings.tsx +6 -21
  515. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +55 -0
  516. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +18 -4
  517. package/corpus/templates/clips/.env.example +8 -0
  518. package/corpus/templates/clips/actions/accept-invite.ts +3 -0
  519. package/corpus/templates/clips/actions/add-recording-to-space.ts +55 -27
  520. package/corpus/templates/clips/actions/add-vocabulary-term.ts +3 -1
  521. package/corpus/templates/clips/actions/archive-recording.ts +5 -12
  522. package/corpus/templates/clips/actions/cleanup-transcript.ts +5 -4
  523. package/corpus/templates/clips/actions/clear-edits.ts +42 -29
  524. package/corpus/templates/clips/actions/create-meeting.ts +87 -40
  525. package/corpus/templates/clips/actions/create-recording.ts +6 -3
  526. package/corpus/templates/clips/actions/delete-meeting.ts +25 -0
  527. package/corpus/templates/clips/actions/delete-recording-permanent.ts +3 -0
  528. package/corpus/templates/clips/actions/disconnect-calendar.ts +10 -4
  529. package/corpus/templates/clips/actions/export-insights-csv.ts +28 -8
  530. package/corpus/templates/clips/actions/finalize-meeting.ts +106 -33
  531. package/corpus/templates/clips/actions/finalize-recording.ts +201 -3
  532. package/corpus/templates/clips/actions/get-organization-insights.ts +51 -7
  533. package/corpus/templates/clips/actions/import-loom-recording.ts +17 -0
  534. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +18 -1
  535. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +7 -1
  536. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +16 -0
  537. package/corpus/templates/clips/actions/list-clip-views.ts +60 -0
  538. package/corpus/templates/clips/actions/regenerate-summary.ts +10 -0
  539. package/corpus/templates/clips/actions/remove-vocabulary-term.ts +59 -0
  540. package/corpus/templates/clips/actions/request-transcript.ts +170 -25
  541. package/corpus/templates/clips/actions/restore-recording.ts +5 -12
  542. package/corpus/templates/clips/actions/save-browser-transcript.ts +13 -2
  543. package/corpus/templates/clips/actions/set-chapters.ts +6 -15
  544. package/corpus/templates/clips/actions/set-thumbnail.ts +77 -36
  545. package/corpus/templates/clips/actions/split-recording.ts +49 -35
  546. package/corpus/templates/clips/actions/stitch-recordings.ts +7 -4
  547. package/corpus/templates/clips/actions/stop-meeting-recording.ts +15 -1
  548. package/corpus/templates/clips/actions/sync-calendars.ts +10 -108
  549. package/corpus/templates/clips/actions/trash-recording.ts +43 -14
  550. package/corpus/templates/clips/actions/trim-recording.ts +48 -27
  551. package/corpus/templates/clips/actions/undo-edit.ts +49 -37
  552. package/corpus/templates/clips/app/components/capture-install-options.tsx +6 -6
  553. package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +208 -0
  554. package/corpus/templates/clips/app/components/editable-recording-title.tsx +2 -2
  555. package/corpus/templates/clips/app/components/editor/chapters-editor.tsx +7 -7
  556. package/corpus/templates/clips/app/components/editor/editor-toolbar.tsx +26 -26
  557. package/corpus/templates/clips/app/components/editor/split-button.tsx +5 -4
  558. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +9 -9
  559. package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +11 -16
  560. package/corpus/templates/clips/app/components/editor/timeline.tsx +3 -3
  561. package/corpus/templates/clips/app/components/editor/transcript-editor.tsx +5 -5
  562. package/corpus/templates/clips/app/components/library/bulk-action-toolbar.tsx +4 -4
  563. package/corpus/templates/clips/app/components/library/create-space-dialog.tsx +8 -8
  564. package/corpus/templates/clips/app/components/library/empty-state.tsx +2 -1
  565. package/corpus/templates/clips/app/components/library/folder-tree.tsx +14 -14
  566. package/corpus/templates/clips/app/components/library/library-grid.tsx +24 -98
  567. package/corpus/templates/clips/app/components/library/library-layout.tsx +25 -21
  568. package/corpus/templates/clips/app/components/library/organization-switcher.tsx +11 -11
  569. package/corpus/templates/clips/app/components/library/recording-card.tsx +44 -50
  570. package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -5
  571. package/corpus/templates/clips/app/components/library/sort-menu.tsx +5 -4
  572. package/corpus/templates/clips/app/components/library/tag-input.tsx +4 -4
  573. package/corpus/templates/clips/app/components/meetings/attendee-stack.tsx +2 -6
  574. package/corpus/templates/clips/app/components/meetings/auto-record-prompt.tsx +1 -1
  575. package/corpus/templates/clips/app/components/meetings/bullet-link.tsx +4 -4
  576. package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +5 -5
  577. package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +3 -3
  578. package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +17 -9
  579. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +6 -11
  580. package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +215 -21
  581. package/corpus/templates/clips/app/components/player/access-password-prompt.tsx +3 -2
  582. package/corpus/templates/clips/app/components/player/comments-panel.tsx +14 -14
  583. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +7 -6
  584. package/corpus/templates/clips/app/components/player/insights-panel.tsx +18 -6
  585. package/corpus/templates/clips/app/components/player/player-controls.tsx +42 -36
  586. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +4 -4
  587. package/corpus/templates/clips/app/components/player/settings-panel.tsx +13 -12
  588. package/corpus/templates/clips/app/components/player/share-dialog.tsx +80 -49
  589. package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +3 -2
  590. package/corpus/templates/clips/app/components/player/timestamped-comment-button.tsx +2 -2
  591. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +7 -7
  592. package/corpus/templates/clips/app/components/player/video-player.tsx +182 -15
  593. package/corpus/templates/clips/app/components/recorder/camera-visualizer.tsx +1 -1
  594. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +1 -1
  595. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +49 -36
  596. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +399 -57
  597. package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +6 -5
  598. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +7 -6
  599. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +10 -10
  600. package/corpus/templates/clips/app/components/sharing/viewed-by-popover.tsx +132 -0
  601. package/corpus/templates/clips/app/components/ui/alert-dialog.tsx +1 -0
  602. package/corpus/templates/clips/app/components/ui/avatar.tsx +1 -0
  603. package/corpus/templates/clips/app/components/ui/badge.tsx +1 -0
  604. package/corpus/templates/clips/app/components/ui/button.tsx +1 -0
  605. package/corpus/templates/clips/app/components/ui/card.tsx +1 -0
  606. package/corpus/templates/clips/app/components/ui/checkbox.tsx +1 -0
  607. package/corpus/templates/clips/app/components/ui/collapsible.tsx +1 -0
  608. package/corpus/templates/clips/app/components/ui/context-menu.tsx +1 -0
  609. package/corpus/templates/clips/app/components/ui/dialog.tsx +1 -0
  610. package/corpus/templates/clips/app/components/ui/dropdown-menu.tsx +1 -0
  611. package/corpus/templates/clips/app/components/ui/input.tsx +1 -0
  612. package/corpus/templates/clips/app/components/ui/label.tsx +1 -0
  613. package/corpus/templates/clips/app/components/ui/popover.tsx +1 -0
  614. package/corpus/templates/clips/app/components/ui/scroll-area.tsx +1 -0
  615. package/corpus/templates/clips/app/components/ui/select.tsx +1 -0
  616. package/corpus/templates/clips/app/components/ui/separator.tsx +1 -0
  617. package/corpus/templates/clips/app/components/ui/sheet.tsx +1 -0
  618. package/corpus/templates/clips/app/components/ui/skeleton.tsx +1 -0
  619. package/corpus/templates/clips/app/components/ui/slider.tsx +1 -0
  620. package/corpus/templates/clips/app/components/ui/sonner.tsx +1 -0
  621. package/corpus/templates/clips/app/components/ui/spinner.tsx +1 -0
  622. package/corpus/templates/clips/app/components/ui/switch.tsx +1 -0
  623. package/corpus/templates/clips/app/components/ui/table.tsx +1 -0
  624. package/corpus/templates/clips/app/components/ui/tabs.tsx +1 -0
  625. package/corpus/templates/clips/app/components/ui/textarea.tsx +1 -0
  626. package/corpus/templates/clips/app/components/ui/toolkit-provider.tsx +12 -0
  627. package/corpus/templates/clips/app/components/ui/tooltip.tsx +1 -0
  628. package/corpus/templates/clips/app/components/workspace/branding-editor.tsx +5 -9
  629. package/corpus/templates/clips/app/components/workspace/insights-hub.tsx +9 -19
  630. package/corpus/templates/clips/app/components/workspace/invite-dialog.tsx +10 -9
  631. package/corpus/templates/clips/app/components/workspace/members-list.tsx +11 -10
  632. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +2 -1
  633. package/corpus/templates/clips/app/components/workspace/top-creators-table.tsx +3 -2
  634. package/corpus/templates/clips/app/components/workspace/top-videos-table.tsx +3 -2
  635. package/corpus/templates/clips/app/global.css +1 -0
  636. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +2 -1
  637. package/corpus/templates/clips/app/hooks/use-mobile.ts +1 -0
  638. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +14 -10
  639. package/corpus/templates/clips/app/hooks/use-view-tracking.ts +14 -0
  640. package/corpus/templates/clips/app/i18n/ar-SA.ts +56 -0
  641. package/corpus/templates/clips/app/i18n/de-DE.ts +53 -0
  642. package/corpus/templates/clips/app/i18n/en-US.ts +52 -0
  643. package/corpus/templates/clips/app/i18n/es-ES.ts +53 -0
  644. package/corpus/templates/clips/app/i18n/fr-FR.ts +53 -0
  645. package/corpus/templates/clips/app/i18n/hi-IN.ts +52 -0
  646. package/corpus/templates/clips/app/i18n/ja-JP.ts +53 -0
  647. package/corpus/templates/clips/app/i18n/ko-KR.ts +52 -0
  648. package/corpus/templates/clips/app/i18n/pt-BR.ts +53 -0
  649. package/corpus/templates/clips/app/i18n/zh-CN.ts +51 -0
  650. package/corpus/templates/clips/app/i18n/zh-TW.ts +51 -0
  651. package/corpus/templates/clips/app/lib/recorder-preferences.ts +4 -0
  652. package/corpus/templates/clips/app/lib/timestamp-mapping.ts +5 -0
  653. package/corpus/templates/clips/app/root.tsx +26 -23
  654. package/corpus/templates/clips/app/routes/_app.dictate.tsx +39 -23
  655. package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
  656. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +134 -39
  657. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +20 -20
  658. package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -3
  659. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +92 -60
  660. package/corpus/templates/clips/app/routes/_app.settings.organization.tsx +8 -13
  661. package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +1 -1
  662. package/corpus/templates/clips/app/routes/_app.trash.tsx +96 -31
  663. package/corpus/templates/clips/app/routes/bug-report.done.tsx +1 -1
  664. package/corpus/templates/clips/app/routes/bug-report.tsx +11 -11
  665. package/corpus/templates/clips/app/routes/download.tsx +2 -2
  666. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +2 -1
  667. package/corpus/templates/clips/app/routes/invite.$token.tsx +8 -13
  668. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +263 -251
  669. package/corpus/templates/clips/app/routes/record.tsx +185 -78
  670. package/corpus/templates/clips/app/routes/share.$shareId.tsx +51 -29
  671. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +71 -16
  672. package/corpus/templates/clips/changelog/2026-07-05-mobile-clip-pages-use-a-tighter-viewer-layout-with-inline-ac.md +6 -0
  673. package/corpus/templates/clips/changelog/2026-07-06-ai-setup-now-switches-to-your-saved-provider-key-and-uses-fa.md +6 -0
  674. package/corpus/templates/clips/changelog/2026-07-06-browsers-that-can-t-play-a-clip-s-video-format-now-see-a-cle.md +6 -0
  675. package/corpus/templates/clips/changelog/2026-07-06-calendar-connections-no-longer-ask-you-to-reconnect-after-te.md +6 -0
  676. package/corpus/templates/clips/changelog/2026-07-06-chrome-extension-recordings-keep-your-selected-microphone.md +6 -0
  677. package/corpus/templates/clips/changelog/2026-07-06-clip-recordings-now-use-streaming-uploads-by-default.md +6 -0
  678. package/corpus/templates/clips/changelog/2026-07-06-clip-titles-in-the-library-grid-now-open-the-clip-instead-of.md +6 -0
  679. package/corpus/templates/clips/changelog/2026-07-06-desktop-countdown-now-dims-the-screen-before-recording.md +5 -0
  680. package/corpus/templates/clips/changelog/2026-07-06-desktop-popover-no-longer-drops-bluetooth-headphones-into-call-quality-mode.md +6 -0
  681. package/corpus/templates/clips/changelog/2026-07-06-desktop-recorder-now-refreshes-cameras-and-mics-when-the-pop.md +6 -0
  682. package/corpus/templates/clips/changelog/2026-07-06-desktop-recordings-keep-microphone-volume-clear-while-recor.md +5 -0
  683. package/corpus/templates/clips/changelog/2026-07-06-desktop-recordings-no-longer-get-stuck-optimizing-when-scree.md +6 -0
  684. package/corpus/templates/clips/changelog/2026-07-06-dictation-history-from-the-desktop-app-now-appears-in-the-di.md +6 -0
  685. package/corpus/templates/clips/changelog/2026-07-06-dictation-no-longer-overwrites-your-clipboard-and-messaging-.md +6 -0
  686. package/corpus/templates/clips/changelog/2026-07-06-double-tap-your-dictation-key-for-hands-free-dictation-tap-a.md +6 -0
  687. package/corpus/templates/clips/changelog/2026-07-06-fixed-desktop-recordings-sometimes-uploading-without-audio-a.md +6 -0
  688. package/corpus/templates/clips/changelog/2026-07-06-fixed-desktop-recordings-that-could-get-stuck-uploading-and-.md +6 -0
  689. package/corpus/templates/clips/changelog/2026-07-06-fixed-recordings-sometimes-capturing-from-the-wrong-micropho.md +6 -0
  690. package/corpus/templates/clips/changelog/2026-07-06-hands-free-dictation-and-meeting-cleanup-recover-more-reliab.md +6 -0
  691. package/corpus/templates/clips/changelog/2026-07-06-live-meetings-show-time-remaining-and-you-can-end-a-meeting-.md +6 -0
  692. package/corpus/templates/clips/changelog/2026-07-06-manage-your-personal-dictation-dictionary-from-the-dictate-t.md +6 -0
  693. package/corpus/templates/clips/changelog/2026-07-06-meetings-interrupted-by-quitting-or-crashing-now-close-out-c.md +6 -0
  694. package/corpus/templates/clips/changelog/2026-07-06-mic-only-desktop-recordings-now-use-the-selected-microphone-.md +6 -0
  695. package/corpus/templates/clips/changelog/2026-07-06-paste-your-last-dictation-anytime-with-cmd-ctrl-v-or-from-th.md +6 -0
  696. package/corpus/templates/clips/changelog/2026-07-06-press-esc-to-cancel-a-dictation-in-progress.md +6 -0
  697. package/corpus/templates/clips/changelog/2026-07-06-recordings-whose-video-file-contains-no-audio-track-no-longe.md +6 -0
  698. package/corpus/templates/clips/changelog/2026-07-06-search-inside-meeting-transcripts.md +6 -0
  699. package/corpus/templates/clips/changelog/2026-07-06-see-who-viewed-your-shared-clips-and-when-click-the-view-cou.md +6 -0
  700. package/corpus/templates/clips/changelog/2026-07-06-share-links-for-agents-now-appear-as-soon-as-the-share-menu-.md +6 -0
  701. package/corpus/templates/clips/changelog/2026-07-06-share-popovers-now-stay-above-video-playback-controls.md +6 -0
  702. package/corpus/templates/clips/changelog/2026-07-06-shared-clip-pages-now-recover-automatically-from-transient-v.md +6 -0
  703. package/corpus/templates/clips/changelog/2026-07-06-the-desktop-app-now-walks-you-through-permissions-on-first-l.md +6 -0
  704. package/corpus/templates/clips/changelog/2026-07-06-transcript-cleanup-preferences-now-save-per-user.md +6 -0
  705. package/corpus/templates/clips/changelog/2026-07-06-transcripts-now-retry-automatically-after-transient-failures.md +6 -0
  706. package/corpus/templates/clips/changelog/2026-07-06-video-compression-now-runs-in-the-background-without-blocking.md +6 -0
  707. package/corpus/templates/clips/changelog/2026-07-07-native-microphone-recordings-now-reduce-steady-background-noise.md +6 -0
  708. package/corpus/templates/clips/changelog/2026-07-07-recording-transcripts-now-line-up-with-the-video-start-afte.md +6 -0
  709. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  710. package/corpus/templates/clips/chrome-extension/src/background.ts +141 -48
  711. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +383 -59
  712. package/corpus/templates/clips/chrome-extension/src/overlay.ts +51 -10
  713. package/corpus/templates/clips/chrome-extension/src/popup.ts +57 -6
  714. package/corpus/templates/clips/desktop/package.json +1 -1
  715. package/corpus/templates/clips/desktop/scripts/sign-macos-local.ts +67 -0
  716. package/corpus/templates/clips/desktop/src/app.tsx +38 -13
  717. package/corpus/templates/clips/desktop/src/components/SourceRow.tsx +1 -0
  718. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +88 -28
  719. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +165 -55
  720. package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +35 -10
  721. package/corpus/templates/clips/desktop/src/lib/backtrack.ts +12 -0
  722. package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +129 -34
  723. package/corpus/templates/clips/desktop/src/lib/media-device-selection.ts +157 -0
  724. package/corpus/templates/clips/desktop/src/lib/permissions.ts +2 -0
  725. package/corpus/templates/clips/desktop/src/lib/recorder.ts +109 -47
  726. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +22 -2
  727. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +29 -1
  728. package/corpus/templates/clips/desktop/src/lib/updater.ts +6 -0
  729. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +372 -31
  730. package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +90 -0
  731. package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +4 -6
  732. package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +214 -9
  733. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +37 -52
  734. package/corpus/templates/clips/desktop/src/styles.css +1 -1
  735. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +7 -0
  736. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +371 -43
  737. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +79 -0
  738. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1418 -260
  739. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +188 -44
  740. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +197 -8
  741. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +64 -8
  742. package/corpus/templates/clips/desktop/src-tauri/src/state.rs +3 -2
  743. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +10 -0
  744. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +29 -8
  745. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +136 -13
  746. package/corpus/templates/clips/desktop/vite.config.ts +3 -0
  747. package/corpus/templates/clips/package.json +1 -1
  748. package/corpus/templates/clips/server/db/schema.ts +26 -0
  749. package/corpus/templates/clips/server/jobs/builder-media-compression.ts +41 -0
  750. package/corpus/templates/clips/server/jobs/stale-meeting-sweeper.ts +294 -0
  751. package/corpus/templates/clips/server/lib/builder-media-compression.ts +976 -0
  752. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +107 -56
  753. package/corpus/templates/clips/server/lib/env-flags.ts +4 -0
  754. package/corpus/templates/clips/server/lib/google-calendar-client.ts +8 -1
  755. package/corpus/templates/clips/server/lib/media-worker.ts +153 -0
  756. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +58 -17
  757. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +17 -5
  758. package/corpus/templates/clips/server/lib/video-remux.ts +90 -0
  759. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +4 -0
  760. package/corpus/templates/clips/server/plugins/db.ts +196 -0
  761. package/corpus/templates/clips/server/register-secrets.ts +78 -0
  762. package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.get.ts +15 -0
  763. package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +27 -0
  764. package/corpus/templates/clips/server/routes/api/media-worker/callback.post.ts +88 -0
  765. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +87 -7
  766. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +11 -4
  767. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +55 -13
  768. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +24 -42
  769. package/corpus/templates/clips/server/routes/api/view-event.post.ts +29 -1
  770. package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
  771. package/corpus/templates/clips/shared/media-device-selection.ts +157 -0
  772. package/corpus/templates/clips/shared/media-worker-contract.ts +146 -0
  773. package/corpus/templates/content/.agents/skills/actions/SKILL.md +55 -0
  774. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +67 -2
  775. package/corpus/templates/content/AGENTS.md +73 -65
  776. package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
  777. package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
  778. package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
  779. package/corpus/templates/content/actions/_database-utils.ts +44 -4
  780. package/corpus/templates/content/actions/_local-file-documents.ts +90 -1
  781. package/corpus/templates/content/actions/_notion-action-utils.ts +12 -4
  782. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
  783. package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
  784. package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
  785. package/corpus/templates/content/actions/delete-content-database.ts +1 -0
  786. package/corpus/templates/content/actions/delete-document.ts +8 -0
  787. package/corpus/templates/content/actions/get-document.ts +11 -2
  788. package/corpus/templates/content/actions/list-content-databases.ts +57 -9
  789. package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
  790. package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
  791. package/corpus/templates/content/actions/remove-local-file-source.ts +100 -0
  792. package/corpus/templates/content/actions/restore-content-database.ts +1 -0
  793. package/corpus/templates/content/actions/sync-notion-comments.ts +174 -20
  794. package/corpus/templates/content/actions/update-document.ts +134 -5
  795. package/corpus/templates/content/app/blocks/SourceComponentBlock.tsx +31 -3
  796. package/corpus/templates/content/app/blocks/contentBlockRegistry.tsx +4 -4
  797. package/corpus/templates/content/app/components/EmptyState.tsx +1 -1
  798. package/corpus/templates/content/app/components/ThemeToggle.tsx +6 -6
  799. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +5 -5
  800. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
  801. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +5 -5
  802. package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +1 -1
  803. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +9 -14630
  804. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +142 -79
  805. package/corpus/templates/content/app/components/editor/DocumentEditorSkeleton.tsx +36 -0
  806. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +98 -46
  807. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +29 -44
  808. package/corpus/templates/content/app/components/editor/EmojiPicker.tsx +5 -4
  809. package/corpus/templates/content/app/components/editor/LinkHoverPreview.tsx +5 -4
  810. package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +1 -1
  811. package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +2 -2
  812. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +5 -5
  813. package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +13 -13
  814. package/corpus/templates/content/app/components/editor/VersionHistoryPanel.tsx +10 -10
  815. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +28 -11
  816. package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
  817. package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
  818. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +736 -69
  819. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +36 -1
  820. package/corpus/templates/content/app/components/editor/database/settings.tsx +3224 -0
  821. package/corpus/templates/content/app/components/editor/database/shared.tsx +19 -19
  822. package/corpus/templates/content/app/components/editor/database/text.ts +23 -0
  823. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +50 -9
  824. package/corpus/templates/content/app/components/editor/extensions/AudioBlock.tsx +19 -18
  825. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +57 -31
  826. package/corpus/templates/content/app/components/editor/extensions/LocalMdxComponentNode.tsx +8 -8
  827. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +9 -0
  828. package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +19 -18
  829. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
  830. package/corpus/templates/content/app/components/layout/Header.tsx +1 -5
  831. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -8
  832. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +214 -56
  833. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +33 -30
  834. package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +11 -11
  835. package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +109 -1
  836. package/corpus/templates/content/app/components/ui/alert-dialog.tsx +1 -0
  837. package/corpus/templates/content/app/components/ui/button.tsx +1 -0
  838. package/corpus/templates/content/app/components/ui/dialog.tsx +1 -0
  839. package/corpus/templates/content/app/components/ui/dropdown-menu.tsx +1 -0
  840. package/corpus/templates/content/app/components/ui/input.tsx +1 -0
  841. package/corpus/templates/content/app/components/ui/label.tsx +1 -0
  842. package/corpus/templates/content/app/components/ui/popover.tsx +1 -0
  843. package/corpus/templates/content/app/components/ui/separator.tsx +1 -0
  844. package/corpus/templates/content/app/components/ui/sheet.tsx +1 -0
  845. package/corpus/templates/content/app/components/ui/skeleton.tsx +1 -0
  846. package/corpus/templates/content/app/components/ui/sonner.tsx +1 -0
  847. package/corpus/templates/content/app/components/ui/spinner.tsx +1 -0
  848. package/corpus/templates/content/app/components/ui/table.tsx +1 -0
  849. package/corpus/templates/content/app/components/ui/toaster.tsx +1 -0
  850. package/corpus/templates/content/app/components/ui/toolkit-provider.tsx +12 -0
  851. package/corpus/templates/content/app/components/ui/tooltip.tsx +1 -0
  852. package/corpus/templates/content/app/global.css +42 -0
  853. package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
  854. package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
  855. package/corpus/templates/content/app/hooks/use-documents.ts +136 -37
  856. package/corpus/templates/content/app/hooks/use-mobile.ts +1 -0
  857. package/corpus/templates/content/app/hooks/use-notion.ts +85 -15
  858. package/corpus/templates/content/app/i18n/zh-TW.ts +49 -0
  859. package/corpus/templates/content/app/i18n-data.ts +508 -0
  860. package/corpus/templates/content/app/root.tsx +57 -29
  861. package/corpus/templates/content/app/routes/_app._index.tsx +1 -1
  862. package/corpus/templates/content/app/routes/_app.local-files.tsx +268 -81
  863. package/corpus/templates/content/app/routes/_app.settings.tsx +4 -21
  864. package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
  865. package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
  866. package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
  867. package/corpus/templates/content/changelog/2026-07-01-builder-source-components-show-preservation-and-review-states.md +6 -0
  868. package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
  869. package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
  870. package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
  871. package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
  872. package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
  873. package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
  874. package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
  875. package/corpus/templates/content/changelog/2026-07-06-document-headers-and-page-bodies-now-share-one-background-shade.md +6 -0
  876. package/corpus/templates/content/changelog/2026-07-06-export-submenus-stay-visible-outside-the-page-actions-menu.md +6 -0
  877. package/corpus/templates/content/changelog/2026-07-06-fixed-collaborative-documents-reapplying-content-before-save.md +6 -0
  878. package/corpus/templates/content/changelog/2026-07-06-local-files-now-remember-sidebar-collapse-state-and-include-.md +6 -0
  879. package/corpus/templates/content/changelog/2026-07-06-notion-comment-sync-now-preserves-threads-replies-stay-attac.md +6 -0
  880. package/corpus/templates/content/changelog/2026-07-06-notion-sync-is-far-more-reliable-auto-sync-no-longer-overwri.md +6 -0
  881. package/corpus/templates/content/changelog/2026-07-06-opening-pages-now-highlights-the-sidebar-item-and-shows-the-.md +6 -0
  882. package/corpus/templates/content/changelog/2026-07-06-presence-markers-in-the-editor-are-now-compact-instead-of-co.md +6 -0
  883. package/corpus/templates/content/changelog/2026-07-06-sidebar-collapse-now-keeps-the-same-background-shade-as-the-.md +6 -0
  884. package/corpus/templates/content/changelog/2026-07-06-the-content-header-no-longer-shows-the-global-notifications-.md +6 -0
  885. package/corpus/templates/content/parity/matrix.md +24 -24
  886. package/corpus/templates/content/parity/matrix.ts +7 -3
  887. package/corpus/templates/content/server/db/schema.ts +15 -0
  888. package/corpus/templates/content/server/lib/notion-sync.ts +481 -39
  889. package/corpus/templates/content/server/lib/notion.ts +212 -48
  890. package/corpus/templates/content/server/plugins/db.ts +20 -0
  891. package/corpus/templates/content/server/routes/api/documents/[id]/notion/resolve.post.ts +14 -2
  892. package/corpus/templates/content/server/routes/api/notion/callback.get.ts +54 -6
  893. package/corpus/templates/content/shared/api.ts +13 -0
  894. package/corpus/templates/content/shared/builder-mdx.ts +84 -15
  895. package/corpus/templates/content/shared/builder-source-component-registry.ts +177 -0
  896. package/corpus/templates/content/shared/nfm.ts +459 -66
  897. package/corpus/templates/content/shared/source-component-block.ts +72 -6
  898. package/corpus/templates/content/vite.config.ts +17 -1
  899. package/corpus/templates/design/.agents/skills/actions/SKILL.md +55 -0
  900. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +44 -25
  901. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
  902. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
  903. package/corpus/templates/design/AGENTS.md +12 -4
  904. package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
  905. package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
  906. package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
  907. package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
  908. package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
  909. package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
  910. package/corpus/templates/design/actions/create-component.ts +29 -60
  911. package/corpus/templates/design/actions/create-file.ts +9 -2
  912. package/corpus/templates/design/actions/duplicate-design.ts +31 -2
  913. package/corpus/templates/design/actions/edit-design.ts +36 -38
  914. package/corpus/templates/design/actions/generate-design-title.ts +87 -0
  915. package/corpus/templates/design/actions/generate-design.ts +53 -18
  916. package/corpus/templates/design/actions/generate-screens.ts +108 -4
  917. package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
  918. package/corpus/templates/design/actions/import-design-source.ts +8 -58
  919. package/corpus/templates/design/actions/insert-asset.ts +34 -29
  920. package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
  921. package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
  922. package/corpus/templates/design/actions/present-design-variants.ts +7 -2
  923. package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
  924. package/corpus/templates/design/actions/run-design-audit.ts +127 -1
  925. package/corpus/templates/design/actions/take-design-screenshot.ts +673 -0
  926. package/corpus/templates/design/actions/update-file.ts +190 -75
  927. package/corpus/templates/design/app/components/ThemeToggle.tsx +6 -6
  928. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
  929. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +480 -233
  930. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
  931. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +2 -2
  932. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +99 -24
  933. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +6 -97
  934. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +7 -7
  935. package/corpus/templates/design/app/components/design/EditPanel.tsx +1120 -6977
  936. package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +9 -8
  937. package/corpus/templates/design/app/components/design/LayersPanel.tsx +433 -99
  938. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +6 -5
  939. package/corpus/templates/design/app/components/design/MotionDock.tsx +1138 -117
  940. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
  941. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +3 -3
  942. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
  943. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +3 -3
  944. package/corpus/templates/design/app/components/design/StatesPanel.tsx +13 -13
  945. package/corpus/templates/design/app/components/design/TokensPanel.tsx +14 -14
  946. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +6 -6
  947. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2221 -244
  948. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
  949. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
  950. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
  951. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
  952. package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +3 -3
  953. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +5 -8
  954. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +6 -6
  955. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +2 -1
  956. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +18 -20
  957. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
  958. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +6 -6
  959. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
  960. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
  961. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
  962. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
  963. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
  964. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
  965. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
  966. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
  967. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
  968. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
  969. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
  970. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
  971. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
  972. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
  973. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
  974. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
  975. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
  976. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
  977. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
  978. package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
  979. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
  980. package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
  981. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
  982. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
  983. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +3 -3
  984. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +98 -47
  985. package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
  986. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +3 -3
  987. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +207 -105
  988. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +6 -6
  989. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
  990. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
  991. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +6 -6
  992. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
  993. package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
  994. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +106 -16
  995. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +14 -14
  996. package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +9 -9
  997. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
  998. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
  999. package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
  1000. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
  1001. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
  1002. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
  1003. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
  1004. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
  1005. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
  1006. package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
  1007. package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
  1008. package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
  1009. package/corpus/templates/design/app/components/design/types.ts +15 -0
  1010. package/corpus/templates/design/app/components/design-system/DesignSystemCard.tsx +3 -2
  1011. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +124 -23
  1012. package/corpus/templates/design/app/components/layout/Header.tsx +2 -2
  1013. package/corpus/templates/design/app/components/layout/Sidebar.tsx +5 -5
  1014. package/corpus/templates/design/app/components/ui/alert-dialog.tsx +1 -0
  1015. package/corpus/templates/design/app/components/ui/avatar.tsx +1 -0
  1016. package/corpus/templates/design/app/components/ui/badge.tsx +1 -0
  1017. package/corpus/templates/design/app/components/ui/button.tsx +1 -0
  1018. package/corpus/templates/design/app/components/ui/card.tsx +1 -0
  1019. package/corpus/templates/design/app/components/ui/checkbox.tsx +1 -0
  1020. package/corpus/templates/design/app/components/ui/collapsible.tsx +1 -0
  1021. package/corpus/templates/design/app/components/ui/context-menu.tsx +1 -0
  1022. package/corpus/templates/design/app/components/ui/dialog.tsx +1 -0
  1023. package/corpus/templates/design/app/components/ui/dropdown-menu.tsx +1 -0
  1024. package/corpus/templates/design/app/components/ui/input.tsx +1 -0
  1025. package/corpus/templates/design/app/components/ui/label.tsx +1 -0
  1026. package/corpus/templates/design/app/components/ui/popover.tsx +1 -0
  1027. package/corpus/templates/design/app/components/ui/select.tsx +1 -0
  1028. package/corpus/templates/design/app/components/ui/separator.tsx +1 -0
  1029. package/corpus/templates/design/app/components/ui/sheet.tsx +1 -0
  1030. package/corpus/templates/design/app/components/ui/skeleton.tsx +1 -0
  1031. package/corpus/templates/design/app/components/ui/slider.tsx +1 -0
  1032. package/corpus/templates/design/app/components/ui/sonner.tsx +1 -0
  1033. package/corpus/templates/design/app/components/ui/spinner.tsx +1 -0
  1034. package/corpus/templates/design/app/components/ui/switch.tsx +1 -0
  1035. package/corpus/templates/design/app/components/ui/tabs.tsx +1 -0
  1036. package/corpus/templates/design/app/components/ui/textarea.tsx +1 -0
  1037. package/corpus/templates/design/app/components/ui/toolkit-provider.tsx +12 -0
  1038. package/corpus/templates/design/app/components/ui/tooltip.tsx +1 -0
  1039. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +7 -7
  1040. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +7 -7
  1041. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +3 -3
  1042. package/corpus/templates/design/app/global.css +9 -8
  1043. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
  1044. package/corpus/templates/design/app/i18n/zh-TW.ts +166 -13
  1045. package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
  1046. package/corpus/templates/design/app/i18n-data.ts +1702 -187
  1047. package/corpus/templates/design/app/i18n-motion.ts +563 -0
  1048. package/corpus/templates/design/app/lib/design-import.ts +73 -0
  1049. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +6 -6
  1050. package/corpus/templates/design/app/pages/DesignSystems.tsx +31 -31
  1051. package/corpus/templates/design/app/pages/Index.tsx +66 -51
  1052. package/corpus/templates/design/app/pages/NotFound.tsx +2 -1
  1053. package/corpus/templates/design/app/pages/Present.tsx +2 -1
  1054. package/corpus/templates/design/app/pages/VisualEdit.tsx +2 -1
  1055. package/corpus/templates/design/app/root.tsx +7 -4
  1056. package/corpus/templates/design/app/routes/settings.tsx +22 -49
  1057. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
  1058. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
  1059. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
  1060. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
  1061. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
  1062. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
  1063. package/corpus/templates/design/changelog/2026-07-05-code-sidebar-tabs-now-use-a-quieter-active-highlight-that-ma.md +6 -0
  1064. package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
  1065. package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
  1066. package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
  1067. package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
  1068. package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
  1069. package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
  1070. package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
  1071. package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
  1072. package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
  1073. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
  1074. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
  1075. package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
  1076. package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
  1077. package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
  1078. package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
  1079. package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
  1080. package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
  1081. package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
  1082. package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
  1083. package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
  1084. package/corpus/templates/design/changelog/2026-07-06-fig-files-now-stay-on-the-builder-indexing-path-through-desi.md +6 -0
  1085. package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
  1086. package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
  1087. package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
  1088. package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
  1089. package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
  1090. package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
  1091. package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
  1092. package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
  1093. package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
  1094. package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
  1095. package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
  1096. package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
  1097. package/corpus/templates/design/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
  1098. package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
  1099. package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
  1100. package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
  1101. package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
  1102. package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
  1103. package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
  1104. package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
  1105. package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
  1106. package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
  1107. package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
  1108. package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
  1109. package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
  1110. package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
  1111. package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
  1112. package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
  1113. package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
  1114. package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
  1115. package/corpus/templates/design/changelog/2026-07-06-the-design-agent-now-visually-reviews-rendered-screenshots-o.md +6 -0
  1116. package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
  1117. package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
  1118. package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
  1119. package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
  1120. package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
  1121. package/corpus/templates/design/server/handlers/import-design-file.ts +4 -43
  1122. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +2 -2
  1123. package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
  1124. package/corpus/templates/design/server/lib/visible-clipboard-html.ts +32 -0
  1125. package/corpus/templates/design/server/source-workspace.ts +139 -59
  1126. package/corpus/templates/design/shared/board-file.ts +11 -2
  1127. package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
  1128. package/corpus/templates/design/shared/canvas-math.ts +354 -35
  1129. package/corpus/templates/design/shared/code-layer.ts +277 -8
  1130. package/corpus/templates/design/shared/design-review.ts +1 -0
  1131. package/corpus/templates/design/shared/design-state.ts +11 -4
  1132. package/corpus/templates/design/shared/interaction-states.ts +548 -0
  1133. package/corpus/templates/design/shared/motion-compiler.ts +186 -23
  1134. package/corpus/templates/design/shared/motion-easing.ts +294 -0
  1135. package/corpus/templates/design/shared/motion-timeline.ts +400 -34
  1136. package/corpus/templates/design/shared/prompt-title.ts +95 -0
  1137. package/corpus/templates/design/shared/responsive-classes.ts +448 -10
  1138. package/corpus/templates/design/shared/screen-annotation.ts +46 -0
  1139. package/corpus/templates/design/shared/shader-fills.ts +1009 -0
  1140. package/corpus/templates/design/shared/shader-presets.ts +852 -4
  1141. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +55 -0
  1142. package/corpus/templates/dispatch/actions/list-staged-datasets.ts +3 -0
  1143. package/corpus/templates/dispatch/app/routes/settings.tsx +3 -17
  1144. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +55 -0
  1145. package/corpus/templates/forms/app/components/CloudUpgrade.tsx +10 -10
  1146. package/corpus/templates/forms/app/components/ThemeToggle.tsx +5 -5
  1147. package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +11 -10
  1148. package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +10 -13
  1149. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +17 -17
  1150. package/corpus/templates/forms/app/components/ui/alert-dialog.tsx +1 -0
  1151. package/corpus/templates/forms/app/components/ui/badge.tsx +1 -0
  1152. package/corpus/templates/forms/app/components/ui/button.tsx +1 -0
  1153. package/corpus/templates/forms/app/components/ui/calendar.tsx +1 -1
  1154. package/corpus/templates/forms/app/components/ui/card.tsx +1 -0
  1155. package/corpus/templates/forms/app/components/ui/checkbox.tsx +1 -0
  1156. package/corpus/templates/forms/app/components/ui/command.tsx +1 -1
  1157. package/corpus/templates/forms/app/components/ui/dialog.tsx +1 -0
  1158. package/corpus/templates/forms/app/components/ui/dropdown-menu.tsx +1 -0
  1159. package/corpus/templates/forms/app/components/ui/input.tsx +1 -0
  1160. package/corpus/templates/forms/app/components/ui/label.tsx +1 -0
  1161. package/corpus/templates/forms/app/components/ui/popover.tsx +1 -0
  1162. package/corpus/templates/forms/app/components/ui/radio-group.tsx +1 -0
  1163. package/corpus/templates/forms/app/components/ui/scroll-area.tsx +1 -0
  1164. package/corpus/templates/forms/app/components/ui/select.tsx +1 -0
  1165. package/corpus/templates/forms/app/components/ui/separator.tsx +1 -0
  1166. package/corpus/templates/forms/app/components/ui/skeleton.tsx +1 -0
  1167. package/corpus/templates/forms/app/components/ui/slider.tsx +1 -0
  1168. package/corpus/templates/forms/app/components/ui/switch.tsx +1 -0
  1169. package/corpus/templates/forms/app/components/ui/table.tsx +1 -0
  1170. package/corpus/templates/forms/app/components/ui/tabs.tsx +1 -0
  1171. package/corpus/templates/forms/app/components/ui/textarea.tsx +1 -0
  1172. package/corpus/templates/forms/app/components/ui/toolkit-provider.tsx +12 -0
  1173. package/corpus/templates/forms/app/components/ui/tooltip.tsx +1 -0
  1174. package/corpus/templates/forms/app/global.css +1 -0
  1175. package/corpus/templates/forms/app/hooks/use-mobile.ts +1 -0
  1176. package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -1
  1177. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +24 -26
  1178. package/corpus/templates/forms/app/pages/FormFillPage.tsx +2 -2
  1179. package/corpus/templates/forms/app/pages/FormsListPage.tsx +21 -21
  1180. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +11 -11
  1181. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +4 -4
  1182. package/corpus/templates/forms/app/root.tsx +11 -8
  1183. package/corpus/templates/forms/app/routes/_app.settings.tsx +6 -21
  1184. package/corpus/templates/forms/app/routes/form-preview.tsx +3 -3
  1185. package/corpus/templates/forms/changelog/2026-07-06-the-form-builder-header-no-longer-shows-the-global-notificat.md +6 -0
  1186. package/corpus/templates/macros/app/components/AddExerciseDialog.tsx +1 -1
  1187. package/corpus/templates/macros/app/components/AddMealDialog.tsx +1 -1
  1188. package/corpus/templates/macros/app/components/AddWeightDialog.tsx +1 -1
  1189. package/corpus/templates/macros/app/components/DailyProgress.tsx +7 -7
  1190. package/corpus/templates/macros/app/components/WeeklyCaloriesChart.tsx +2 -1
  1191. package/corpus/templates/macros/app/components/WeightCard.tsx +10 -10
  1192. package/corpus/templates/macros/app/components/WeightTracker.tsx +1 -1
  1193. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +6 -10
  1194. package/corpus/templates/macros/app/components/ui/alert-dialog.tsx +1 -0
  1195. package/corpus/templates/macros/app/components/ui/label.tsx +1 -0
  1196. package/corpus/templates/macros/app/components/ui/select.tsx +1 -0
  1197. package/corpus/templates/macros/app/components/ui/sheet.tsx +1 -0
  1198. package/corpus/templates/macros/app/components/ui/skeleton.tsx +1 -0
  1199. package/corpus/templates/macros/app/components/ui/sonner.tsx +1 -0
  1200. package/corpus/templates/macros/app/components/ui/toolkit-provider.tsx +12 -0
  1201. package/corpus/templates/macros/app/components/ui/tooltip.tsx +1 -0
  1202. package/corpus/templates/macros/app/global.css +1 -0
  1203. package/corpus/templates/macros/app/root.tsx +17 -14
  1204. package/corpus/templates/macros/app/routes/_index.tsx +1 -1
  1205. package/corpus/templates/macros/app/routes/analytics.tsx +8 -8
  1206. package/corpus/templates/macros/app/routes/settings.tsx +4 -19
  1207. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +55 -0
  1208. package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +67 -0
  1209. package/corpus/templates/mail/.agents/skills/draft-queue/SKILL.md +35 -1
  1210. package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +74 -33
  1211. package/corpus/templates/mail/AGENTS.md +102 -16
  1212. package/corpus/templates/mail/actions/archive-email.ts +67 -11
  1213. package/corpus/templates/mail/actions/manage-snippets.ts +106 -0
  1214. package/corpus/templates/mail/actions/mark-read.ts +45 -11
  1215. package/corpus/templates/mail/actions/send-queued-drafts.ts +77 -42
  1216. package/corpus/templates/mail/actions/star-email.ts +55 -10
  1217. package/corpus/templates/mail/actions/trash-email.ts +48 -9
  1218. package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +1 -1
  1219. package/corpus/templates/mail/app/components/ThemeToggle.tsx +6 -6
  1220. package/corpus/templates/mail/app/components/agent-engine-picker.tsx +10 -10
  1221. package/corpus/templates/mail/app/components/email/CodeBlockLangPicker.tsx +4 -3
  1222. package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +5 -5
  1223. package/corpus/templates/mail/app/components/email/ComposeEditor.tsx +109 -10
  1224. package/corpus/templates/mail/app/components/email/ComposeModal.tsx +44 -19
  1225. package/corpus/templates/mail/app/components/email/ComposeSlashMenu.tsx +139 -17
  1226. package/corpus/templates/mail/app/components/email/EmailList.tsx +522 -314
  1227. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +130 -45
  1228. package/corpus/templates/mail/app/components/email/EmailThread.tsx +9 -9
  1229. package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +35 -13
  1230. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +13 -13
  1231. package/corpus/templates/mail/app/components/email/MobileActionBar.tsx +13 -13
  1232. package/corpus/templates/mail/app/components/email/RecipientInput.tsx +53 -42
  1233. package/corpus/templates/mail/app/components/email/SendLaterButton.tsx +5 -4
  1234. package/corpus/templates/mail/app/components/email/SnoozeModal.tsx +1 -1
  1235. package/corpus/templates/mail/app/components/email/SnoozePopover.tsx +6 -6
  1236. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +19 -23
  1237. package/corpus/templates/mail/app/components/layout/HeaderActions.tsx +12 -4
  1238. package/corpus/templates/mail/app/components/layout/SearchBar.tsx +138 -18
  1239. package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +23 -23
  1240. package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +303 -0
  1241. package/corpus/templates/mail/app/components/ui/alert-dialog.tsx +1 -0
  1242. package/corpus/templates/mail/app/components/ui/badge.tsx +1 -0
  1243. package/corpus/templates/mail/app/components/ui/button.tsx +1 -0
  1244. package/corpus/templates/mail/app/components/ui/calendar.tsx +1 -1
  1245. package/corpus/templates/mail/app/components/ui/command.tsx +1 -1
  1246. package/corpus/templates/mail/app/components/ui/dialog.tsx +1 -0
  1247. package/corpus/templates/mail/app/components/ui/drawer.tsx +1 -0
  1248. package/corpus/templates/mail/app/components/ui/dropdown-menu.tsx +1 -0
  1249. package/corpus/templates/mail/app/components/ui/popover.tsx +1 -0
  1250. package/corpus/templates/mail/app/components/ui/select.tsx +1 -0
  1251. package/corpus/templates/mail/app/components/ui/skeleton.tsx +1 -0
  1252. package/corpus/templates/mail/app/components/ui/spinner.tsx +1 -0
  1253. package/corpus/templates/mail/app/components/ui/switch.tsx +1 -0
  1254. package/corpus/templates/mail/app/components/ui/toolkit-provider.tsx +12 -0
  1255. package/corpus/templates/mail/app/components/ui/tooltip.tsx +1 -0
  1256. package/corpus/templates/mail/app/global.css +20 -3
  1257. package/corpus/templates/mail/app/hooks/use-emails.ts +187 -0
  1258. package/corpus/templates/mail/app/hooks/use-snippets.ts +103 -0
  1259. package/corpus/templates/mail/app/i18n/ar-SA.ts +17 -0
  1260. package/corpus/templates/mail/app/i18n/de-DE.ts +18 -0
  1261. package/corpus/templates/mail/app/i18n/en-US.ts +17 -0
  1262. package/corpus/templates/mail/app/i18n/es-ES.ts +17 -0
  1263. package/corpus/templates/mail/app/i18n/fr-FR.ts +18 -0
  1264. package/corpus/templates/mail/app/i18n/hi-IN.ts +17 -0
  1265. package/corpus/templates/mail/app/i18n/ja-JP.ts +18 -0
  1266. package/corpus/templates/mail/app/i18n/ko-KR.ts +17 -0
  1267. package/corpus/templates/mail/app/i18n/pt-BR.ts +17 -0
  1268. package/corpus/templates/mail/app/i18n/zh-CN.ts +16 -0
  1269. package/corpus/templates/mail/app/i18n/zh-TW.ts +16 -0
  1270. package/corpus/templates/mail/app/pages/DraftQueuePage.tsx +14 -14
  1271. package/corpus/templates/mail/app/pages/InboxPage.tsx +3 -3
  1272. package/corpus/templates/mail/app/pages/NotFound.tsx +2 -1
  1273. package/corpus/templates/mail/app/pages/SettingsPage.tsx +64 -66
  1274. package/corpus/templates/mail/app/root.tsx +21 -18
  1275. package/corpus/templates/mail/app/routes/_index.tsx +2 -1
  1276. package/corpus/templates/mail/app/routes/email.tsx +2 -2
  1277. package/corpus/templates/mail/changelog/2026-07-06-a-queued-draft-can-no-longer-be-sent-twice-by-simultaneous-s.md +6 -0
  1278. package/corpus/templates/mail/changelog/2026-07-06-bulk-archive-trash-star-and-mark-read-now-complete-in-one-fa.md +6 -0
  1279. package/corpus/templates/mail/changelog/2026-07-06-dark-mode-sidebars-and-notifications-now-use-the-softer-gray.md +6 -0
  1280. package/corpus/templates/mail/changelog/2026-07-06-inbox-automations-pick-up-new-mail-faster.md +6 -0
  1281. package/corpus/templates/mail/changelog/2026-07-06-inbox-lists-stay-fast-as-scheduled-and-snoozed-mail-accumula.md +6 -0
  1282. package/corpus/templates/mail/changelog/2026-07-06-long-inboxes-scroll-smoothly-and-stay-fast-as-more-mail-load.md +6 -0
  1283. package/corpus/templates/mail/changelog/2026-07-06-paste-or-drop-images-straight-into-the-composer-to-send-them.md +6 -0
  1284. package/corpus/templates/mail/changelog/2026-07-06-save-reusable-snippets-and-insert-them-from-the-compose-slas.md +6 -0
  1285. package/corpus/templates/mail/changelog/2026-07-06-scrolling-older-mail-keeps-loading-correctly-when-no-gmail-a.md +6 -0
  1286. package/corpus/templates/mail/changelog/2026-07-06-search-shows-instant-matches-from-already-loaded-mail-while-.md +6 -0
  1287. package/corpus/templates/mail/changelog/2026-07-06-sends-now-fail-with-a-clear-error-instead-of-quietly-missing.md +6 -0
  1288. package/corpus/templates/mail/changelog/2026-07-06-the-mail-header-no-longer-shows-the-global-notifications-bel.md +6 -0
  1289. package/corpus/templates/mail/package.json +1 -0
  1290. package/corpus/templates/mail/server/db/schema.ts +11 -0
  1291. package/corpus/templates/mail/server/handlers/emails.ts +20 -1
  1292. package/corpus/templates/mail/server/lib/automation-engine.ts +53 -25
  1293. package/corpus/templates/mail/server/lib/google-auth.ts +140 -9
  1294. package/corpus/templates/mail/server/lib/jobs.ts +22 -6
  1295. package/corpus/templates/mail/server/lib/outgoing-email.ts +11 -2
  1296. package/corpus/templates/mail/server/lib/queued-drafts.ts +145 -4
  1297. package/corpus/templates/mail/server/plugins/db.ts +29 -0
  1298. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +55 -0
  1299. package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +42 -3
  1300. package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -4
  1301. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +20 -20
  1302. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +3 -2
  1303. package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +2 -2
  1304. package/corpus/templates/plan/app/components/plan/GuestModeBanner.tsx +2 -1
  1305. package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +8 -8
  1306. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +6 -1
  1307. package/corpus/templates/plan/app/components/plan/PlanImageViewer.tsx +8 -12
  1308. package/corpus/templates/plan/app/components/plan/PlanMarkdownEditor.tsx +6 -1
  1309. package/corpus/templates/plan/app/components/plan/PlanVisualSurface.tsx +3 -3
  1310. package/corpus/templates/plan/app/components/plan/planBlocks.tsx +4 -4
  1311. package/corpus/templates/plan/app/components/plan/wireframe/kit/rough.tsx +61 -27
  1312. package/corpus/templates/plan/app/components/ui/alert-dialog.tsx +1 -0
  1313. package/corpus/templates/plan/app/components/ui/avatar.tsx +1 -0
  1314. package/corpus/templates/plan/app/components/ui/badge.tsx +1 -0
  1315. package/corpus/templates/plan/app/components/ui/button.tsx +1 -0
  1316. package/corpus/templates/plan/app/components/ui/card.tsx +1 -0
  1317. package/corpus/templates/plan/app/components/ui/checkbox.tsx +1 -0
  1318. package/corpus/templates/plan/app/components/ui/collapsible.tsx +1 -0
  1319. package/corpus/templates/plan/app/components/ui/dialog.tsx +1 -0
  1320. package/corpus/templates/plan/app/components/ui/dropdown-menu.tsx +1 -0
  1321. package/corpus/templates/plan/app/components/ui/input.tsx +1 -0
  1322. package/corpus/templates/plan/app/components/ui/label.tsx +1 -0
  1323. package/corpus/templates/plan/app/components/ui/popover.tsx +1 -0
  1324. package/corpus/templates/plan/app/components/ui/scroll-area.tsx +1 -0
  1325. package/corpus/templates/plan/app/components/ui/select.tsx +1 -0
  1326. package/corpus/templates/plan/app/components/ui/separator.tsx +1 -0
  1327. package/corpus/templates/plan/app/components/ui/sheet.tsx +1 -0
  1328. package/corpus/templates/plan/app/components/ui/skeleton.tsx +1 -0
  1329. package/corpus/templates/plan/app/components/ui/sonner.tsx +1 -0
  1330. package/corpus/templates/plan/app/components/ui/spinner.tsx +1 -0
  1331. package/corpus/templates/plan/app/components/ui/textarea.tsx +1 -0
  1332. package/corpus/templates/plan/app/components/ui/toggle-group.tsx +1 -0
  1333. package/corpus/templates/plan/app/components/ui/toolkit-provider.tsx +12 -0
  1334. package/corpus/templates/plan/app/components/ui/tooltip.tsx +1 -0
  1335. package/corpus/templates/plan/app/global.css +13 -0
  1336. package/corpus/templates/plan/app/hooks/use-plan-presence.ts +11 -1
  1337. package/corpus/templates/plan/app/lib/plan-access-prompt.ts +74 -0
  1338. package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +1435 -0
  1339. package/corpus/templates/plan/app/lib/plan-comment-editor-helpers.ts +142 -0
  1340. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +376 -0
  1341. package/corpus/templates/plan/app/lib/plan-native-anchors.ts +682 -0
  1342. package/corpus/templates/plan/app/pages/PlansPage.tsx +216 -2747
  1343. package/corpus/templates/plan/app/root.tsx +13 -10
  1344. package/corpus/templates/plan/app/routes/_index.tsx +1 -2
  1345. package/corpus/templates/plan/app/routes/local-plans.$slug.tsx +1 -1
  1346. package/corpus/templates/plan/app/routes/plans.$id.tsx +1 -2
  1347. package/corpus/templates/plan/app/routes/plans.tsx +1 -2
  1348. package/corpus/templates/plan/app/routes/recaps.$id.tsx +1 -2
  1349. package/corpus/templates/plan/app/routes/recaps.tsx +1 -2
  1350. package/corpus/templates/plan/app/routes/settings.tsx +7 -21
  1351. package/corpus/templates/plan/changelog/2026-07-05-plan-pages-open-at-the-top-unless-a-shared-link-targets-a-sp.md +6 -0
  1352. package/corpus/templates/plan/changelog/2026-07-06-fixed-collaborative-plan-editing-so-content-no-longer-duplic.md +6 -0
  1353. package/corpus/templates/plan/changelog/2026-07-06-fixed-visual-reports-using-excessive-cpu-while-rendering-ske.md +6 -0
  1354. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +55 -0
  1355. package/corpus/templates/slides/app/components/ThemeToggle.tsx +6 -6
  1356. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +5 -5
  1357. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +7 -7
  1358. package/corpus/templates/slides/app/components/deck/ExcalidrawSlide.tsx +2 -1
  1359. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +1 -1
  1360. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +3 -2
  1361. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +15 -14
  1362. package/corpus/templates/slides/app/components/editor/AnimationsPanel.tsx +7 -7
  1363. package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +5 -5
  1364. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +6 -6
  1365. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +29 -16
  1366. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -9
  1367. package/corpus/templates/slides/app/components/editor/GenerateSlidesDialog.tsx +6 -5
  1368. package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +12 -12
  1369. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +4 -3
  1370. package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +5 -4
  1371. package/corpus/templates/slides/app/components/editor/ImportButton.tsx +14 -4
  1372. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -1
  1373. package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +10 -10
  1374. package/corpus/templates/slides/app/components/editor/SlideBubbleMenu.tsx +10 -10
  1375. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +6 -6
  1376. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +2 -1
  1377. package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +1 -1
  1378. package/corpus/templates/slides/app/components/layout/Header.tsx +2 -2
  1379. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
  1380. package/corpus/templates/slides/app/components/ui/alert-dialog.tsx +1 -0
  1381. package/corpus/templates/slides/app/components/ui/button.tsx +1 -0
  1382. package/corpus/templates/slides/app/components/ui/card.tsx +1 -0
  1383. package/corpus/templates/slides/app/components/ui/dialog.tsx +1 -0
  1384. package/corpus/templates/slides/app/components/ui/dropdown-menu.tsx +1 -0
  1385. package/corpus/templates/slides/app/components/ui/input.tsx +1 -0
  1386. package/corpus/templates/slides/app/components/ui/label.tsx +1 -0
  1387. package/corpus/templates/slides/app/components/ui/popover.tsx +1 -0
  1388. package/corpus/templates/slides/app/components/ui/scroll-area.tsx +1 -0
  1389. package/corpus/templates/slides/app/components/ui/select.tsx +1 -0
  1390. package/corpus/templates/slides/app/components/ui/separator.tsx +1 -0
  1391. package/corpus/templates/slides/app/components/ui/sheet.tsx +1 -0
  1392. package/corpus/templates/slides/app/components/ui/skeleton.tsx +1 -0
  1393. package/corpus/templates/slides/app/components/ui/spinner.tsx +1 -0
  1394. package/corpus/templates/slides/app/components/ui/textarea.tsx +1 -0
  1395. package/corpus/templates/slides/app/components/ui/toast.tsx +1 -0
  1396. package/corpus/templates/slides/app/components/ui/toaster.tsx +1 -0
  1397. package/corpus/templates/slides/app/components/ui/toggle-group.tsx +1 -0
  1398. package/corpus/templates/slides/app/components/ui/toolkit-provider.tsx +12 -0
  1399. package/corpus/templates/slides/app/components/ui/tooltip.tsx +1 -0
  1400. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +6 -6
  1401. package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +7 -7
  1402. package/corpus/templates/slides/app/global.css +1 -0
  1403. package/corpus/templates/slides/app/hooks/use-toast.ts +1 -0
  1404. package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
  1405. package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -3
  1406. package/corpus/templates/slides/app/pages/DesignSystems.tsx +1 -1
  1407. package/corpus/templates/slides/app/pages/Index.tsx +14 -17
  1408. package/corpus/templates/slides/app/root.tsx +13 -10
  1409. package/corpus/templates/slides/app/routes/settings.tsx +6 -21
  1410. package/corpus/templates/slides/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
  1411. package/corpus/templates/slides/server/handlers/uploads.ts +7 -2
  1412. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  1413. package/dist/agent/durable-background.d.ts +29 -0
  1414. package/dist/agent/durable-background.d.ts.map +1 -1
  1415. package/dist/agent/durable-background.js +46 -0
  1416. package/dist/agent/durable-background.js.map +1 -1
  1417. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  1418. package/dist/agent/engine/ai-sdk-engine.js +7 -2
  1419. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  1420. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  1421. package/dist/agent/engine/anthropic-engine.js +28 -2
  1422. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  1423. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  1424. package/dist/agent/engine/builder-engine.js +1 -1
  1425. package/dist/agent/engine/builder-engine.js.map +1 -1
  1426. package/dist/agent/engine/output-tokens.d.ts +3 -3
  1427. package/dist/agent/engine/output-tokens.d.ts.map +1 -1
  1428. package/dist/agent/engine/output-tokens.js +16 -14
  1429. package/dist/agent/engine/output-tokens.js.map +1 -1
  1430. package/dist/agent/engine/registry.d.ts +3 -3
  1431. package/dist/agent/engine/registry.d.ts.map +1 -1
  1432. package/dist/agent/engine/registry.js +28 -25
  1433. package/dist/agent/engine/registry.js.map +1 -1
  1434. package/dist/agent/engine/translate-ai-sdk.d.ts +14 -2
  1435. package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
  1436. package/dist/agent/engine/translate-ai-sdk.js +33 -18
  1437. package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
  1438. package/dist/agent/engine/translate-anthropic.d.ts.map +1 -1
  1439. package/dist/agent/engine/translate-anthropic.js +42 -1
  1440. package/dist/agent/engine/translate-anthropic.js.map +1 -1
  1441. package/dist/agent/engine/types.d.ts +15 -0
  1442. package/dist/agent/engine/types.d.ts.map +1 -1
  1443. package/dist/agent/engine/types.js.map +1 -1
  1444. package/dist/agent/model-config.d.ts +8 -0
  1445. package/dist/agent/model-config.d.ts.map +1 -1
  1446. package/dist/agent/model-config.js +86 -0
  1447. package/dist/agent/model-config.js.map +1 -1
  1448. package/dist/agent/production-agent.d.ts +91 -5
  1449. package/dist/agent/production-agent.d.ts.map +1 -1
  1450. package/dist/agent/production-agent.js +389 -180
  1451. package/dist/agent/production-agent.js.map +1 -1
  1452. package/dist/agent/runtime-context.d.ts +29 -0
  1453. package/dist/agent/runtime-context.d.ts.map +1 -1
  1454. package/dist/agent/runtime-context.js +40 -11
  1455. package/dist/agent/runtime-context.js.map +1 -1
  1456. package/dist/agent/tool-result-images.d.ts +49 -0
  1457. package/dist/agent/tool-result-images.d.ts.map +1 -0
  1458. package/dist/agent/tool-result-images.js +141 -0
  1459. package/dist/agent/tool-result-images.js.map +1 -0
  1460. package/dist/brand-kit/fig/index.d.ts.map +1 -1
  1461. package/dist/brand-kit/fig/index.js +6 -3
  1462. package/dist/brand-kit/fig/index.js.map +1 -1
  1463. package/dist/client/AgentPanel.d.ts.map +1 -1
  1464. package/dist/client/AgentPanel.js +67 -18
  1465. package/dist/client/AgentPanel.js.map +1 -1
  1466. package/dist/client/AssistantChat.d.ts.map +1 -1
  1467. package/dist/client/AssistantChat.js +72 -40
  1468. package/dist/client/AssistantChat.js.map +1 -1
  1469. package/dist/client/KeepTabOpenNotice.d.ts +55 -0
  1470. package/dist/client/KeepTabOpenNotice.d.ts.map +1 -0
  1471. package/dist/client/KeepTabOpenNotice.js +85 -0
  1472. package/dist/client/KeepTabOpenNotice.js.map +1 -0
  1473. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  1474. package/dist/client/MultiTabAssistantChat.js +2 -1
  1475. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  1476. package/dist/client/RunStuckBanner.d.ts +7 -1
  1477. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  1478. package/dist/client/RunStuckBanner.js +7 -2
  1479. package/dist/client/RunStuckBanner.js.map +1 -1
  1480. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  1481. package/dist/client/chat/run-recovery.js +7 -1
  1482. package/dist/client/chat/run-recovery.js.map +1 -1
  1483. package/dist/client/clipboard.d.ts.map +1 -1
  1484. package/dist/client/clipboard.js +5 -6
  1485. package/dist/client/clipboard.js.map +1 -1
  1486. package/dist/client/components/AgentPresenceChip.d.ts +1 -11
  1487. package/dist/client/components/AgentPresenceChip.d.ts.map +1 -1
  1488. package/dist/client/components/AgentPresenceChip.js +1 -41
  1489. package/dist/client/components/AgentPresenceChip.js.map +1 -1
  1490. package/dist/client/components/LiveCursorOverlay.d.ts +1 -44
  1491. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  1492. package/dist/client/components/LiveCursorOverlay.js +1 -123
  1493. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  1494. package/dist/client/components/PresenceBar.d.ts +1 -26
  1495. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  1496. package/dist/client/components/PresenceBar.js +1 -164
  1497. package/dist/client/components/PresenceBar.js.map +1 -1
  1498. package/dist/client/components/RecentEditHighlights.d.ts +1 -42
  1499. package/dist/client/components/RecentEditHighlights.d.ts.map +1 -1
  1500. package/dist/client/components/RecentEditHighlights.js +1 -145
  1501. package/dist/client/components/RecentEditHighlights.js.map +1 -1
  1502. package/dist/client/components/RemoteSelectionRings.d.ts +1 -51
  1503. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
  1504. package/dist/client/components/RemoteSelectionRings.js +1 -129
  1505. package/dist/client/components/RemoteSelectionRings.js.map +1 -1
  1506. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  1507. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  1508. package/dist/client/composer/TiptapComposer.js +74 -26
  1509. package/dist/client/composer/TiptapComposer.js.map +1 -1
  1510. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
  1511. package/dist/client/db-admin/SqlEditor.js +13 -3
  1512. package/dist/client/db-admin/SqlEditor.js.map +1 -1
  1513. package/dist/client/dynamic-suggestions.d.ts.map +1 -1
  1514. package/dist/client/dynamic-suggestions.js +7 -1
  1515. package/dist/client/dynamic-suggestions.js.map +1 -1
  1516. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  1517. package/dist/client/extensions/ExtensionViewer.js +7 -7
  1518. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  1519. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
  1520. package/dist/client/extensions/ExtensionsListPage.js +5 -7
  1521. package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
  1522. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
  1523. package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
  1524. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
  1525. package/dist/client/host-bridge.d.ts +2 -0
  1526. package/dist/client/host-bridge.d.ts.map +1 -1
  1527. package/dist/client/host-bridge.js +26 -10
  1528. package/dist/client/host-bridge.js.map +1 -1
  1529. package/dist/client/i18n.d.ts +1 -0
  1530. package/dist/client/i18n.d.ts.map +1 -1
  1531. package/dist/client/i18n.js +3 -1
  1532. package/dist/client/i18n.js.map +1 -1
  1533. package/dist/client/index.d.ts +2 -1
  1534. package/dist/client/index.d.ts.map +1 -1
  1535. package/dist/client/index.js +2 -1
  1536. package/dist/client/index.js.map +1 -1
  1537. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  1538. package/dist/client/resources/McpIntegrationDialog.js +12 -31
  1539. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  1540. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +3 -1
  1541. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  1542. package/dist/client/rich-markdown-editor/SharedRichEditor.js +2 -1
  1543. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  1544. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +3 -1
  1545. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  1546. package/dist/client/rich-markdown-editor/useCollabReconcile.js +17 -8
  1547. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  1548. package/dist/client/settings/SettingsPanel.d.ts +3 -1
  1549. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  1550. package/dist/client/settings/SettingsPanel.js +103 -9
  1551. package/dist/client/settings/SettingsPanel.js.map +1 -1
  1552. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  1553. package/dist/client/settings/SettingsTabsPage.js +5 -5
  1554. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  1555. package/dist/client/settings/index.d.ts +1 -1
  1556. package/dist/client/settings/index.d.ts.map +1 -1
  1557. package/dist/client/settings/index.js +1 -1
  1558. package/dist/client/settings/index.js.map +1 -1
  1559. package/dist/client/sharing/VisibilityBadge.d.ts +1 -10
  1560. package/dist/client/sharing/VisibilityBadge.d.ts.map +1 -1
  1561. package/dist/client/sharing/VisibilityBadge.js +1 -18
  1562. package/dist/client/sharing/VisibilityBadge.js.map +1 -1
  1563. package/dist/client/use-action.d.ts +3 -0
  1564. package/dist/client/use-action.d.ts.map +1 -1
  1565. package/dist/client/use-action.js +34 -3
  1566. package/dist/client/use-action.js.map +1 -1
  1567. package/dist/client/use-db-sync.d.ts +4 -0
  1568. package/dist/client/use-db-sync.d.ts.map +1 -1
  1569. package/dist/client/use-db-sync.js +37 -18
  1570. package/dist/client/use-db-sync.js.map +1 -1
  1571. package/dist/client/use-run-stuck-detection.d.ts +8 -1
  1572. package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
  1573. package/dist/client/use-run-stuck-detection.js +3 -2
  1574. package/dist/client/use-run-stuck-detection.js.map +1 -1
  1575. package/dist/coding-tools/run-code.d.ts +36 -0
  1576. package/dist/coding-tools/run-code.d.ts.map +1 -1
  1577. package/dist/coding-tools/run-code.js +333 -62
  1578. package/dist/coding-tools/run-code.js.map +1 -1
  1579. package/dist/coding-tools/sandbox/background.d.ts +141 -0
  1580. package/dist/coding-tools/sandbox/background.d.ts.map +1 -0
  1581. package/dist/coding-tools/sandbox/background.js +279 -0
  1582. package/dist/coding-tools/sandbox/background.js.map +1 -0
  1583. package/dist/coding-tools/sandbox/executions-store.d.ts +138 -0
  1584. package/dist/coding-tools/sandbox/executions-store.d.ts.map +1 -0
  1585. package/dist/coding-tools/sandbox/executions-store.js +380 -0
  1586. package/dist/coding-tools/sandbox/executions-store.js.map +1 -0
  1587. package/dist/coding-tools/sandbox/index.d.ts +22 -13
  1588. package/dist/coding-tools/sandbox/index.d.ts.map +1 -1
  1589. package/dist/coding-tools/sandbox/index.js +31 -13
  1590. package/dist/coding-tools/sandbox/index.js.map +1 -1
  1591. package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts.map +1 -1
  1592. package/dist/coding-tools/sandbox/local-child-process-adapter.js +61 -4
  1593. package/dist/coding-tools/sandbox/local-child-process-adapter.js.map +1 -1
  1594. package/dist/collab/awareness.d.ts +2 -2
  1595. package/dist/collab/awareness.d.ts.map +1 -1
  1596. package/dist/collab/client.d.ts +16 -12
  1597. package/dist/collab/client.d.ts.map +1 -1
  1598. package/dist/collab/client.js +744 -496
  1599. package/dist/collab/client.js.map +1 -1
  1600. package/dist/collab/presence.d.ts +2 -20
  1601. package/dist/collab/presence.d.ts.map +1 -1
  1602. package/dist/collab/presence.js +3 -0
  1603. package/dist/collab/presence.js.map +1 -1
  1604. package/dist/collab/recent-edits.d.ts +2 -37
  1605. package/dist/collab/recent-edits.d.ts.map +1 -1
  1606. package/dist/collab/recent-edits.js +2 -9
  1607. package/dist/collab/recent-edits.js.map +1 -1
  1608. package/dist/collab/struct-routes.d.ts +1 -1
  1609. package/dist/data-programs/actions.d.ts +19 -0
  1610. package/dist/data-programs/actions.d.ts.map +1 -0
  1611. package/dist/data-programs/actions.js +279 -0
  1612. package/dist/data-programs/actions.js.map +1 -0
  1613. package/dist/data-programs/contract.d.ts +70 -0
  1614. package/dist/data-programs/contract.d.ts.map +1 -0
  1615. package/dist/data-programs/contract.js +225 -0
  1616. package/dist/data-programs/contract.js.map +1 -0
  1617. package/dist/data-programs/execute.d.ts +96 -0
  1618. package/dist/data-programs/execute.d.ts.map +1 -0
  1619. package/dist/data-programs/execute.js +454 -0
  1620. package/dist/data-programs/execute.js.map +1 -0
  1621. package/dist/data-programs/index.d.ts +11 -0
  1622. package/dist/data-programs/index.d.ts.map +1 -0
  1623. package/dist/data-programs/index.js +11 -0
  1624. package/dist/data-programs/index.js.map +1 -0
  1625. package/dist/data-programs/schema.d.ts +517 -0
  1626. package/dist/data-programs/schema.d.ts.map +1 -0
  1627. package/dist/data-programs/schema.js +138 -0
  1628. package/dist/data-programs/schema.js.map +1 -0
  1629. package/dist/data-programs/store.d.ts +158 -0
  1630. package/dist/data-programs/store.d.ts.map +1 -0
  1631. package/dist/data-programs/store.js +493 -0
  1632. package/dist/data-programs/store.js.map +1 -0
  1633. package/dist/db/create-get-db.d.ts +25 -0
  1634. package/dist/db/create-get-db.d.ts.map +1 -1
  1635. package/dist/db/create-get-db.js +25 -2
  1636. package/dist/db/create-get-db.js.map +1 -1
  1637. package/dist/deploy/build.d.ts.map +1 -1
  1638. package/dist/deploy/build.js +34 -0
  1639. package/dist/deploy/build.js.map +1 -1
  1640. package/dist/extensions/actions.js +2 -2
  1641. package/dist/extensions/actions.js.map +1 -1
  1642. package/dist/file-upload/builder.d.ts.map +1 -1
  1643. package/dist/file-upload/builder.js +9 -0
  1644. package/dist/file-upload/builder.js.map +1 -1
  1645. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  1646. package/dist/integrations/webhook-handler.js +21 -10
  1647. package/dist/integrations/webhook-handler.js.map +1 -1
  1648. package/dist/local-artifacts/index.js +1 -1
  1649. package/dist/local-artifacts/index.js.map +1 -1
  1650. package/dist/localization/default-messages.d.ts +0 -1
  1651. package/dist/localization/default-messages.d.ts.map +1 -1
  1652. package/dist/localization/default-messages.js +1 -2
  1653. package/dist/localization/default-messages.js.map +1 -1
  1654. package/dist/mcp-client/index.d.ts +9 -0
  1655. package/dist/mcp-client/index.d.ts.map +1 -1
  1656. package/dist/mcp-client/index.js +23 -0
  1657. package/dist/mcp-client/index.js.map +1 -1
  1658. package/dist/observability/routes.d.ts +3 -3
  1659. package/dist/onboarding/default-steps.d.ts.map +1 -1
  1660. package/dist/onboarding/default-steps.js +2 -2
  1661. package/dist/onboarding/default-steps.js.map +1 -1
  1662. package/dist/progress/routes.d.ts +1 -1
  1663. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  1664. package/dist/resources/store.d.ts.map +1 -1
  1665. package/dist/resources/store.js +35 -3
  1666. package/dist/resources/store.js.map +1 -1
  1667. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  1668. package/dist/scripts/agent-engines/list-agent-engines.js +4 -8
  1669. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  1670. package/dist/scripts/agent-engines/test-agent-engine.d.ts.map +1 -1
  1671. package/dist/scripts/agent-engines/test-agent-engine.js +7 -2
  1672. package/dist/scripts/agent-engines/test-agent-engine.js.map +1 -1
  1673. package/dist/scripts/db/tool-mode.js +3 -3
  1674. package/dist/scripts/db/tool-mode.js.map +1 -1
  1675. package/dist/scripts/resources/migrate-learnings.d.ts +5 -1
  1676. package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
  1677. package/dist/scripts/resources/migrate-learnings.js +8 -10
  1678. package/dist/scripts/resources/migrate-learnings.js.map +1 -1
  1679. package/dist/server/agent-chat-plugin.d.ts +20 -6
  1680. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  1681. package/dist/server/agent-chat-plugin.js +138 -59
  1682. package/dist/server/agent-chat-plugin.js.map +1 -1
  1683. package/dist/server/agent-discovery.d.ts.map +1 -1
  1684. package/dist/server/agent-discovery.js +62 -8
  1685. package/dist/server/agent-discovery.js.map +1 -1
  1686. package/dist/server/auth.d.ts.map +1 -1
  1687. package/dist/server/auth.js +75 -3
  1688. package/dist/server/auth.js.map +1 -1
  1689. package/dist/server/builder-design-systems.js +2 -2
  1690. package/dist/server/builder-design-systems.js.map +1 -1
  1691. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  1692. package/dist/server/core-routes-plugin.js +100 -20
  1693. package/dist/server/core-routes-plugin.js.map +1 -1
  1694. package/dist/server/credential-provider.d.ts +6 -3
  1695. package/dist/server/credential-provider.d.ts.map +1 -1
  1696. package/dist/server/credential-provider.js +25 -8
  1697. package/dist/server/credential-provider.js.map +1 -1
  1698. package/dist/server/csrf.d.ts.map +1 -1
  1699. package/dist/server/csrf.js +3 -0
  1700. package/dist/server/csrf.js.map +1 -1
  1701. package/dist/server/framework-request-handler.d.ts.map +1 -1
  1702. package/dist/server/framework-request-handler.js +37 -1
  1703. package/dist/server/framework-request-handler.js.map +1 -1
  1704. package/dist/server/oauth-helpers.d.ts.map +1 -1
  1705. package/dist/server/oauth-helpers.js +9 -1
  1706. package/dist/server/oauth-helpers.js.map +1 -1
  1707. package/dist/server/onboarding-html.d.ts.map +1 -1
  1708. package/dist/server/onboarding-html.js +80 -48
  1709. package/dist/server/onboarding-html.js.map +1 -1
  1710. package/dist/server/prompts/shared-rules.js +2 -2
  1711. package/dist/server/prompts/shared-rules.js.map +1 -1
  1712. package/dist/server/transcribe-voice.d.ts +1 -1
  1713. package/dist/server/transcribe-voice.d.ts.map +1 -1
  1714. package/dist/server/transcribe-voice.js +77 -15
  1715. package/dist/server/transcribe-voice.js.map +1 -1
  1716. package/dist/styles/agent-native.css +57 -45
  1717. package/dist/templates/default/.agents/skills/actions/SKILL.md +55 -0
  1718. package/dist/templates/default/AGENTS.md +11 -14
  1719. package/dist/templates/default/actions/navigate.ts +1 -1
  1720. package/dist/templates/default/app/i18n/ar-SA.ts +0 -1
  1721. package/dist/templates/default/app/i18n/de-DE.ts +0 -1
  1722. package/dist/templates/default/app/i18n/en-US.ts +0 -1
  1723. package/dist/templates/default/app/i18n/es-ES.ts +0 -1
  1724. package/dist/templates/default/app/i18n/fr-FR.ts +0 -1
  1725. package/dist/templates/default/app/i18n/hi-IN.ts +0 -1
  1726. package/dist/templates/default/app/i18n/ja-JP.ts +0 -1
  1727. package/dist/templates/default/app/i18n/ko-KR.ts +0 -1
  1728. package/dist/templates/default/app/i18n/pt-BR.ts +0 -1
  1729. package/dist/templates/default/app/i18n/zh-CN.ts +0 -1
  1730. package/dist/templates/default/app/i18n/zh-TW.ts +0 -1
  1731. package/dist/templates/default/app/routes/settings.tsx +22 -33
  1732. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
  1733. package/dist/templates/headless/AGENTS.md +9 -6
  1734. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
  1735. package/dist/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
  1736. package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
  1737. package/dist/vite/agents-bundle-plugin.d.ts.map +1 -1
  1738. package/dist/vite/agents-bundle-plugin.js +26 -1
  1739. package/dist/vite/agents-bundle-plugin.js.map +1 -1
  1740. package/dist/vite/client.d.ts.map +1 -1
  1741. package/dist/vite/client.js +11 -0
  1742. package/dist/vite/client.js.map +1 -1
  1743. package/docs/content/agent-native-toolkit.mdx +87 -146
  1744. package/docs/content/cloneable-saas.mdx +19 -19
  1745. package/docs/content/components.mdx +16 -4
  1746. package/docs/content/data-programs.mdx +329 -0
  1747. package/docs/content/deployment.mdx +9 -9
  1748. package/docs/content/extensions.mdx +8 -7
  1749. package/docs/content/generative-ui.mdx +5 -3
  1750. package/docs/content/key-concepts.mdx +6 -9
  1751. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +100 -70
  1752. package/docs/content/locales/ar-SA/cloneable-saas.mdx +19 -19
  1753. package/docs/content/locales/ar-SA/components.mdx +12 -2
  1754. package/docs/content/locales/ar-SA/data-programs.mdx +329 -0
  1755. package/docs/content/locales/ar-SA/deployment.mdx +8 -9
  1756. package/docs/content/locales/ar-SA/extensions.mdx +6 -4
  1757. package/docs/content/locales/ar-SA/key-concepts.mdx +7 -9
  1758. package/docs/content/locales/ar-SA/real-time-collaboration.mdx +9 -10
  1759. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +100 -74
  1760. package/docs/content/locales/de-DE/cloneable-saas.mdx +19 -19
  1761. package/docs/content/locales/de-DE/components.mdx +12 -2
  1762. package/docs/content/locales/de-DE/data-programs.mdx +329 -0
  1763. package/docs/content/locales/de-DE/deployment.mdx +8 -9
  1764. package/docs/content/locales/de-DE/extensions.mdx +6 -4
  1765. package/docs/content/locales/de-DE/key-concepts.mdx +8 -10
  1766. package/docs/content/locales/de-DE/real-time-collaboration.mdx +9 -10
  1767. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +100 -74
  1768. package/docs/content/locales/es-ES/cloneable-saas.mdx +19 -19
  1769. package/docs/content/locales/es-ES/components.mdx +12 -2
  1770. package/docs/content/locales/es-ES/data-programs.mdx +329 -0
  1771. package/docs/content/locales/es-ES/deployment.mdx +8 -9
  1772. package/docs/content/locales/es-ES/extensions.mdx +6 -4
  1773. package/docs/content/locales/es-ES/key-concepts.mdx +8 -10
  1774. package/docs/content/locales/es-ES/real-time-collaboration.mdx +9 -10
  1775. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +100 -76
  1776. package/docs/content/locales/fr-FR/cloneable-saas.mdx +19 -19
  1777. package/docs/content/locales/fr-FR/components.mdx +12 -2
  1778. package/docs/content/locales/fr-FR/data-programs.mdx +329 -0
  1779. package/docs/content/locales/fr-FR/deployment.mdx +8 -9
  1780. package/docs/content/locales/fr-FR/extensions.mdx +6 -4
  1781. package/docs/content/locales/fr-FR/key-concepts.mdx +8 -10
  1782. package/docs/content/locales/fr-FR/real-time-collaboration.mdx +9 -10
  1783. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +100 -71
  1784. package/docs/content/locales/hi-IN/cloneable-saas.mdx +19 -19
  1785. package/docs/content/locales/hi-IN/components.mdx +20 -10
  1786. package/docs/content/locales/hi-IN/data-programs.mdx +329 -0
  1787. package/docs/content/locales/hi-IN/deployment.mdx +8 -9
  1788. package/docs/content/locales/hi-IN/extensions.mdx +6 -4
  1789. package/docs/content/locales/hi-IN/key-concepts.mdx +8 -10
  1790. package/docs/content/locales/hi-IN/real-time-collaboration.mdx +9 -10
  1791. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +100 -64
  1792. package/docs/content/locales/ja-JP/cloneable-saas.mdx +19 -19
  1793. package/docs/content/locales/ja-JP/components.mdx +12 -2
  1794. package/docs/content/locales/ja-JP/data-programs.mdx +329 -0
  1795. package/docs/content/locales/ja-JP/deployment.mdx +8 -9
  1796. package/docs/content/locales/ja-JP/extensions.mdx +6 -4
  1797. package/docs/content/locales/ja-JP/key-concepts.mdx +9 -11
  1798. package/docs/content/locales/ja-JP/real-time-collaboration.mdx +9 -10
  1799. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +100 -60
  1800. package/docs/content/locales/ko-KR/cloneable-saas.mdx +19 -19
  1801. package/docs/content/locales/ko-KR/components.mdx +20 -10
  1802. package/docs/content/locales/ko-KR/data-programs.mdx +329 -0
  1803. package/docs/content/locales/ko-KR/deployment.mdx +8 -9
  1804. package/docs/content/locales/ko-KR/extensions.mdx +16 -14
  1805. package/docs/content/locales/ko-KR/key-concepts.mdx +9 -11
  1806. package/docs/content/locales/ko-KR/real-time-collaboration.mdx +9 -10
  1807. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +100 -74
  1808. package/docs/content/locales/pt-BR/cloneable-saas.mdx +19 -19
  1809. package/docs/content/locales/pt-BR/components.mdx +12 -2
  1810. package/docs/content/locales/pt-BR/data-programs.mdx +329 -0
  1811. package/docs/content/locales/pt-BR/deployment.mdx +8 -9
  1812. package/docs/content/locales/pt-BR/extensions.mdx +6 -4
  1813. package/docs/content/locales/pt-BR/key-concepts.mdx +8 -11
  1814. package/docs/content/locales/pt-BR/real-time-collaboration.mdx +9 -10
  1815. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +100 -58
  1816. package/docs/content/locales/zh-CN/cloneable-saas.mdx +19 -19
  1817. package/docs/content/locales/zh-CN/components.mdx +20 -10
  1818. package/docs/content/locales/zh-CN/data-programs.mdx +329 -0
  1819. package/docs/content/locales/zh-CN/deployment.mdx +7 -9
  1820. package/docs/content/locales/zh-CN/extensions.mdx +16 -14
  1821. package/docs/content/locales/zh-CN/key-concepts.mdx +7 -11
  1822. package/docs/content/locales/zh-CN/real-time-collaboration.mdx +10 -11
  1823. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +100 -58
  1824. package/docs/content/locales/zh-TW/cloneable-saas.mdx +19 -19
  1825. package/docs/content/locales/zh-TW/components.mdx +20 -10
  1826. package/docs/content/locales/zh-TW/data-programs.mdx +329 -0
  1827. package/docs/content/locales/zh-TW/deployment.mdx +7 -9
  1828. package/docs/content/locales/zh-TW/extensions.mdx +16 -14
  1829. package/docs/content/locales/zh-TW/key-concepts.mdx +7 -11
  1830. package/docs/content/locales/zh-TW/real-time-collaboration.mdx +10 -11
  1831. package/docs/content/real-time-collaboration.mdx +10 -11
  1832. package/docs/content/toolkit-app-adapters.mdx +46 -0
  1833. package/docs/content/toolkit-collaboration-ui.mdx +48 -0
  1834. package/docs/content/toolkit-sharing-ui.mdx +33 -0
  1835. package/docs/content/toolkit-shell-hooks.mdx +27 -0
  1836. package/docs/content/toolkit-ui.mdx +69 -0
  1837. package/package.json +6 -2
  1838. package/src/templates/default/.agents/skills/actions/SKILL.md +55 -0
  1839. package/src/templates/default/AGENTS.md +11 -14
  1840. package/src/templates/default/actions/navigate.spec.ts +53 -0
  1841. package/src/templates/default/actions/navigate.ts +1 -1
  1842. package/src/templates/default/app/i18n/ar-SA.ts +0 -1
  1843. package/src/templates/default/app/i18n/de-DE.ts +0 -1
  1844. package/src/templates/default/app/i18n/en-US.ts +0 -1
  1845. package/src/templates/default/app/i18n/es-ES.ts +0 -1
  1846. package/src/templates/default/app/i18n/fr-FR.ts +0 -1
  1847. package/src/templates/default/app/i18n/hi-IN.ts +0 -1
  1848. package/src/templates/default/app/i18n/ja-JP.ts +0 -1
  1849. package/src/templates/default/app/i18n/ko-KR.ts +0 -1
  1850. package/src/templates/default/app/i18n/pt-BR.ts +0 -1
  1851. package/src/templates/default/app/i18n/zh-CN.ts +0 -1
  1852. package/src/templates/default/app/i18n/zh-TW.ts +0 -1
  1853. package/src/templates/default/app/routes/settings.tsx +22 -33
  1854. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
  1855. package/src/templates/headless/AGENTS.md +9 -6
  1856. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
  1857. package/src/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
  1858. package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
  1859. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
  1860. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
  1861. package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22928
  1862. package/corpus/templates/design/server/lib/figma-import/clipboard.ts +0 -122
  1863. package/corpus/templates/design/server/lib/figma-import/decode.ts +0 -455
  1864. package/corpus/templates/design/server/lib/figma-import/processor.ts +0 -116
  1865. package/corpus/templates/design/server/lib/figma-import/render-html.ts +0 -397
  1866. package/corpus/templates/design/server/lib/figma-import/types.ts +0 -60
@@ -52,6 +52,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
52
52
  var designCanvasScreenId = __DESIGN_CANVAS_SCREEN_ID__ || "";
53
53
  var designCanvasBoardSurface = !!__DESIGN_CANVAS_BOARD_SURFACE__;
54
54
  var scaleToolEnabled = false;
55
+ // Interaction-state forced preview (phase 2 — see shared/interaction-states.ts's
56
+ // "Forced-preview mechanism" doc comment). Tracks which single node id
57
+ // currently carries the `data-an-state-preview` attribute so a later
58
+ // `state-preview` message for a DIFFERENT node clears the previous one
59
+ // first — only one element can be force-previewing a state at a time,
60
+ // matching the inspector's single-selection InteractionStatePanel.
61
+ var statePreviewNodeId: string | null = null;
55
62
  var editorChromeScaleX = Math.max(
56
63
  0.05,
57
64
  Number(__EDITOR_CHROME_SCALE_X__) || 1,
@@ -627,6 +634,33 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
627
634
  var sourceBacked =
628
635
  hasStableOwnSource(el) || !!closestStableSourceElement(el);
629
636
  var sourceId = sourceBacked ? getSourceId(el) || getSelector(el) : "";
637
+ // Id-on-demand (empty-node-id fix, bridge side): AI-generated screens
638
+ // frequently ship with NO data-agent-native-node-id anywhere, which
639
+ // breaks every id-keyed operation host-side ("Could not move that
640
+ // layer", `Node with data-agent-native-node-id="" not found`). When the
641
+ // element has no stable own id, mint a durable candidate once and expose
642
+ // it as `pendingNodeId` in the payload so the HOST can persist it into
643
+ // the source as the element's real data-agent-native-node-id. The mint
644
+ // is stored under data-an-pending-node-id — an attribute that
645
+ // getSourceId/getSelector/closestStableSourceElement deliberately do NOT
646
+ // read — so until the host persists it, resolution still flows through
647
+ // the existing structural-selector fallback unchanged.
648
+ var pendingNodeId = "";
649
+ if (
650
+ !getSourceId(el) &&
651
+ el !== document.body &&
652
+ el !== document.documentElement &&
653
+ el.getAttribute &&
654
+ el.setAttribute
655
+ ) {
656
+ pendingNodeId = el.getAttribute("data-an-pending-node-id") || "";
657
+ if (!pendingNodeId) {
658
+ pendingNodeId = freshRuntimeNodeId("pending");
659
+ try {
660
+ el.setAttribute("data-an-pending-node-id", pendingNodeId);
661
+ } catch (_err) {}
662
+ }
663
+ }
630
664
  var parentLayout = parentStyles
631
665
  ? {
632
666
  display: parentStyles.display,
@@ -735,6 +769,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
735
769
  componentName: componentName || undefined,
736
770
  id: el.id || undefined,
737
771
  sourceId: sourceId,
772
+ pendingNodeId: pendingNodeId || undefined,
738
773
  selector: getSelector(el),
739
774
  classes: Array.from(el.classList),
740
775
  computedStyles: {
@@ -792,6 +827,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
792
827
  outlineStyle: cs.outlineStyle,
793
828
  outlineColor: cs.outlineColor,
794
829
  outlineOffset: cs.outlineOffset,
830
+ // Text glyph outline (Figma-parity text "Stroke") — CSS has no
831
+ // unprefixed alias, so this is read via the vendor-prefixed
832
+ // longhands directly. See applyStyleEdit/normalizeStyleProperty in
833
+ // shared/code-layer.ts for the matching write-side allow-list entry.
834
+ webkitTextStrokeWidth: (
835
+ cs as unknown as { webkitTextStrokeWidth?: string }
836
+ ).webkitTextStrokeWidth,
837
+ webkitTextStrokeColor: (
838
+ cs as unknown as { webkitTextStrokeColor?: string }
839
+ ).webkitTextStrokeColor,
795
840
  boxShadow: cs.boxShadow,
796
841
  textShadow: cs.textShadow,
797
842
  filter: cs.filter,
@@ -1062,6 +1107,72 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1062
1107
  selectionOverlay.appendChild(spacingOverlay);
1063
1108
  document.body.appendChild(selectionOverlay);
1064
1109
 
1110
+ // ── Gradient edit overlay (in-iframe parity for MultiScreenCanvas's
1111
+ // GradientEditOverlay) ──────────────────────────────────────────────────
1112
+ // Renders the same gradient line + endpoint squares + round stop markers
1113
+ // over an element *inside* this screen's iframe content, driven entirely
1114
+ // by `gradient-edit-target` / `gradient-edit-clear` postMessages from the
1115
+ // parent (DesignEditor forwards its existing `gradientEditTarget` state
1116
+ // for the active screen — see the doc comment on
1117
+ // `gradientEditOverlayTarget` below for the exact wiring contract). Linear
1118
+ // gradients only, matching MultiScreenCanvas's overlay scope: an
1119
+ // unparseable or non-linear `cssValue` renders nothing.
1120
+ //
1121
+ // The math below (gradientLineEndpoints/gradientStopPoints/
1122
+ // angleFromDraggedEndpoint/stopPercentFromDraggedPoint) is a direct port
1123
+ // of the same-named pure functions exported from MultiScreenCanvas.tsx —
1124
+ // this file cannot import them (bridge sources may not import/require
1125
+ // anything, see bridge.guard.spec.ts), so the formulas are duplicated
1126
+ // here verbatim. Keep both copies in sync if the math ever changes.
1127
+ var gradientOverlay = document.createElement("div");
1128
+ gradientOverlay.setAttribute("data-agent-native-edit-overlay", "gradient");
1129
+ gradientOverlay.style.cssText =
1130
+ "position:fixed;z-index:99998;pointer-events:none;display:none;box-sizing:border-box;";
1131
+ var gradientOverlaySvg = document.createElementNS(
1132
+ "http://www.w3.org/2000/svg",
1133
+ "svg",
1134
+ );
1135
+ gradientOverlaySvg.setAttribute("data-gradient-edit-line", "");
1136
+ gradientOverlaySvg.style.cssText =
1137
+ "position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none;";
1138
+ var gradientOverlayLineOutline = document.createElementNS(
1139
+ "http://www.w3.org/2000/svg",
1140
+ "line",
1141
+ );
1142
+ gradientOverlayLineOutline.setAttribute("stroke", "rgba(255,255,255,0.95)");
1143
+ var gradientOverlayLine = document.createElementNS(
1144
+ "http://www.w3.org/2000/svg",
1145
+ "line",
1146
+ );
1147
+ gradientOverlayLine.setAttribute(
1148
+ "stroke",
1149
+ "var(--design-editor-accent-color)",
1150
+ );
1151
+ gradientOverlaySvg.appendChild(gradientOverlayLineOutline);
1152
+ gradientOverlaySvg.appendChild(gradientOverlayLine);
1153
+ gradientOverlay.appendChild(gradientOverlaySvg);
1154
+ var gradientOverlayStartHandle = document.createElement("span");
1155
+ gradientOverlayStartHandle.setAttribute("data-gradient-endpoint", "start");
1156
+ gradientOverlayStartHandle.setAttribute("role", "slider");
1157
+ gradientOverlayStartHandle.setAttribute(
1158
+ "aria-label",
1159
+ "Gradient start" /* i18n-ignore */,
1160
+ );
1161
+ gradientOverlayStartHandle.style.cssText =
1162
+ "position:absolute;pointer-events:auto;cursor:move;border-radius:2px;box-sizing:border-box;background:var(--design-editor-accent-contrast-color);border:1px solid var(--design-editor-accent-color);box-shadow:0 1px 2px rgba(0,0,0,0.3);";
1163
+ var gradientOverlayEndHandle = document.createElement("span");
1164
+ gradientOverlayEndHandle.setAttribute("data-gradient-endpoint", "end");
1165
+ gradientOverlayEndHandle.setAttribute("role", "slider");
1166
+ gradientOverlayEndHandle.setAttribute(
1167
+ "aria-label",
1168
+ "Gradient end" /* i18n-ignore */,
1169
+ );
1170
+ gradientOverlayEndHandle.style.cssText =
1171
+ gradientOverlayStartHandle.style.cssText;
1172
+ gradientOverlay.appendChild(gradientOverlayStartHandle);
1173
+ gradientOverlay.appendChild(gradientOverlayEndHandle);
1174
+ document.body.appendChild(gradientOverlay);
1175
+
1065
1176
  var transformBadge = document.createElement("div");
1066
1177
  transformBadge.setAttribute("data-agent-native-transform-badge", "");
1067
1178
  transformBadge.style.cssText =
@@ -1080,6 +1191,25 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1080
1191
  "position:fixed;z-index:100000;display:none;pointer-events:none;background:var(--design-editor-accent-color);border-radius:999px;box-shadow:0 0 0 1px var(--design-editor-accent-color);";
1081
1192
  document.body.appendChild(insertionGuide);
1082
1193
 
1194
+ // Alignment/smart-guide lines shown while dragging (and resizing) an
1195
+ // element inside the iframe — Figma-style snap-to-sibling guides. Two
1196
+ // shared singleton divs (one per axis) are repositioned per-frame rather
1197
+ // than pooled, matching the insertionGuide convention above. Tagged as an
1198
+ // edit-overlay so elementFromEditorPoint/isOverlayElement never treat a
1199
+ // guide line as a hit-test or drop target. Color matches the overview
1200
+ // canvas's alignment guides (bg-destructive/90) translated to raw CSS.
1201
+ var snapGuideV = document.createElement("div");
1202
+ snapGuideV.setAttribute("data-agent-native-edit-overlay", "snap-guide");
1203
+ snapGuideV.style.cssText =
1204
+ "position:fixed;z-index:100000;display:none;pointer-events:none;width:1px;background:hsl(var(--destructive) / 0.9);";
1205
+ document.body.appendChild(snapGuideV);
1206
+
1207
+ var snapGuideH = document.createElement("div");
1208
+ snapGuideH.setAttribute("data-agent-native-edit-overlay", "snap-guide");
1209
+ snapGuideH.style.cssText =
1210
+ "position:fixed;z-index:100000;display:none;pointer-events:none;height:1px;background:hsl(var(--destructive) / 0.9);";
1211
+ document.body.appendChild(snapGuideH);
1212
+
1083
1213
  var measurementOverlay = document.createElement("div");
1084
1214
  measurementOverlay.setAttribute("data-agent-native-measurement-overlay", "");
1085
1215
  measurementOverlay.style.cssText =
@@ -1137,7 +1267,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1137
1267
  } catch (_err) {}
1138
1268
  });
1139
1269
 
1140
- function updateComponentTag(el: Element | null): void {
1270
+ function updateComponentTag(el: Element | null, knownRect?: DOMRect): void {
1141
1271
  if (!el) {
1142
1272
  clearComponentTag();
1143
1273
  return;
@@ -1157,17 +1287,27 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1157
1287
  componentTagOverlay.setAttribute("data-component-node-id", nodeId);
1158
1288
  componentTagOverlay.setAttribute("data-component-name", compName);
1159
1289
 
1160
- var rect = el.getBoundingClientRect();
1161
- var tagHeight = 22;
1162
- var tagTop = rect.top - tagHeight - 4;
1163
- if (tagTop < 4) tagTop = rect.top + 4;
1290
+ // Reuse the caller's fresh rect when available: positionOverlay() already
1291
+ // read this element's rect this frame, and an extra getBoundingClientRect
1292
+ // here is a second forced layout when overlay styles were just written.
1293
+ var rect = knownRect || el.getBoundingClientRect();
1294
+ // Constant-screen-size chrome: pill font/padding/offsets and the
1295
+ // component-root outline compensate for the host's iframe scale.
1296
+ var line = chromeLineScale();
1297
+ var tagHeight = 22 * line;
1298
+ var tagTop = rect.top - tagHeight - 4 * line;
1299
+ if (tagTop < 4 * line) tagTop = rect.top + 4 * line;
1164
1300
  componentTagOverlay.style.display = "block";
1301
+ componentTagOverlay.style.fontSize = 11 * line + "px";
1302
+ componentTagOverlay.style.padding = 2 * line + "px " + 6 * line + "px";
1303
+ componentTagOverlay.style.borderRadius = 4 * line + "px";
1304
+ componentTagOverlay.style.borderWidth = 1 * line + "px";
1165
1305
  componentTagOverlay.style.left = rect.left + "px";
1166
1306
  componentTagOverlay.style.top = tagTop + "px";
1167
1307
  // Purple outline on the selection overlay distinguishes component roots.
1168
1308
  selectionOverlay.style.outline =
1169
- "2px solid " + chromeStrongColorForElement(el);
1170
- selectionOverlay.style.outlineOffset = "2px";
1309
+ 2 * line + "px solid " + chromeStrongColorForElement(el);
1310
+ selectionOverlay.style.outlineOffset = 2 * line + "px";
1171
1311
  }
1172
1312
 
1173
1313
  function clearComponentTag(): void {
@@ -1311,6 +1451,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1311
1451
  var lastSpacingPointerPoint: { x: number; y: number } | null = null;
1312
1452
  var spacingHandleStateByKey: Record<string, { value: number }> = {};
1313
1453
  var spacingHandleNodesByKey: Record<string, Element> = {};
1454
+ var spacingHatchNodesByKey: Record<string, Element> = {};
1314
1455
  var spacingOverlayRenderKey = "";
1315
1456
  var activeDragCancel: (() => boolean) | null = null;
1316
1457
  var activeCrossScreenStyleSnapshot: unknown | undefined = undefined;
@@ -1424,7 +1565,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1424
1565
  var pos = handle.getAttribute("data-corner") || "";
1425
1566
  handle.style.width = 7 * sx + "px";
1426
1567
  handle.style.height = 7 * sy + "px";
1427
- handle.style.borderWidth = Math.max(1, 1 * line) + "px";
1568
+ handle.style.borderWidth = 1 * line + "px";
1428
1569
  if (pos.indexOf("n") !== -1) handle.style.top = -4 * sy + "px";
1429
1570
  if (pos.indexOf("s") !== -1) handle.style.bottom = -4 * sy + "px";
1430
1571
  if (pos.indexOf("w") !== -1) handle.style.left = -4 * sx + "px";
@@ -1734,6 +1875,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1734
1875
  spacingOverlay.innerHTML = "";
1735
1876
  spacingHandleStateByKey = {};
1736
1877
  spacingHandleNodesByKey = {};
1878
+ spacingHatchNodesByKey = {};
1737
1879
  spacingOverlayRenderKey = "";
1738
1880
  if (!spacingDrag) spacingBadge.style.display = "none";
1739
1881
  }
@@ -1777,14 +1919,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1777
1919
  var stripe =
1778
1920
  kind === "gap" ? "rgba(255, 79, 216, 0.58)" : "rgba(46, 168, 255, 0.52)";
1779
1921
  var angle = orientation === "vertical" ? "135deg" : "45deg";
1922
+ // Constant-screen-size chrome: stripe density compensates for the host's
1923
+ // iframe scale so the hatch pattern reads identically at any canvas zoom
1924
+ // instead of blurring together at low zoom.
1925
+ var scale = chromeLineScale();
1780
1926
  return (
1781
1927
  "repeating-linear-gradient(" +
1782
1928
  angle +
1783
1929
  ", " +
1784
1930
  stripe +
1785
- " 0 1px, " +
1931
+ " 0 " +
1932
+ 1 * scale +
1933
+ "px, " +
1786
1934
  tint +
1787
- " 1px 4px, transparent 4px 7px)"
1935
+ " " +
1936
+ 1 * scale +
1937
+ "px " +
1938
+ 4 * scale +
1939
+ "px, transparent " +
1940
+ 4 * scale +
1941
+ "px " +
1942
+ 7 * scale +
1943
+ "px)"
1788
1944
  );
1789
1945
  }
1790
1946
 
@@ -1794,6 +1950,43 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1794
1950
  return Math.max(0, Math.min(999, rounded));
1795
1951
  }
1796
1952
 
1953
+ // Figma-style handle hit area: only the small handle *line* itself (plus a
1954
+ // few px of pointer tolerance) should start a padding drag. The rest of the
1955
+ // padding band must fall through to normal element move/select — dragging
1956
+ // anywhere else inside the element (even inside the padding region) moves
1957
+ // the element, it does not resize padding. Gap handles keep the previous
1958
+ // full-region hit area (out of scope for this fix; not covered by the
1959
+ // reported UX regression). Base tolerance is in editor-chrome (unscaled)
1960
+ // pixels; callers multiply by chromeLineScale() so the hit area keeps a
1961
+ // constant on-screen size regardless of canvas zoom, matching how the
1962
+ // handle line's own thickness (chromeLineScale()) is derived.
1963
+ var PADDING_HANDLE_HIT_TOLERANCE_BASE = 4;
1964
+
1965
+ function hitRectForPaddingHandle(
1966
+ line: { x: number; y: number; width: number; height: number } | undefined,
1967
+ region: { x: number; y: number; width: number; height: number },
1968
+ tolerance: number,
1969
+ ): { x: number; y: number; width: number; height: number } {
1970
+ if (!line) return region;
1971
+ var minX = Math.min(line.x, region.x);
1972
+ var minY = Math.min(line.y, region.y);
1973
+ var maxX = Math.max(line.x + line.width, region.x + region.width);
1974
+ var maxY = Math.max(line.y + line.height, region.y + region.height);
1975
+ // Only the line itself matters for hit-testing; expand just the line's own
1976
+ // rect by the tolerance, then clamp to the padding region bounds so the
1977
+ // hit area never spills outside the visual padding band.
1978
+ var hitX = Math.max(minX, line.x - tolerance);
1979
+ var hitY = Math.max(minY, line.y - tolerance);
1980
+ var hitRight = Math.min(maxX, line.x + line.width + tolerance);
1981
+ var hitBottom = Math.min(maxY, line.y + line.height + tolerance);
1982
+ return {
1983
+ x: hitX,
1984
+ y: hitY,
1985
+ width: Math.max(1, hitRight - hitX),
1986
+ height: Math.max(1, hitBottom - hitY),
1987
+ };
1988
+ }
1989
+
1797
1990
  function makeSpacingHandle(config: {
1798
1991
  key: string;
1799
1992
  groupKey?: string;
@@ -1804,10 +1997,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1804
1997
  orientation: string;
1805
1998
  value: number;
1806
1999
  region: { x: number; y: number; width: number; height: number };
1807
- line?: unknown;
2000
+ line?: { x: number; y: number; width: number; height: number };
1808
2001
  }): unknown {
1809
2002
  var region = config.region;
1810
2003
  if (!region || region.width <= 0 || region.height <= 0) return null;
2004
+ var roundedRegion = {
2005
+ x: Math.round(region.x),
2006
+ y: Math.round(region.y),
2007
+ width: Math.max(1, Math.round(region.width)),
2008
+ height: Math.max(1, Math.round(region.height)),
2009
+ };
2010
+ var hit =
2011
+ config.kind === "padding"
2012
+ ? hitRectForPaddingHandle(
2013
+ config.line,
2014
+ roundedRegion,
2015
+ PADDING_HANDLE_HIT_TOLERANCE_BASE * chromeLineScale(),
2016
+ )
2017
+ : roundedRegion;
1811
2018
  return {
1812
2019
  key: config.key,
1813
2020
  groupKey: config.groupKey || config.key,
@@ -1817,12 +2024,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1817
2024
  side: config.side || "",
1818
2025
  orientation: config.orientation,
1819
2026
  value: clampSpacingValue(config.value),
1820
- region: {
1821
- x: Math.round(region.x),
1822
- y: Math.round(region.y),
1823
- width: Math.max(1, Math.round(region.width)),
1824
- height: Math.max(1, Math.round(region.height)),
1825
- },
2027
+ region: roundedRegion,
2028
+ hit: hit,
1826
2029
  line: config.line,
1827
2030
  };
1828
2031
  }
@@ -1876,7 +2079,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1876
2079
  var paddingLeft = readPx(cs.paddingLeft);
1877
2080
  var sx = chromeScaleX();
1878
2081
  var sy = chromeScaleY();
1879
- var line = Math.max(1, chromeLineScale());
2082
+ var line = chromeLineScale();
1880
2083
  var hLineWidth = Math.max(6, Math.min(18, rect.width * 0.12)) * sx;
1881
2084
  var vLineHeight = Math.max(6, Math.min(18, rect.height * 0.12)) * sy;
1882
2085
  var innerLeft = borderLeft;
@@ -1996,7 +2199,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
1996
2199
  var handles = [];
1997
2200
  var sx = chromeScaleX();
1998
2201
  var sy = chromeScaleY();
1999
- var line = Math.max(1, chromeLineScale());
2202
+ var line = chromeLineScale();
2000
2203
  var hLineWidth = 8 * sx;
2001
2204
  var vLineHeight = 8 * sy;
2002
2205
  var isFlex = cs.display === "flex" || cs.display === "inline-flex";
@@ -2104,6 +2307,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2104
2307
  );
2105
2308
  }
2106
2309
 
2310
+ // Figma-style live value readout for the padding handle: shown while
2311
+ // hovering OR dragging the handle line, positioned ~12px above and to the
2312
+ // right of the pointer (matching showTransformBadge's cursor-relative
2313
+ // offset idiom, but anchored to the badge's bottom-left corner via
2314
+ // translateY(-100%) so the box actually sits above the cursor instead of
2315
+ // growing downward through it) and live-updating as the value changes.
2316
+ // Falls back to the handle-region center when no cursor point is known yet
2317
+ // (e.g. a hover activated programmatically rather than by a pointer move).
2107
2318
  function showSpacingBadgeForHandle(
2108
2319
  handle: {
2109
2320
  key: string;
@@ -2118,20 +2329,41 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2118
2329
  line: { x: number; y: number; width: number; height: number } | undefined;
2119
2330
  } | null,
2120
2331
  value: number,
2332
+ cursorPoint?: { x: number; y: number } | null,
2121
2333
  ): void {
2122
2334
  if (!selectedEl || !handle) {
2123
2335
  spacingBadge.style.display = "none";
2124
2336
  return;
2125
2337
  }
2126
- var rect = selectedEl.getBoundingClientRect();
2127
- var x = rect.left + handle.region.x + handle.region.width / 2;
2128
- var y = rect.top + handle.region.y + handle.region.height / 2;
2129
- spacingBadge.textContent = String(clampSpacingValue(value));
2338
+ // Constant-screen-size chrome: the host CSS-scales this iframe by the
2339
+ // canvas zoom, so every intrinsic size here (font, padding, radius,
2340
+ // cursor offset) multiplies by chromeLineScale() to render at the same
2341
+ // apparent size at any zoom — the badge was previously unscaled, which
2342
+ // made it microscopic at low overview zooms (the "value box never
2343
+ // appears" report) and oversized when zoomed in.
2344
+ var line = chromeLineScale();
2345
+ var point = cursorPoint || lastSpacingPointerPoint;
2346
+ var x: number;
2347
+ var y: number;
2348
+ if (point) {
2349
+ x = point.x + 12 * line;
2350
+ y = point.y - 12 * line;
2351
+ } else {
2352
+ var rect = selectedEl.getBoundingClientRect();
2353
+ x = rect.left + handle.region.x + handle.region.width / 2;
2354
+ y = rect.top + handle.region.y + handle.region.height / 2;
2355
+ }
2356
+ spacingBadge.textContent = String(clampSpacingValue(value)) + "px";
2130
2357
  spacingBadge.style.display = "block";
2131
2358
  spacingBadge.style.background = spacingColor(handle.kind);
2359
+ spacingBadge.style.fontSize = 10 * line + "px";
2360
+ spacingBadge.style.padding = 2 * line + "px " + 4 * line + "px";
2361
+ spacingBadge.style.borderRadius = 3 * line + "px";
2132
2362
  spacingBadge.style.left = x + "px";
2133
2363
  spacingBadge.style.top = y + "px";
2134
- spacingBadge.style.transform = "translate(-50%, -50%)";
2364
+ spacingBadge.style.transform = point
2365
+ ? "translateY(-100%)"
2366
+ : "translate(-50%, -50%)";
2135
2367
  }
2136
2368
 
2137
2369
  function renderSpacingHandle(
@@ -2145,13 +2377,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2145
2377
  orientation: string;
2146
2378
  value: number;
2147
2379
  region: { x: number; y: number; width: number; height: number };
2380
+ hit: { x: number; y: number; width: number; height: number };
2148
2381
  line: { x: number; y: number; width: number; height: number } | undefined;
2149
2382
  } | null,
2150
2383
  activeGroupKeys: Record<string, boolean>,
2384
+ hoverGroupKeys: Record<string, boolean>,
2151
2385
  ): void {
2152
2386
  if (!handle) return;
2153
2387
  spacingHandleStateByKey[handle.key] = handle;
2154
- var highlighted = Boolean(activeGroupKeys[handle.groupKey]);
2388
+ var active = Boolean(activeGroupKeys[handle.groupKey]);
2389
+ var hovered = Boolean(hoverGroupKeys[handle.groupKey]);
2155
2390
  var lineNode = document.createElement("span");
2156
2391
  lineNode.setAttribute("data-agent-native-spacing-line", handle.kind);
2157
2392
  lineNode.style.position = "absolute";
@@ -2160,11 +2395,42 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2160
2395
  lineNode.style.borderRadius = "999px";
2161
2396
  lineNode.style.left = handle.line.x + "px";
2162
2397
  lineNode.style.top = handle.line.y + "px";
2163
- lineNode.style.width = Math.max(1, handle.line.width) + "px";
2164
- lineNode.style.height = Math.max(1, handle.line.height) + "px";
2398
+ // No 1px floor: at zoom > 100% the pill's thickness is intentionally
2399
+ // sub-1px in iframe space (thickness * host scale = constant screen px).
2400
+ lineNode.style.width = handle.line.width + "px";
2401
+ lineNode.style.height = handle.line.height + "px";
2165
2402
  lineNode.style.background = spacingColor(handle.kind);
2166
2403
  spacingOverlay.appendChild(lineNode);
2167
2404
 
2405
+ // Visual-only hatch band over the full padding region. Purely decorative
2406
+ // (pointer-events: none) — it must never intercept clicks, since only the
2407
+ // small hit node below is allowed to start a padding drag. Hatch is a
2408
+ // hover affordance only: it shows the band the user is about to resize,
2409
+ // and disappears the instant a drag starts (kind === "padding" only, per
2410
+ // the reported regression; gap handles are out of scope for this fix).
2411
+ // Constant-screen-size chrome: tile the hatch pattern at a size that
2412
+ // compensates for the host's iframe scale (matches spacingFill's scaled
2413
+ // stripe stops — a fixed 6px tile would clip the scaled pattern).
2414
+ var hatchTile = 6 * chromeLineScale() + "px";
2415
+ if (handle.kind === "padding") {
2416
+ var hatchNode = document.createElement("span");
2417
+ hatchNode.setAttribute("data-agent-native-spacing-hatch", handle.kind);
2418
+ hatchNode.style.position = "absolute";
2419
+ hatchNode.style.display = "block";
2420
+ hatchNode.style.boxSizing = "border-box";
2421
+ hatchNode.style.pointerEvents = "none";
2422
+ hatchNode.style.backgroundSize = hatchTile + " " + hatchTile;
2423
+ hatchNode.style.left = handle.region.x + "px";
2424
+ hatchNode.style.top = handle.region.y + "px";
2425
+ hatchNode.style.width = handle.region.width + "px";
2426
+ hatchNode.style.height = handle.region.height + "px";
2427
+ hatchNode.style.background = hovered
2428
+ ? spacingFill(handle.kind, handle.orientation)
2429
+ : "transparent";
2430
+ spacingHatchNodesByKey[handle.key] = hatchNode;
2431
+ spacingOverlay.appendChild(hatchNode);
2432
+ }
2433
+
2168
2434
  var regionNode = document.createElement("span");
2169
2435
  regionNode.setAttribute("data-agent-native-spacing-region", handle.kind);
2170
2436
  regionNode.setAttribute("data-orientation", handle.orientation);
@@ -2173,19 +2439,26 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2173
2439
  regionNode.style.display = "block";
2174
2440
  regionNode.style.boxSizing = "border-box";
2175
2441
  regionNode.style.pointerEvents = "auto";
2176
- regionNode.style.backgroundSize = "6px 6px";
2442
+ regionNode.style.backgroundSize = hatchTile + " " + hatchTile;
2177
2443
  regionNode.style.cursor =
2178
2444
  handle.orientation === "vertical" ? "ew-resize" : "ns-resize";
2179
- regionNode.style.left = handle.region.x + "px";
2180
- regionNode.style.top = handle.region.y + "px";
2181
- regionNode.style.width = handle.region.width + "px";
2182
- regionNode.style.height = handle.region.height + "px";
2183
- regionNode.style.background = highlighted
2184
- ? spacingFill(handle.kind, handle.orientation)
2185
- : "transparent";
2186
- regionNode.style.outline = highlighted
2187
- ? "1px solid " + spacingColor(handle.kind)
2188
- : "0";
2445
+ var hitRect = handle.kind === "padding" ? handle.hit : handle.region;
2446
+ regionNode.style.left = hitRect.x + "px";
2447
+ regionNode.style.top = hitRect.y + "px";
2448
+ regionNode.style.width = hitRect.width + "px";
2449
+ regionNode.style.height = hitRect.height + "px";
2450
+ // The gap-handle band keeps its previous always-tintable full-region
2451
+ // background (unaffected by this fix); padding handles no longer paint
2452
+ // the hatch on this node — buildSpacingHandles' dedicated hatchNode above
2453
+ // owns that so it can stay outside the (now much smaller) hit area.
2454
+ regionNode.style.background =
2455
+ handle.kind !== "padding" && active
2456
+ ? spacingFill(handle.kind, handle.orientation)
2457
+ : "transparent";
2458
+ regionNode.style.outline =
2459
+ handle.kind !== "padding" && active
2460
+ ? "1px solid " + spacingColor(handle.kind)
2461
+ : "0";
2189
2462
  regionNode.style.outlineOffset = "-1px";
2190
2463
  regionNode.addEventListener(
2191
2464
  "pointerdown",
@@ -2215,18 +2488,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2215
2488
  orientation: string;
2216
2489
  } | null)[],
2217
2490
  activeGroupKeys: Record<string, boolean>,
2491
+ hoverGroupKeys: Record<string, boolean>,
2218
2492
  ): void {
2219
2493
  handles.forEach(function (handle) {
2220
2494
  if (!handle) return;
2495
+ var active = Boolean(activeGroupKeys[handle.groupKey]);
2496
+ var hovered = Boolean(hoverGroupKeys[handle.groupKey]);
2221
2497
  var regionNode = spacingHandleNodesByKey[handle.key];
2222
- if (!regionNode) return;
2223
- var highlighted = Boolean(activeGroupKeys[handle.groupKey]);
2224
- (regionNode as HTMLElement).style.background = highlighted
2225
- ? spacingFill(handle.kind, handle.orientation)
2226
- : "transparent";
2227
- (regionNode as HTMLElement).style.outline = highlighted
2228
- ? "1px solid " + spacingColor(handle.kind)
2229
- : "0";
2498
+ if (regionNode) {
2499
+ var gapHighlighted = handle.kind !== "padding" && active;
2500
+ (regionNode as HTMLElement).style.background = gapHighlighted
2501
+ ? spacingFill(handle.kind, handle.orientation)
2502
+ : "transparent";
2503
+ (regionNode as HTMLElement).style.outline = gapHighlighted
2504
+ ? "1px solid " + spacingColor(handle.kind)
2505
+ : "0";
2506
+ }
2507
+ var hatchNode = spacingHatchNodesByKey[handle.key];
2508
+ if (hatchNode) {
2509
+ (hatchNode as HTMLElement).style.background = hovered
2510
+ ? spacingFill(handle.kind, handle.orientation)
2511
+ : "transparent";
2512
+ }
2230
2513
  });
2231
2514
  }
2232
2515
 
@@ -2261,6 +2544,39 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2261
2544
  return activeGroupKeys;
2262
2545
  }
2263
2546
 
2547
+ // Figma-parity: the diagonal hatch fill over the padding band is a
2548
+ // hover-only VISUAL affordance layered on top of handles that are always
2549
+ // mounted and hit-testable for the selected element (mounting itself is
2550
+ // never gated on hover — see buildSpacingHandles/renderSpacingHandle, which
2551
+ // render every handle unconditionally). The hatch must be visible while the
2552
+ // pointer rests over the handle line (so the user can see the full padding
2553
+ // band they are about to resize) and hidden the instant an actual drag
2554
+ // starts — during the drag only the live value badge communicates the
2555
+ // current amount. This is intentionally a separate concept from
2556
+ // activeSpacingGroupKeys (which mirrors the opposite side during an
2557
+ // alt-drag and still applies while dragging) — hover-hatch and drag-mirror
2558
+ // never overlap in time because a drag suppresses hover state (see
2559
+ // startSpacingDrag).
2560
+ function hoverSpacingGroupKeys(
2561
+ handles: ({
2562
+ key: string;
2563
+ groupKey: string;
2564
+ } | null)[],
2565
+ ): Record<string, boolean> {
2566
+ var hoverGroupKeys: Record<string, boolean> = {};
2567
+ if (spacingDrag) return hoverGroupKeys;
2568
+ var hoveredKey = hoveredSpacingHandleKey;
2569
+ if (!hoveredKey) return hoverGroupKeys;
2570
+ var handleByKey: Record<string, { groupKey: string }> = {};
2571
+ handles.forEach(function (handle) {
2572
+ if (!handle) return;
2573
+ handleByKey[handle.key] = handle;
2574
+ });
2575
+ var hoveredHandle = handleByKey[hoveredKey];
2576
+ if (hoveredHandle) hoverGroupKeys[hoveredHandle.groupKey] = true;
2577
+ return hoverGroupKeys;
2578
+ }
2579
+
2264
2580
  function updateSpacingOverlay(el: Element | null): void {
2265
2581
  if (el && el !== selectedEl) {
2266
2582
  hideSpacingOverlay();
@@ -2277,6 +2593,9 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2277
2593
  }
2278
2594
  var activeHandle = spacingDrag ? spacingDrag.handle : null;
2279
2595
  var activeGroupKeys = activeSpacingGroupKeys(handles, activeHandle);
2596
+ var hoverGroupKeys = hoverSpacingGroupKeys(handles);
2597
+ var badgeHandle =
2598
+ activeHandle || (spacingDrag ? null : hoveredHandleFor(handles));
2280
2599
  var nextRenderKey = handles
2281
2600
  .map(function (handle) {
2282
2601
  return [
@@ -2297,11 +2616,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2297
2616
  spacingOverlay.style.display === "block" &&
2298
2617
  spacingOverlayRenderKey === nextRenderKey
2299
2618
  ) {
2300
- updateSpacingHandleHighlights(handles, activeGroupKeys);
2301
- if (activeHandle) {
2619
+ updateSpacingHandleHighlights(handles, activeGroupKeys, hoverGroupKeys);
2620
+ if (badgeHandle) {
2302
2621
  showSpacingBadgeForHandle(
2303
- activeHandle,
2304
- spacingDrag ? spacingDrag.currentValue : activeHandle.value,
2622
+ badgeHandle,
2623
+ activeHandle && spacingDrag
2624
+ ? spacingDrag.currentValue
2625
+ : badgeHandle.value,
2305
2626
  );
2306
2627
  } else {
2307
2628
  spacingBadge.style.display = "none";
@@ -2313,19 +2634,41 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2313
2634
  spacingOverlay.innerHTML = "";
2314
2635
  spacingHandleStateByKey = {};
2315
2636
  spacingHandleNodesByKey = {};
2637
+ spacingHatchNodesByKey = {};
2316
2638
  handles.forEach(function (handle) {
2317
- renderSpacingHandle(handle, activeGroupKeys);
2639
+ renderSpacingHandle(handle, activeGroupKeys, hoverGroupKeys);
2318
2640
  });
2319
- if (activeHandle) {
2641
+ if (badgeHandle) {
2320
2642
  showSpacingBadgeForHandle(
2321
- activeHandle,
2322
- spacingDrag ? spacingDrag.currentValue : activeHandle.value,
2643
+ badgeHandle,
2644
+ activeHandle && spacingDrag
2645
+ ? spacingDrag.currentValue
2646
+ : badgeHandle.value,
2323
2647
  );
2324
2648
  } else {
2325
2649
  spacingBadge.style.display = "none";
2326
2650
  }
2327
2651
  }
2328
2652
 
2653
+ // Resolves the handle object matching hoveredSpacingHandleKey, if any — used
2654
+ // to keep the value badge visible on hover (not just during an active drag)
2655
+ // per the padding-handle UX fix: hovering the handle line shows the live
2656
+ // "Npx" readout, dragging keeps showing it with the in-progress value. Note
2657
+ // this only affects which handle drives the *badge* — every handle stays
2658
+ // mounted/hit-testable regardless of hover (see buildSpacingHandles).
2659
+ function hoveredHandleFor(
2660
+ handles: ({ key: string } | null)[],
2661
+ ): { key: string } | null {
2662
+ var hoveredKey = hoveredSpacingHandleKey;
2663
+ if (!hoveredKey) return null;
2664
+ var handleByKey: Record<string, { key: string }> = {};
2665
+ handles.forEach(function (handle) {
2666
+ if (!handle) return;
2667
+ handleByKey[handle.key] = handle;
2668
+ });
2669
+ return handleByKey[hoveredKey] || null;
2670
+ }
2671
+
2329
2672
  function spacingKeyFromTarget(target: Element | null): string {
2330
2673
  var region =
2331
2674
  target && target.closest
@@ -2375,6 +2718,40 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2375
2718
  activateSpacingHandle(spacingKey);
2376
2719
  }
2377
2720
 
2721
+ // Geometry-based fallback for the padding/gap handle hover: resolves the
2722
+ // handle whose hit rect (line + scaled tolerance zone) contains the given
2723
+ // client point, using the handle state captured at the last overlay
2724
+ // render. The event-target path above (spacingKeyFromTarget) only fires
2725
+ // when the pointermove's target IS the region node — which depends on
2726
+ // overlay z-order and event routing; this direct hit test makes the
2727
+ // hover badge reliable from the shield's pointermove too, so hovering
2728
+ // anywhere on the handle line (with its tolerance zone) always shows the
2729
+ // "Npx" value box.
2730
+ function spacingHandleKeyAtPoint(clientX: number, clientY: number): string {
2731
+ if (!selectedEl || !document.documentElement.contains(selectedEl)) {
2732
+ return "";
2733
+ }
2734
+ var rect = selectedEl.getBoundingClientRect();
2735
+ var localX = clientX - rect.left;
2736
+ var localY = clientY - rect.top;
2737
+ var keys = Object.keys(spacingHandleStateByKey);
2738
+ for (var i = 0; i < keys.length; i += 1) {
2739
+ var handle = spacingHandleStateByKey[keys[i]];
2740
+ if (!handle) continue;
2741
+ var hit = handle.hit || handle.region;
2742
+ if (!hit) continue;
2743
+ if (
2744
+ localX >= hit.x &&
2745
+ localX <= hit.x + hit.width &&
2746
+ localY >= hit.y &&
2747
+ localY <= hit.y + hit.height
2748
+ ) {
2749
+ return handle.key;
2750
+ }
2751
+ }
2752
+ return "";
2753
+ }
2754
+
2378
2755
  function spacingRegionFromPoint(
2379
2756
  clientX: number,
2380
2757
  clientY: number,
@@ -2450,9 +2827,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2450
2827
  var sx = chromeScaleX();
2451
2828
  var sy = chromeScaleY();
2452
2829
  var line = chromeLineScale();
2453
- highlightOverlay.style.borderWidth = 1.5 * line + "px";
2454
- parentAutoLayoutOverlay.style.borderWidth = Math.max(1, 1 * line) + "px";
2830
+ // Figma parity: the hover outline is visibly thinner than the selection
2831
+ // outline hover is a light "you could select this" hint, selection is
2832
+ // the stronger confirmed-state chrome. Matches the overview canvas's
2833
+ // hover (1 * chromeScale) vs selection (1.5 * chromeScale) ratio.
2834
+ highlightOverlay.style.borderWidth = 1 * line + "px";
2835
+ parentAutoLayoutOverlay.style.borderWidth = 1 * line + "px";
2455
2836
  selectionOverlay.style.borderWidth = 1.5 * line + "px";
2837
+ marqueeSelectionOverlay.style.borderWidth = 1 * line + "px";
2456
2838
  passiveSelectionOverlays.forEach(scalePassiveSelectionOverlay);
2457
2839
  if (selectedEl) updateSpacingOverlay(selectedEl);
2458
2840
 
@@ -2476,7 +2858,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2476
2858
  var pos = handle.getAttribute("data-agent-native-edit-handle") || "";
2477
2859
  handle.style.width = 7 * sx + "px";
2478
2860
  handle.style.height = 7 * sy + "px";
2479
- handle.style.borderWidth = Math.max(1, 1 * line) + "px";
2861
+ handle.style.borderWidth = 1 * line + "px";
2480
2862
  if (pos.indexOf("n") !== -1) handle.style.top = -4 * sy + "px";
2481
2863
  if (pos.indexOf("s") !== -1) handle.style.bottom = -4 * sy + "px";
2482
2864
  if (pos.indexOf("w") !== -1) handle.style.left = -4 * sx + "px";
@@ -2553,7 +2935,9 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2553
2935
  if (overlay === selectionOverlay) {
2554
2936
  applySelectionChrome(el);
2555
2937
  updateSpacingOverlay(el);
2556
- updateComponentTag(el);
2938
+ // `rect` is undefined on the rotated-local-box path; updateComponentTag
2939
+ // falls back to its own read in that case.
2940
+ updateComponentTag(el, rect);
2557
2941
  updateParentAutoLayoutOverlay(el);
2558
2942
  } else {
2559
2943
  applyElementOverlayChrome(overlay, el);
@@ -2591,6 +2975,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2591
2975
  var overlay = passiveSelectionOverlays[index];
2592
2976
  if (overlay) positionOverlay(overlay, el);
2593
2977
  });
2978
+ positionGradientOverlay();
2594
2979
  syncOverlayObservers();
2595
2980
  }
2596
2981
 
@@ -2699,6 +3084,21 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2699
3084
  var horizontal = Math.abs(x2 - x1) >= Math.abs(y2 - y1);
2700
3085
  var line = document.createElement("div");
2701
3086
  var labelEl = document.createElement("div");
3087
+ // Constant-screen-size chrome: line thickness, label font/padding, and
3088
+ // label offsets all compensate for the host's iframe scale so the
3089
+ // measurement readout looks identical at any canvas zoom.
3090
+ var scale = chromeLineScale();
3091
+ var lineWidth = 1 * chromeLineScale();
3092
+ var labelChrome =
3093
+ "transform-origin:center;border-radius:" +
3094
+ 3 * scale +
3095
+ "px;background:var(--design-editor-measure-color);color:white;padding:" +
3096
+ 1 * scale +
3097
+ "px " +
3098
+ 4 * scale +
3099
+ "px;font-size:" +
3100
+ 11 * scale +
3101
+ "px;";
2702
3102
  if (horizontal) {
2703
3103
  var left = Math.min(x1, x2);
2704
3104
  var width = Math.max(1, Math.abs(x2 - x1));
@@ -2709,13 +3109,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2709
3109
  y1 +
2710
3110
  "px;width:" +
2711
3111
  width +
2712
- "px;border-top:1px dashed var(--design-editor-measure-color);";
3112
+ "px;border-top:" +
3113
+ lineWidth +
3114
+ "px dashed var(--design-editor-measure-color);";
2713
3115
  labelEl.style.cssText =
2714
3116
  "position:fixed;left:" +
2715
3117
  (left + width / 2) +
2716
3118
  "px;top:" +
2717
- (y1 - 9) +
2718
- "px;transform:translateX(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;";
3119
+ (y1 - 9 * scale) +
3120
+ "px;transform:translateX(-50%);" +
3121
+ labelChrome;
2719
3122
  } else {
2720
3123
  var top = Math.min(y1, y2);
2721
3124
  var height = Math.max(1, Math.abs(y2 - y1));
@@ -2726,13 +3129,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2726
3129
  top +
2727
3130
  "px;height:" +
2728
3131
  height +
2729
- "px;border-left:1px dashed var(--design-editor-measure-color);";
3132
+ "px;border-left:" +
3133
+ lineWidth +
3134
+ "px dashed var(--design-editor-measure-color);";
2730
3135
  labelEl.style.cssText =
2731
3136
  "position:fixed;left:" +
2732
- (x1 + 5) +
3137
+ (x1 + 5 * scale) +
2733
3138
  "px;top:" +
2734
3139
  (top + height / 2) +
2735
- "px;transform:translateY(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;";
3140
+ "px;transform:translateY(-50%);" +
3141
+ labelChrome;
2736
3142
  }
2737
3143
  labelEl.textContent = label;
2738
3144
  measurementOverlay.appendChild(line);
@@ -2975,7 +3381,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2975
3381
  var didScroll = scrollElementByWheelDelta(scrollTarget, delta.x, delta.y);
2976
3382
  if (!didScroll) return;
2977
3383
  stopNativeInteraction(e);
2978
- window.requestAnimationFrame(refreshOverlays);
3384
+ // Coalesced (not a raw requestAnimationFrame(refreshOverlays)): trackpads
3385
+ // emit several wheel events per frame, and scheduling one full overlay
3386
+ // refresh per event stacks N redundant refreshOverlays() runs into every
3387
+ // frame. With an element selected each run forces multiple synchronous
3388
+ // layout reads, which is exactly the per-event work that froze scrolling
3389
+ // on layout-heavy pages while a selection was active.
3390
+ scheduleRefreshOverlays();
2979
3391
  }
2980
3392
 
2981
3393
  function isEditorTypingTarget(target) {
@@ -2997,6 +3409,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
2997
3409
  var normalized = key && key.length === 1 ? key.toLowerCase() : key;
2998
3410
  var primary = e.metaKey || e.ctrlKey;
2999
3411
  if (key === "Escape" || key === "Enter") return true;
3412
+ // Space arms Figma-style temporary hand-tool panning while the cursor is
3413
+ // over the preview iframe. Only forward the plain (no-modifier) chord —
3414
+ // the isEditorTypingTarget guard above already keeps this from hijacking
3415
+ // Space while the user is typing in an editable in-iframe target.
3416
+ if (key === " " && e.code === "Space") {
3417
+ return !primary && !e.altKey && !e.shiftKey;
3418
+ }
3000
3419
  // Forward Tab only when an element is actively selected so the iframe does
3001
3420
  // not intercept Tab when the user is tabbing through browser UI with nothing
3002
3421
  // selected (preserves native keyboard accessibility).
@@ -3538,6 +3957,460 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
3538
3957
  return Number.isFinite(num) ? num : null;
3539
3958
  }
3540
3959
 
3960
+ // ── Gradient edit overlay: math + minimal linear-gradient CSS parser ────
3961
+ // Ports of MultiScreenCanvas.tsx's exported `gradientLineEndpoints` /
3962
+ // `gradientStopPoints` / `angleFromDraggedEndpoint` /
3963
+ // `stopPercentFromDraggedPoint` (see that file's doc comments for the
3964
+ // full derivation) — duplicated verbatim since this file cannot import.
3965
+
3966
+ function clampGradientT(t: number): number {
3967
+ if (!Number.isFinite(t)) return 0;
3968
+ return Math.max(0, Math.min(1, t));
3969
+ }
3970
+
3971
+ function gradientLineEndpoints(
3972
+ angleDeg: number,
3973
+ width: number,
3974
+ height: number,
3975
+ ) {
3976
+ var rad = (angleDeg * Math.PI) / 180;
3977
+ var dx = Math.sin(rad);
3978
+ var dy = -Math.cos(rad);
3979
+ var halfLength = Math.abs((width / 2) * dx) + Math.abs((height / 2) * dy);
3980
+ var center = { x: width / 2, y: height / 2 };
3981
+ return {
3982
+ start: { x: center.x - dx * halfLength, y: center.y - dy * halfLength },
3983
+ end: { x: center.x + dx * halfLength, y: center.y + dy * halfLength },
3984
+ };
3985
+ }
3986
+
3987
+ function gradientStopPoints(
3988
+ angleDeg: number,
3989
+ width: number,
3990
+ height: number,
3991
+ stops: Array<{ position: number }>,
3992
+ ) {
3993
+ var line = gradientLineEndpoints(angleDeg, width, height);
3994
+ return stops.map(function (stop) {
3995
+ var t = clampGradientT(stop.position / 100);
3996
+ return {
3997
+ x: line.start.x + (line.end.x - line.start.x) * t,
3998
+ y: line.start.y + (line.end.y - line.start.y) * t,
3999
+ position: stop.position,
4000
+ };
4001
+ });
4002
+ }
4003
+
4004
+ function angleFromDraggedEndpoint(
4005
+ point: { x: number; y: number },
4006
+ width: number,
4007
+ height: number,
4008
+ which: "start" | "end",
4009
+ ): number {
4010
+ var center = { x: width / 2, y: height / 2 };
4011
+ var dx = point.x - center.x;
4012
+ var dy = point.y - center.y;
4013
+ if (dx === 0 && dy === 0) return 0;
4014
+ var deg = (Math.atan2(dy, dx) * 180) / Math.PI + 90;
4015
+ if (which === "start") deg += 180;
4016
+ deg = ((deg % 360) + 360) % 360;
4017
+ return deg;
4018
+ }
4019
+
4020
+ function stopPercentFromDraggedPoint(
4021
+ point: { x: number; y: number },
4022
+ angleDeg: number,
4023
+ width: number,
4024
+ height: number,
4025
+ ): number {
4026
+ var line = gradientLineEndpoints(angleDeg, width, height);
4027
+ var lineDx = line.end.x - line.start.x;
4028
+ var lineDy = line.end.y - line.start.y;
4029
+ var lengthSquared = lineDx * lineDx + lineDy * lineDy;
4030
+ if (lengthSquared === 0) return 0;
4031
+ var t =
4032
+ ((point.x - line.start.x) * lineDx + (point.y - line.start.y) * lineDy) /
4033
+ lengthSquared;
4034
+ return clampGradientT(t) * 100;
4035
+ }
4036
+
4037
+ // Minimal linear-only port of GradientEditor.tsx's parseGradientCss /
4038
+ // gradientToCss (that component owns the canonical parser; this is a
4039
+ // reduced copy scoped to just `linear-gradient(...)`, matching the
4040
+ // MultiScreenCanvas overlay's own linear-only scope).
4041
+ var GRADIENT_LINEAR_RE = /^linear-gradient\s*\(([\s\S]*)\)\s*$/i;
4042
+ var GRADIENT_ANGLE_RE = /(-?\d+(?:\.\d+)?)deg/;
4043
+ function splitGradientTopLevel(input: string): string[] {
4044
+ var parts: string[] = [];
4045
+ var depth = 0;
4046
+ var current = "";
4047
+ for (var i = 0; i < input.length; i += 1) {
4048
+ var char = input.charAt(i);
4049
+ if (char === "(") depth += 1;
4050
+ if (char === ")") depth -= 1;
4051
+ if (char === "," && depth === 0) {
4052
+ parts.push(current.trim());
4053
+ current = "";
4054
+ continue;
4055
+ }
4056
+ current += char;
4057
+ }
4058
+ if (current.trim()) parts.push(current.trim());
4059
+ return parts;
4060
+ }
4061
+ function parseLinearGradientCss(value: string): {
4062
+ angle: number;
4063
+ stops: Array<{ id: string; color: string; position: number }>;
4064
+ } | null {
4065
+ var match = String(value || "")
4066
+ .trim()
4067
+ .match(GRADIENT_LINEAR_RE);
4068
+ if (!match) return null;
4069
+ var segments = splitGradientTopLevel(match[1]);
4070
+ if (segments.length === 0) return null;
4071
+ var angle = 90;
4072
+ var stopStart = 0;
4073
+ var first = segments[0];
4074
+ var angleMatch = first.match(GRADIENT_ANGLE_RE);
4075
+ if (angleMatch) {
4076
+ angle = Number(angleMatch[1]);
4077
+ stopStart = 1;
4078
+ } else if (/to\s+/i.test(first)) {
4079
+ stopStart = 1;
4080
+ }
4081
+ var stopSegments = segments.slice(stopStart);
4082
+ var stops: Array<{ id: string; color: string; position: number }> = [];
4083
+ stopSegments.forEach(function (seg, index) {
4084
+ var posMatch = seg.match(/(-?\d+(?:\.\d+)?)%\s*$/);
4085
+ var color = posMatch ? seg.slice(0, posMatch.index).trim() : seg.trim();
4086
+ if (!color) return;
4087
+ var position = posMatch
4088
+ ? Math.max(0, Math.min(100, Number(posMatch[1])))
4089
+ : (index / Math.max(1, stopSegments.length - 1)) * 100;
4090
+ stops.push({ id: "gstop-" + index, color: color, position: position });
4091
+ });
4092
+ if (stops.length < 2) return null;
4093
+ return { angle: angle, stops: stops };
4094
+ }
4095
+ function linearGradientToCss(gradient: {
4096
+ angle: number;
4097
+ stops: Array<{ id: string; color: string; position: number }>;
4098
+ }): string {
4099
+ var sorted = gradient.stops.slice().sort(function (a, b) {
4100
+ return a.position - b.position;
4101
+ });
4102
+ var stopsCss = sorted
4103
+ .map(function (stop) {
4104
+ return stop.color + " " + Math.round(stop.position * 100) / 100 + "%";
4105
+ })
4106
+ .join(", ");
4107
+ return (
4108
+ "linear-gradient(" +
4109
+ Math.round(gradient.angle * 100) / 100 +
4110
+ "deg, " +
4111
+ stopsCss +
4112
+ ")"
4113
+ );
4114
+ }
4115
+
4116
+ // gradientEditOverlayTarget doc / parent wiring contract:
4117
+ //
4118
+ // This bridge only RENDERS the overlay + emits drag deltas; it has no idea
4119
+ // which element on the host side "is" the gradient-edited node beyond the
4120
+ // `nodeId` the parent gives it. The parent (DesignEditor.tsx, NOT owned by
4121
+ // this change — see the report) is expected to:
4122
+ //
4123
+ // 1. Keep its existing `gradientEditTarget` state (already threaded into
4124
+ // MultiScreenCanvas's board/screen-frame overlay) as the single
4125
+ // source of truth for "is a gradient edit session active, for which
4126
+ // node, with which CSS value".
4127
+ // 2. Whenever that target refers to an element *inside* the active
4128
+ // screen's iframe content (as opposed to a board/draft primitive
4129
+ // MultiScreenCanvas already draws chrome for directly), postMessage
4130
+ // `{ type: "gradient-edit-target", nodeId, cssValue }` into that
4131
+ // screen's iframe — `nodeId` being the element's
4132
+ // `data-agent-native-node-id`. Post `{ type: "gradient-edit-clear" }`
4133
+ // when the session ends (selection changes, popover closes, or the
4134
+ // target moves to a different screen/board node).
4135
+ // 3. Listen for this bridge's `{ type: "gradient-edit-change", nodeId,
4136
+ // cssValue, phase }` postMessages and route them through the same
4137
+ // style-apply path `visual-style-change` already uses (phase
4138
+ // "preview" for live feedback, "commit" once on release — mirroring
4139
+ // GradientEditOverlayTarget's own onChange contract in
4140
+ // MultiScreenCanvas.tsx).
4141
+ //
4142
+ // Until that parent-side wiring lands, this bridge simply never receives
4143
+ // `gradient-edit-target` and stays fully inert (see the early-return checks
4144
+ // below), so this is a strictly additive, zero-behavior-change surface
4145
+ // until wired up.
4146
+ var gradientEditTarget: { nodeId: string; cssValue: string } | null = null;
4147
+ var gradientDrag: {
4148
+ kind: "endpoint" | "stop";
4149
+ which?: "start" | "end";
4150
+ stopId?: string;
4151
+ pointerId: number;
4152
+ } | null = null;
4153
+
4154
+ function gradientEditTargetElement(): HTMLElement | null {
4155
+ if (!gradientEditTarget) return null;
4156
+ return document.querySelector(
4157
+ '[data-agent-native-node-id="' +
4158
+ String(gradientEditTarget.nodeId)
4159
+ .replace(/\\/g, "\\\\")
4160
+ .replace(/"/g, '\\"') +
4161
+ '"]',
4162
+ ) as HTMLElement | null;
4163
+ }
4164
+
4165
+ function hideGradientOverlay(): void {
4166
+ gradientOverlay.style.display = "none";
4167
+ while (gradientOverlay.querySelectorAll("[data-gradient-stop]").length) {
4168
+ var stopEl = gradientOverlay.querySelector("[data-gradient-stop]");
4169
+ if (stopEl && stopEl.parentNode) stopEl.parentNode.removeChild(stopEl);
4170
+ }
4171
+ }
4172
+
4173
+ function positionGradientOverlay(): void {
4174
+ var target = gradientEditTarget;
4175
+ if (!target) {
4176
+ hideGradientOverlay();
4177
+ return;
4178
+ }
4179
+ var el = gradientEditTargetElement();
4180
+ if (!el || !document.documentElement.contains(el)) {
4181
+ hideGradientOverlay();
4182
+ return;
4183
+ }
4184
+ var gradient = parseLinearGradientCss(target.cssValue);
4185
+ if (!gradient) {
4186
+ // Non-linear/unparseable — render nothing (linear-only scope, matches
4187
+ // MultiScreenCanvas's GradientEditOverlay contract exactly).
4188
+ hideGradientOverlay();
4189
+ return;
4190
+ }
4191
+ var rect = el.getBoundingClientRect();
4192
+ var width = Math.max(1, rect.width);
4193
+ var height = Math.max(1, rect.height);
4194
+ gradientOverlay.style.display = "block";
4195
+ gradientOverlay.style.left = rect.left + "px";
4196
+ gradientOverlay.style.top = rect.top + "px";
4197
+ gradientOverlay.style.width = width + "px";
4198
+ gradientOverlay.style.height = height + "px";
4199
+
4200
+ var line = gradientLineEndpoints(gradient.angle, width, height);
4201
+ var stopPoints = gradientStopPoints(
4202
+ gradient.angle,
4203
+ width,
4204
+ height,
4205
+ gradient.stops,
4206
+ );
4207
+
4208
+ var line1 = chromeLineScale();
4209
+ var lineStrokeWidth = 1.5 * line1;
4210
+ gradientOverlaySvg.setAttribute("viewBox", "0 0 " + width + " " + height);
4211
+ [gradientOverlayLineOutline, gradientOverlayLine].forEach(
4212
+ function (lineEl, index) {
4213
+ lineEl.setAttribute("x1", String(line.start.x));
4214
+ lineEl.setAttribute("y1", String(line.start.y));
4215
+ lineEl.setAttribute("x2", String(line.end.x));
4216
+ lineEl.setAttribute("y2", String(line.end.y));
4217
+ lineEl.setAttribute(
4218
+ "stroke-width",
4219
+ String(index === 0 ? lineStrokeWidth + 1.5 * line1 : lineStrokeWidth),
4220
+ );
4221
+ },
4222
+ );
4223
+
4224
+ var endpointSize = 10 * line1;
4225
+ var endpointBorderWidth = 1.5 * line1;
4226
+ [
4227
+ { el: gradientOverlayStartHandle, point: line.start, which: "start" },
4228
+ { el: gradientOverlayEndHandle, point: line.end, which: "end" },
4229
+ ].forEach(function (entry) {
4230
+ entry.el.style.left = entry.point.x - endpointSize / 2 + "px";
4231
+ entry.el.style.top = entry.point.y - endpointSize / 2 + "px";
4232
+ entry.el.style.width = endpointSize + "px";
4233
+ entry.el.style.height = endpointSize + "px";
4234
+ entry.el.style.borderWidth = endpointBorderWidth + "px";
4235
+ entry.el.setAttribute(
4236
+ "aria-valuenow",
4237
+ String(Math.round(gradient.angle)),
4238
+ );
4239
+ });
4240
+
4241
+ // Stop markers are rebuilt each render (cheap: 2-8 stops typical) rather
4242
+ // than pooled, matching the overlay's overall "small + self-contained"
4243
+ // design (see the doc comment on MultiScreenCanvas's GradientEditOverlay).
4244
+ hideGradientOverlayStops();
4245
+ var stopSize = 12 * line1;
4246
+ var stopBorderWidth = 2 * line1;
4247
+ stopPoints.forEach(function (point, index) {
4248
+ var stop = gradient.stops[index];
4249
+ if (!stop) return;
4250
+ var stopEl = document.createElement("span");
4251
+ stopEl.setAttribute("data-gradient-stop", stop.id);
4252
+ stopEl.setAttribute("role", "slider");
4253
+ stopEl.setAttribute(
4254
+ "aria-label",
4255
+ stop.color + " at " + Math.round(stop.position) + "%",
4256
+ );
4257
+ stopEl.setAttribute("aria-valuenow", String(Math.round(stop.position)));
4258
+ stopEl.style.cssText =
4259
+ "position:absolute;pointer-events:auto;cursor:grab;border-radius:999px;box-sizing:border-box;border:1px solid white;box-shadow:0 0 0 1px rgba(0,0,0,0.25);";
4260
+ stopEl.style.left = point.x - stopSize / 2 + "px";
4261
+ stopEl.style.top = point.y - stopSize / 2 + "px";
4262
+ stopEl.style.width = stopSize + "px";
4263
+ stopEl.style.height = stopSize + "px";
4264
+ stopEl.style.borderWidth = stopBorderWidth + "px";
4265
+ stopEl.style.backgroundColor = stop.color;
4266
+ stopEl.addEventListener("pointerdown", function (ev: PointerEvent) {
4267
+ beginGradientDrag(ev, { kind: "stop", stopId: stop.id });
4268
+ });
4269
+ gradientOverlay.appendChild(stopEl);
4270
+ });
4271
+ }
4272
+
4273
+ function hideGradientOverlayStops(): void {
4274
+ Array.prototype.slice
4275
+ .call(gradientOverlay.querySelectorAll("[data-gradient-stop]"))
4276
+ .forEach(function (node: Element) {
4277
+ if (node.parentNode) node.parentNode.removeChild(node);
4278
+ });
4279
+ }
4280
+
4281
+ function gradientOverlayLocalPoint(event: PointerEvent): {
4282
+ x: number;
4283
+ y: number;
4284
+ } {
4285
+ var rect = gradientOverlay.getBoundingClientRect();
4286
+ return { x: event.clientX - rect.left, y: event.clientY - rect.top };
4287
+ }
4288
+
4289
+ function emitGradientChange(
4290
+ gradient: {
4291
+ angle: number;
4292
+ stops: Array<{ id: string; color: string; position: number }>;
4293
+ },
4294
+ phase: "preview" | "commit",
4295
+ ): void {
4296
+ if (!gradientEditTarget) return;
4297
+ (window.parent as Window).postMessage(
4298
+ {
4299
+ type: "gradient-edit-change",
4300
+ nodeId: gradientEditTarget.nodeId,
4301
+ cssValue: linearGradientToCss(gradient),
4302
+ phase: phase,
4303
+ },
4304
+ "*",
4305
+ );
4306
+ // Keep the local target's cssValue in sync so a subsequent drag tick (or
4307
+ // a re-render triggered by scroll/resize) reflects the in-progress value
4308
+ // instead of waiting for the parent to round-trip a fresh
4309
+ // gradient-edit-target message.
4310
+ gradientEditTarget = {
4311
+ nodeId: gradientEditTarget.nodeId,
4312
+ cssValue: linearGradientToCss(gradient),
4313
+ };
4314
+ }
4315
+
4316
+ function beginGradientDrag(
4317
+ event: PointerEvent,
4318
+ kind:
4319
+ | { kind: "endpoint"; which: "start" | "end" }
4320
+ | { kind: "stop"; stopId: string },
4321
+ ): void {
4322
+ event.stopPropagation();
4323
+ event.preventDefault();
4324
+ gradientDrag = {
4325
+ kind: kind.kind,
4326
+ which: kind.kind === "endpoint" ? kind.which : undefined,
4327
+ stopId: kind.kind === "stop" ? kind.stopId : undefined,
4328
+ pointerId: event.pointerId,
4329
+ };
4330
+ var handleEl = event.currentTarget as Element;
4331
+ if (handleEl && (handleEl as HTMLElement).setPointerCapture) {
4332
+ (handleEl as HTMLElement).setPointerCapture(event.pointerId);
4333
+ }
4334
+ document.addEventListener("pointermove", onGradientDragMove, true);
4335
+ document.addEventListener("pointerup", onGradientDragEnd, true);
4336
+ document.addEventListener("pointercancel", onGradientDragEnd, true);
4337
+ }
4338
+
4339
+ function onGradientDragMove(event: PointerEvent): void {
4340
+ var drag = gradientDrag;
4341
+ var target = gradientEditTarget;
4342
+ var el = gradientEditTargetElement();
4343
+ if (!drag || drag.pointerId !== event.pointerId || !target || !el) return;
4344
+ var gradient = parseLinearGradientCss(target.cssValue);
4345
+ if (!gradient) return;
4346
+ var rect = el.getBoundingClientRect();
4347
+ var width = Math.max(1, rect.width);
4348
+ var height = Math.max(1, rect.height);
4349
+ var local = gradientOverlayLocalPoint(event);
4350
+ if (drag.kind === "endpoint" && drag.which) {
4351
+ var nextAngle = angleFromDraggedEndpoint(
4352
+ local,
4353
+ width,
4354
+ height,
4355
+ drag.which,
4356
+ );
4357
+ emitGradientChange(
4358
+ { angle: nextAngle, stops: gradient.stops },
4359
+ "preview",
4360
+ );
4361
+ positionGradientOverlay();
4362
+ return;
4363
+ }
4364
+ if (drag.kind === "stop" && drag.stopId) {
4365
+ var nextPosition = stopPercentFromDraggedPoint(
4366
+ local,
4367
+ gradient.angle,
4368
+ width,
4369
+ height,
4370
+ );
4371
+ var stopId = drag.stopId;
4372
+ emitGradientChange(
4373
+ {
4374
+ angle: gradient.angle,
4375
+ stops: gradient.stops.map(function (stop) {
4376
+ return stop.id === stopId
4377
+ ? { id: stop.id, color: stop.color, position: nextPosition }
4378
+ : stop;
4379
+ }),
4380
+ },
4381
+ "preview",
4382
+ );
4383
+ positionGradientOverlay();
4384
+ }
4385
+ }
4386
+
4387
+ function onGradientDragEnd(event: PointerEvent): void {
4388
+ var drag = gradientDrag;
4389
+ if (!drag || drag.pointerId !== event.pointerId) return;
4390
+ gradientDrag = null;
4391
+ document.removeEventListener("pointermove", onGradientDragMove, true);
4392
+ document.removeEventListener("pointerup", onGradientDragEnd, true);
4393
+ document.removeEventListener("pointercancel", onGradientDragEnd, true);
4394
+ var target = gradientEditTarget;
4395
+ if (!target) return;
4396
+ var gradient = parseLinearGradientCss(target.cssValue);
4397
+ if (!gradient) return;
4398
+ emitGradientChange(gradient, "commit");
4399
+ }
4400
+
4401
+ gradientOverlayStartHandle.addEventListener(
4402
+ "pointerdown",
4403
+ function (ev: PointerEvent) {
4404
+ beginGradientDrag(ev, { kind: "endpoint", which: "start" });
4405
+ },
4406
+ );
4407
+ gradientOverlayEndHandle.addEventListener(
4408
+ "pointerdown",
4409
+ function (ev: PointerEvent) {
4410
+ beginGradientDrag(ev, { kind: "endpoint", which: "end" });
4411
+ },
4412
+ );
4413
+
3541
4414
  function currentRotation(el) {
3542
4415
  var transform =
3543
4416
  el.style.transform || window.getComputedStyle(el).transform || "";
@@ -3663,6 +4536,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
3663
4536
  currentValue: originValue,
3664
4537
  mirrorOpposite: !!e.altKey,
3665
4538
  };
4539
+ // Hide the hover-only hatch fill the instant the drag begins (Figma-style:
4540
+ // hatch communicates "this is the resizable band" on hover; once dragging,
4541
+ // only the live value badge should be visible over the padding band).
4542
+ updateSpacingOverlay(selectedEl);
3666
4543
  showSpacingBadgeForHandle(handle, originValue);
3667
4544
 
3668
4545
  function updateSpacingDragMirrorState(mirrorOpposite: boolean) {
@@ -4002,10 +4879,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4002
4879
  clientX: number,
4003
4880
  clientY: number,
4004
4881
  ): void {
4882
+ // Constant-screen-size chrome (see showSpacingBadgeForHandle): scale the
4883
+ // label's intrinsic sizes by chromeLineScale() so the move/resize badge
4884
+ // reads the same at any canvas zoom.
4885
+ var line = chromeLineScale();
4005
4886
  transformBadge.textContent = text;
4006
4887
  transformBadge.style.display = "block";
4007
- transformBadge.style.left = clientX + 12 + "px";
4008
- transformBadge.style.top = clientY + 12 + "px";
4888
+ transformBadge.style.fontSize = 11 * line + "px";
4889
+ transformBadge.style.padding = 3 * line + "px " + 5 * line + "px";
4890
+ transformBadge.style.borderRadius = 4 * line + "px";
4891
+ transformBadge.style.borderWidth = 1 * line + "px";
4892
+ transformBadge.style.left = clientX + 12 * line + "px";
4893
+ transformBadge.style.top = clientY + 12 * line + "px";
4009
4894
  }
4010
4895
 
4011
4896
  function hideTransformBadge(): void {
@@ -4045,6 +4930,84 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4045
4930
  return true;
4046
4931
  }
4047
4932
 
4933
+ // Multi-select group move: when the user drags an element that is a member
4934
+ // of the current multi-selection (primary selectedEl + the passive
4935
+ // shift-click/marquee set), the whole group moves together — Figma
4936
+ // behavior. Returns the full member list in DOCUMENT ORDER (so a group
4937
+ // flow-insert lands the members consecutively in their existing visual
4938
+ // order) when gestureEl belongs to a 2+ selection, or just [gestureEl]
4939
+ // otherwise. Members nested inside another member are dropped: moving the
4940
+ // ancestor already moves them, and double-applying the delta would fling
4941
+ // them.
4942
+ function collectMoveGroupMembers(gestureEl: Element): Element[] {
4943
+ if (!gestureEl) return [];
4944
+ var raw: Element[] = [];
4945
+ if (selectedEl) raw.push(selectedEl);
4946
+ for (var i = 0; i < passiveSelectionEls.length; i += 1) {
4947
+ raw.push(passiveSelectionEls[i]);
4948
+ }
4949
+ var members: Element[] = [];
4950
+ for (var j = 0; j < raw.length; j += 1) {
4951
+ var candidate = raw[j];
4952
+ if (
4953
+ !candidate ||
4954
+ candidate === document.body ||
4955
+ candidate === document.documentElement ||
4956
+ !document.documentElement.contains(candidate) ||
4957
+ isLayerInteractionBlocked(candidate) ||
4958
+ members.indexOf(candidate) !== -1
4959
+ ) {
4960
+ continue;
4961
+ }
4962
+ members.push(candidate);
4963
+ }
4964
+ // Drop members contained by another member.
4965
+ members = members.filter(function (member) {
4966
+ return !members.some(function (other) {
4967
+ return other !== member && other.contains(member);
4968
+ });
4969
+ });
4970
+ var gestureMember: Element | null = null;
4971
+ for (var k = 0; k < members.length; k += 1) {
4972
+ if (
4973
+ members[k] === gestureEl ||
4974
+ (members[k].contains && members[k].contains(gestureEl))
4975
+ ) {
4976
+ gestureMember = members[k];
4977
+ break;
4978
+ }
4979
+ }
4980
+ if (!gestureMember || members.length < 2) return [gestureEl];
4981
+ members.sort(function (a, b) {
4982
+ var position = a.compareDocumentPosition(b);
4983
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
4984
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
4985
+ return 0;
4986
+ });
4987
+ return members;
4988
+ }
4989
+
4990
+ // Resolves the group member that owns a drag gesture's target element (the
4991
+ // member itself or an ancestor member), or null when the target is not
4992
+ // part of the current multi-selection.
4993
+ function groupMemberForGestureTarget(target: Element | null): Element | null {
4994
+ if (!target) return null;
4995
+ var raw: Element[] = selectedEl
4996
+ ? [selectedEl].concat(passiveSelectionEls)
4997
+ : passiveSelectionEls.slice();
4998
+ for (var i = 0; i < raw.length; i += 1) {
4999
+ var member = raw[i];
5000
+ if (
5001
+ member &&
5002
+ document.documentElement.contains(member) &&
5003
+ (member === target || (member.contains && member.contains(target)))
5004
+ ) {
5005
+ return member;
5006
+ }
5007
+ }
5008
+ return null;
5009
+ }
5010
+
4048
5011
  function isAutoLayoutElement(el: Element | null): boolean {
4049
5012
  if (!el) return false;
4050
5013
  var cs = window.getComputedStyle(el);
@@ -4068,41 +5031,217 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4068
5031
  return cs.position === "absolute" || cs.position === "fixed";
4069
5032
  }
4070
5033
 
4071
- function absolutePrimitiveContainerTargetForPoint(
4072
- el: Element | null,
4073
- clientX: number,
4074
- clientY: number,
4075
- ) {
4076
- var hits: Element[] = document.elementsFromPoint
4077
- ? document.elementsFromPoint(clientX, clientY)
4078
- : ([document.elementFromPoint(clientX, clientY)] as Element[]);
4079
- var seen: Element[] = [];
4080
- for (var i = 0; i < hits.length; i += 1) {
4081
- var cursor: Element | null = hits[i];
4082
- var candidate: Element | null = null;
4083
- while (cursor && cursor !== document.body) {
4084
- if (isAbsolutePrimitiveContainer(cursor)) {
4085
- candidate = cursor;
4086
- break;
4087
- }
4088
- cursor = cursor.parentElement;
4089
- }
4090
- if (!candidate || seen.indexOf(candidate) !== -1) continue;
4091
- seen.push(candidate);
4092
- if (candidate === el) continue;
4093
- if (el && el.contains && el.contains(candidate)) continue;
4094
- if (el && candidate.contains && candidate.contains(el)) continue;
4095
- if (isOverlayElement(candidate) || isLayerInteractionBlocked(candidate)) {
4096
- continue;
5034
+ // Figma-parity "drop into a frame" conversion: when a plain rect/div drop
5035
+ // target isn't already auto-layout, dropping a child into it turns it into
5036
+ // one. Direction/gap are inferred from the container's existing children
5037
+ // (same spread-axis heuristic as inferAutoLayoutFromChildren in
5038
+ // DesignEditor.tsx: whichever axis the children's bounding boxes spread
5039
+ // further along wins) when it already has other children; an empty
5040
+ // container defaults to flex-direction:column, matching the product
5041
+ // decision for a fresh single-child drop with no layout signal yet.
5042
+ function inferAutoLayoutConversionForContainer(
5043
+ container: Element,
5044
+ excludeEls: Element[],
5045
+ ): { direction: "row" | "column"; gap: number } {
5046
+ var siblings = draggableElementChildren(container).filter(function (child) {
5047
+ return excludeEls.indexOf(child) === -1;
5048
+ });
5049
+ if (siblings.length === 0) {
5050
+ return { direction: "column", gap: 10 };
5051
+ }
5052
+ var rects = siblings.map(function (child) {
5053
+ return child.getBoundingClientRect();
5054
+ });
5055
+ var minX = Math.min.apply(
5056
+ null,
5057
+ rects.map(function (r) {
5058
+ return r.left;
5059
+ }),
5060
+ );
5061
+ var maxX = Math.max.apply(
5062
+ null,
5063
+ rects.map(function (r) {
5064
+ return r.left + r.width;
5065
+ }),
5066
+ );
5067
+ var minY = Math.min.apply(
5068
+ null,
5069
+ rects.map(function (r) {
5070
+ return r.top;
5071
+ }),
5072
+ );
5073
+ var maxY = Math.max.apply(
5074
+ null,
5075
+ rects.map(function (r) {
5076
+ return r.top + r.height;
5077
+ }),
5078
+ );
5079
+ var direction: "row" | "column" =
5080
+ maxX - minX >= maxY - minY ? "row" : "column";
5081
+ if (rects.length < 2) {
5082
+ return { direction: direction, gap: 10 };
5083
+ }
5084
+ var sorted = rects.slice().sort(function (a, b) {
5085
+ return direction === "row" ? a.left - b.left : a.top - b.top;
5086
+ });
5087
+ var gaps: number[] = [];
5088
+ for (var i = 1; i < sorted.length; i += 1) {
5089
+ var prev = sorted[i - 1];
5090
+ var current = sorted[i];
5091
+ var gapValue =
5092
+ direction === "row"
5093
+ ? current.left - (prev.left + prev.width)
5094
+ : current.top - (prev.top + prev.height);
5095
+ if (isFinite(gapValue) && gapValue > 0) gaps.push(gapValue);
5096
+ }
5097
+ if (gaps.length === 0) {
5098
+ return { direction: direction, gap: 10 };
5099
+ }
5100
+ gaps.sort(function (a, b) {
5101
+ return a - b;
5102
+ });
5103
+ var mid = Math.floor(gaps.length / 2);
5104
+ var median =
5105
+ gaps.length % 2 === 0 ? (gaps[mid - 1] + gaps[mid]) / 2 : gaps[mid];
5106
+ return { direction: direction, gap: Math.round(median) };
5107
+ }
5108
+
5109
+ // Applies the flex conversion to `container` and posts it to the host as a
5110
+ // normal visual-style-change for that container's own selector/elementInfo
5111
+ // — the same message shape the style panel already uses, just targeting
5112
+ // the drop-target anchor instead of the current selection, so no host-side
5113
+ // routing changes are needed. Runs BEFORE the moved-element's own
5114
+ // visual-structure-change post so the host's synchronous same-tick content
5115
+ // refs (see DesignEditor.tsx's getFreshActiveContent) compose the two
5116
+ // edits in order: container becomes flex, then the child moves into it.
5117
+ // `excludeEls`: the dragged element(s) — every member of a group drag —
5118
+ // so the direction/gap inference only looks at the container's existing
5119
+ // children, never the incoming ones.
5120
+ function applyAutoLayoutConversionForDrop(
5121
+ container: Element,
5122
+ excludeEls: Element[],
5123
+ ): void {
5124
+ var inferred = inferAutoLayoutConversionForContainer(container, excludeEls);
5125
+ var el = container as HTMLElement;
5126
+ el.style.display = "flex";
5127
+ el.style.flexDirection = inferred.direction;
5128
+ el.style.gap = inferred.gap + "px";
5129
+ (window.parent as Window).postMessage(
5130
+ {
5131
+ type: "visual-style-change",
5132
+ selector: getSelector(container),
5133
+ styles: {
5134
+ display: "flex",
5135
+ "flex-direction": inferred.direction,
5136
+ gap: inferred.gap + "px",
5137
+ },
5138
+ payload: getElementInfo(container),
5139
+ },
5140
+ "*",
5141
+ );
5142
+ }
5143
+
5144
+ // ── Board-text auto-color adaptation on nest ─────────────────────────────
5145
+ //
5146
+ // Board-drawn text on the dark infinite canvas gets an explicit inline
5147
+ // default `color:#ffffff` (+ Inter) from DesignEditor's
5148
+ // appendCanvasPrimitiveToHtml — necessary there because "currentColor"
5149
+ // would inherit the unstyled document's black and vanish on the dark
5150
+ // board. But when that text is later dragged INTO a (typically light)
5151
+ // container, the stale inline white makes it white-on-white invisible.
5152
+ //
5153
+ // On re-parent into a different container, adapt: if the text's inline
5154
+ // color is the auto-applied board default (marker present, or —
5155
+ // pre-marker content — exactly the default white AND the destination is
5156
+ // light), switch it to `color:inherit` so it picks up the container's
5157
+ // effective text color. A color the user explicitly set is NEVER touched:
5158
+ // DesignEditor should stamp `data-an-auto-text-color` when IT auto-picks
5159
+ // the color at creation and remove the marker on any explicit color edit;
5160
+ // when the marker is present the color is definitely auto (always safe to
5161
+ // adapt), and when absent the conservative default-white + light-target
5162
+ // heuristic below only fires in the exact case where the text would be
5163
+ // invisible anyway.
5164
+ var BOARD_TEXT_AUTO_COLOR_MARKER = "data-an-auto-text-color";
5165
+
5166
+ function parseCssRgb(
5167
+ value: string,
5168
+ ): { r: number; g: number; b: number; a: number } | null {
5169
+ var match = /^rgba?\(([^)]+)\)$/.exec((value || "").trim());
5170
+ if (!match) return null;
5171
+ var parts = match[1].split(",").map(function (part) {
5172
+ return parseFloat(part.trim());
5173
+ });
5174
+ if (parts.length < 3 || parts.some(isNaN)) return null;
5175
+ return {
5176
+ r: parts[0],
5177
+ g: parts[1],
5178
+ b: parts[2],
5179
+ a: parts.length > 3 ? parts[3] : 1,
5180
+ };
5181
+ }
5182
+
5183
+ // Walks up from the container until it finds a non-transparent computed
5184
+ // background and reports whether it is light (relative-luminance
5185
+ // threshold). An unstyled chain means the default white page background.
5186
+ function containerBackgroundIsLight(container: Element): boolean {
5187
+ var cursor: Element | null = container;
5188
+ while (cursor && cursor !== document.documentElement) {
5189
+ var bg = window.getComputedStyle(cursor).backgroundColor;
5190
+ var rgb = parseCssRgb(bg);
5191
+ if (rgb && rgb.a > 0.01) {
5192
+ var luminance = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
5193
+ return luminance > 150;
4097
5194
  }
4098
- return {
4099
- anchor: candidate,
4100
- placement: "inside",
4101
- axis: "y",
4102
- dropMode: "absolute-container",
4103
- };
5195
+ cursor = cursor.parentElement;
4104
5196
  }
4105
- return null;
5197
+ return true;
5198
+ }
5199
+
5200
+ function adaptAutoTextColorForNest(
5201
+ member: Element,
5202
+ container: Element | null,
5203
+ ): void {
5204
+ if (!container || member.parentElement === container) return;
5205
+ var kind = (
5206
+ member.getAttribute("data-an-primitive") ||
5207
+ member.getAttribute("data-agent-native-primitive") ||
5208
+ ""
5209
+ ).toLowerCase();
5210
+ if (kind !== "text") return;
5211
+ var el = member as HTMLElement;
5212
+ var inline = el.style.color;
5213
+ if (!inline || inline === "inherit" || inline === "currentcolor") return;
5214
+ var hasAutoMarker = member.hasAttribute(BOARD_TEXT_AUTO_COLOR_MARKER);
5215
+ if (!hasAutoMarker) {
5216
+ var normalized = inline.replace(/\s+/g, "").toLowerCase();
5217
+ var isDefaultWhite =
5218
+ normalized === "#ffffff" ||
5219
+ normalized === "#fff" ||
5220
+ normalized === "rgb(255,255,255)" ||
5221
+ normalized === "white";
5222
+ if (!isDefaultWhite) return;
5223
+ if (!containerBackgroundIsLight(container)) return;
5224
+ }
5225
+ el.style.color = "inherit";
5226
+ (window.parent as Window).postMessage(
5227
+ {
5228
+ type: "visual-style-change",
5229
+ selector: getSelector(member),
5230
+ styles: { color: "inherit" },
5231
+ payload: getElementInfo(member),
5232
+ },
5233
+ "*",
5234
+ );
5235
+ }
5236
+
5237
+ // Resolves the actual container element a drop target lands the moved
5238
+ // element(s) in: the anchor itself for "inside" placement, otherwise the
5239
+ // anchor's parent.
5240
+ function dropContainerForTarget(target): Element | null {
5241
+ if (!target || !target.anchor) return null;
5242
+ return target.placement === "inside"
5243
+ ? target.anchor
5244
+ : target.anchor.parentElement;
4106
5245
  }
4107
5246
 
4108
5247
  function isOutsideIframeViewport(clientX: number, clientY: number): boolean {
@@ -4278,14 +5417,95 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4278
5417
  return "y";
4279
5418
  }
4280
5419
 
4281
- function reorderTargetForPoint(el, clientX, clientY) {
5420
+ // Resolves a between-children insertion inside `container` from the
5421
+ // pointer position: the nearest visible child (by flow-axis center)
5422
+ // becomes the anchor with before/after placement, which renders as the
5423
+ // Figma-style insertion LINE between children. Returns null when the
5424
+ // container has no eligible children (caller falls back to "inside").
5425
+ //
5426
+ // This is the B5-4 fix: hovering the container's own background — its
5427
+ // padding, or the gaps BETWEEN children, which is where the pointer
5428
+ // naturally sits when dropping "between two cards" — used to resolve to
5429
+ // placement "inside" (appendChild = always lands after the LAST child,
5430
+ // with the container-fill affordance instead of an insertion line). Both
5431
+ // in-screen drag paths now route container-background hits through this
5432
+ // helper so dropping between children works and shows the line.
5433
+ function nearestChildInsertionTarget(
5434
+ container: Element,
5435
+ clientX: number,
5436
+ clientY: number,
5437
+ excludeEls?: Element[],
5438
+ ) {
5439
+ var excluded: Element[] = excludeEls || [];
5440
+ function isExcluded(node) {
5441
+ for (var i = 0; i < excluded.length; i += 1) {
5442
+ var member = excluded[i];
5443
+ if (
5444
+ member &&
5445
+ (member === node || (member.contains && member.contains(node)))
5446
+ ) {
5447
+ return true;
5448
+ }
5449
+ }
5450
+ return false;
5451
+ }
5452
+ var children = draggableElementChildren(container).filter(function (child) {
5453
+ return !isExcluded(child);
5454
+ });
5455
+ if (!children.length) return null;
5456
+ var axis = parentFlowAxis(container);
5457
+ var best: Element | null = null;
5458
+ var bestDistance = Infinity;
5459
+ var placement = "after";
5460
+ for (var j = 0; j < children.length; j += 1) {
5461
+ var rect = children[j].getBoundingClientRect();
5462
+ // Skip zero-size children (e.g. Alpine <template> nodes, hidden
5463
+ // elements) — they are not visible slots.
5464
+ if (rect.width <= 0 || rect.height <= 0) continue;
5465
+ var center =
5466
+ axis === "x" ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
5467
+ var pointer = axis === "x" ? clientX : clientY;
5468
+ var distance = Math.abs(pointer - center);
5469
+ if (distance < bestDistance) {
5470
+ bestDistance = distance;
5471
+ best = children[j];
5472
+ placement = pointer < center ? "before" : "after";
5473
+ }
5474
+ }
5475
+ if (!best) return null;
5476
+ return {
5477
+ anchor: best,
5478
+ placement: placement,
5479
+ axis: axis,
5480
+ dropMode: "flow-insert",
5481
+ };
5482
+ }
5483
+
5484
+ // `excludeEls` (optional): other members of a multi-select group drag.
5485
+ // They can never be the anchor/target of their own group's reorder (that
5486
+ // would insert the group relative to an element that is itself about to
5487
+ // move), so hits on them fall through to the sibling-scan fallback and the
5488
+ // sibling scan skips them.
5489
+ function reorderTargetForPoint(el, clientX, clientY, excludeEls) {
4282
5490
  if (!el || !el.parentElement) return null;
5491
+ var dragged: Element[] = [el].concat(excludeEls || []);
5492
+ function isDraggedOrInsideDragged(node) {
5493
+ for (var di = 0; di < dragged.length; di += 1) {
5494
+ var member = dragged[di];
5495
+ if (
5496
+ member &&
5497
+ (member === node || (member.contains && member.contains(node)))
5498
+ ) {
5499
+ return true;
5500
+ }
5501
+ }
5502
+ return false;
5503
+ }
4283
5504
  var hit = elementFromEditorPoint(clientX, clientY);
4284
5505
  if (
4285
5506
  hit &&
4286
5507
  hit !== document.documentElement &&
4287
- hit !== el &&
4288
- !el.contains(hit) &&
5508
+ !isDraggedOrInsideDragged(hit) &&
4289
5509
  !isOverlayElement(hit)
4290
5510
  ) {
4291
5511
  if (isContainerDropTarget(hit)) {
@@ -4300,6 +5520,19 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4300
5520
  clientY,
4301
5521
  );
4302
5522
  if (!edgePlacement) {
5523
+ // B5-4: the pointer is over the container's inner area — its
5524
+ // padding or the gap BETWEEN children (a direct child under the
5525
+ // pointer would have been the hit instead). Resolve to the
5526
+ // nearest child slot so the drop lands between children with the
5527
+ // insertion LINE, instead of the old placement:"inside" append-
5528
+ // after-last with only the container-fill affordance.
5529
+ var betweenChildren = nearestChildInsertionTarget(
5530
+ hit,
5531
+ clientX,
5532
+ clientY,
5533
+ dragged,
5534
+ );
5535
+ if (betweenChildren) return betweenChildren;
4303
5536
  return {
4304
5537
  anchor: hit,
4305
5538
  placement: "inside",
@@ -4336,7 +5569,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4336
5569
  var parent = el.parentElement;
4337
5570
  var axis = parentFlowAxis(parent);
4338
5571
  var siblings = draggableElementChildren(parent).filter(function (child) {
4339
- return child !== el;
5572
+ return !isDraggedOrInsideDragged(child);
4340
5573
  });
4341
5574
  if (!siblings.length) return null;
4342
5575
  var beforeTarget = null;
@@ -4360,41 +5593,99 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4360
5593
  };
4361
5594
  }
4362
5595
 
5596
+ // Accepts a single element or an array (group drags temporarily disable
5597
+ // pointer-events on EVERY dragged member so the hit test sees what's
5598
+ // underneath the whole group, not a sibling member riding along under the
5599
+ // pointer).
4363
5600
  function elementFromEditorPointIgnoring(
4364
5601
  clientX: number,
4365
5602
  clientY: number,
4366
- ignore: Element | null,
5603
+ ignore: Element | Element[] | null,
4367
5604
  ): Element | null {
4368
- var previousPointerEvents: string | null = null;
4369
- if (ignore && ignore instanceof HTMLElement) {
4370
- previousPointerEvents = ignore.style.pointerEvents;
4371
- ignore.style.pointerEvents = "none";
4372
- }
5605
+ var ignoreList: HTMLElement[] = [];
5606
+ var previousPointerEvents: string[] = [];
5607
+ (Array.isArray(ignore) ? ignore : ignore ? [ignore] : []).forEach(
5608
+ function (item) {
5609
+ if (item && item instanceof HTMLElement) {
5610
+ ignoreList.push(item);
5611
+ previousPointerEvents.push(item.style.pointerEvents);
5612
+ item.style.pointerEvents = "none";
5613
+ }
5614
+ },
5615
+ );
4373
5616
  var hit = elementFromEditorPoint(clientX, clientY);
4374
- if (ignore && ignore instanceof HTMLElement) {
4375
- ignore.style.pointerEvents = previousPointerEvents ?? "";
4376
- }
5617
+ ignoreList.forEach(function (item, index) {
5618
+ item.style.pointerEvents = previousPointerEvents[index] ?? "";
5619
+ });
4377
5620
  return hit;
4378
5621
  }
4379
5622
 
4380
- function autoLayoutInsertionTargetForPoint(el, clientX, clientY) {
4381
- var hit = elementFromEditorPointIgnoring(clientX, clientY, el);
5623
+ // Item 8 re-parent policy for absolute-position drags (this function
5624
+ // feeds onMove's currentAutoLayoutTarget for the isFlowReorderCandidate
5625
+ // === false path, i.e. plain absolute-positioned elements/shapes, NOT flow
5626
+ // children — see reorderTargetForPoint above for that separate case).
5627
+ //
5628
+ // PRODUCT DECISION (supersedes the old "genuine auto-layout only" policy
5629
+ // below): dragging one element onto another must nest it as a child with
5630
+ // auto-layout, exactly like dropping an element into a frame in Figma —
5631
+ // this applies to plain rectangles/divs too, not just existing flex/grid
5632
+ // containers. `isContainerDropTarget` (below) is the single nestable-
5633
+ // container test shared with reorderTargetForPoint's flow-reorder path and
5634
+ // the overview canvas's getPrimitiveDropTargetForPoint, so in-screen and
5635
+ // cross-screen drag agree on what counts as a container: any block-level
5636
+ // element that can hold children (plain divs/sections/etc. included),
5637
+ // excluding text/leaf tags, overlay chrome, and the dragged element's own
5638
+ // descendants (cycle guard). When the resolved container is not already an
5639
+ // auto-layout (flex/grid) display, the caller (onUp) converts it to
5640
+ // display:flex on drop — see needsAutoLayoutConversion below and
5641
+ // applyAutoLayoutConversionForDrop.
5642
+ //
5643
+ // (Historical note: an earlier revision of this policy matched ANY
5644
+ // absolute-positioned rect primitive as a drop-into target purely from
5645
+ // pointer overlap, with no leaf/text exclusion and no cycle guard, which
5646
+ // caused two merely-overlapping absolute elements to silently adopt one
5647
+ // another. isContainerDropTarget's tag/role checks plus the cursor
5648
+ // ancestor-walk's cycle guard below are what keep this version scoped to
5649
+ // Figma's actual "drop into a frame" behavior instead of that regression.)
5650
+ // `excludeEls` (optional): additional dragged elements to treat exactly
5651
+ // like `el` — used by multi-select group drags so no member of the moving
5652
+ // group is hit-tested, walked through, or offered as a nesting container
5653
+ // for its own group.
5654
+ function autoLayoutInsertionTargetForPoint(el, clientX, clientY, excludeEls) {
5655
+ var dragged: Element[] = [el].concat(excludeEls || []);
5656
+ function isDraggedOrInsideDragged(node) {
5657
+ for (var i = 0; i < dragged.length; i += 1) {
5658
+ var member = dragged[i];
5659
+ if (
5660
+ member &&
5661
+ (member === node || (member.contains && member.contains(node)))
5662
+ ) {
5663
+ return true;
5664
+ }
5665
+ }
5666
+ return false;
5667
+ }
5668
+ var hit = elementFromEditorPointIgnoring(clientX, clientY, dragged);
4382
5669
  if (!hit || hit === document.documentElement || hit === document.body) {
4383
5670
  return null;
4384
5671
  }
4385
5672
  var cursor = hit;
4386
5673
  while (cursor && cursor !== document.body) {
4387
5674
  if (
4388
- cursor === el ||
4389
- (el && el.contains && el.contains(cursor)) ||
5675
+ isDraggedOrInsideDragged(cursor) ||
4390
5676
  isOverlayElement(cursor) ||
4391
5677
  isLayerInteractionBlocked(cursor)
4392
5678
  ) {
4393
5679
  cursor = cursor.parentElement;
4394
5680
  continue;
4395
5681
  }
5682
+ // document.body is excluded as a nesting target here (both branches
5683
+ // below): it is the screen root, not a Figma-style frame, so hovering
5684
+ // loose background — including hovering a leaf like an image or text
5685
+ // node whose parent happens to be body — must fall through to a plain
5686
+ // absolute placement instead of silently wrapping body in auto-layout.
4396
5687
  var parent = cursor.parentElement;
4397
- if (parent && isAutoLayoutElement(parent)) {
5688
+ if (parent && parent !== document.body && isContainerDropTarget(parent)) {
4398
5689
  var parentAxis = parentFlowAxis(parent);
4399
5690
  var childRect = cursor.getBoundingClientRect();
4400
5691
  var childCenter =
@@ -4407,27 +5698,44 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4407
5698
  placement: childPointer < childCenter ? "before" : "after",
4408
5699
  axis: parentAxis,
4409
5700
  dropMode: "flow-insert",
5701
+ needsAutoLayoutConversion: !isAutoLayoutElement(parent),
5702
+ conversionTarget: parent,
4410
5703
  };
4411
5704
  }
4412
- if (isAutoLayoutElement(cursor) && isContainerDropTarget(cursor)) {
5705
+ if (cursor !== document.body && isContainerDropTarget(cursor)) {
5706
+ // B5-4 (absolute path): pointer over the container's own background
5707
+ // — its padding or a gap between children. Prefer the nearest child
5708
+ // slot (insertion line at the pointer index) over plain "inside"
5709
+ // (which appends after the last child); fall back to "inside" only
5710
+ // for containers with no visible children.
5711
+ var betweenContainerChildren = nearestChildInsertionTarget(
5712
+ cursor,
5713
+ clientX,
5714
+ clientY,
5715
+ dragged,
5716
+ );
5717
+ if (betweenContainerChildren) {
5718
+ return {
5719
+ anchor: betweenContainerChildren.anchor,
5720
+ placement: betweenContainerChildren.placement,
5721
+ axis: betweenContainerChildren.axis,
5722
+ dropMode: "flow-insert",
5723
+ needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
5724
+ conversionTarget: cursor,
5725
+ };
5726
+ }
4413
5727
  return {
4414
5728
  anchor: cursor,
4415
5729
  placement: "inside",
4416
5730
  axis: parentFlowAxis(cursor),
4417
5731
  dropMode: "flow-insert",
4418
- };
4419
- }
4420
- if (isAbsolutePrimitiveContainer(cursor)) {
4421
- return {
4422
- anchor: cursor,
4423
- placement: "inside",
4424
- axis: "y",
4425
- dropMode: "absolute-container",
5732
+ needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
5733
+ conversionTarget: cursor,
4426
5734
  };
4427
5735
  }
4428
5736
  cursor = parent;
4429
5737
  }
4430
- return absolutePrimitiveContainerTargetForPoint(el, clientX, clientY);
5738
+ return null;
4431
5739
  }
4432
5740
 
4433
5741
  function showInsertionGuideFor(target) {
@@ -4435,6 +5743,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4435
5743
  hideInsertionGuide();
4436
5744
  return;
4437
5745
  }
5746
+ // Compensate for the host's inverse-scale chrome model (see
5747
+ // applyEditorChromeScale / chromeLineScale): the host shrinks this iframe
5748
+ // via a CSS transform at low canvas zoom, so a hardcoded "2px" line here
5749
+ // would render sub-pixel (effectively invisible) at typical overview zoom
5750
+ // levels — this was the actual regression, not a missing code path. Every
5751
+ // other chrome line/border in this file (selection border, spacing lines,
5752
+ // handle borders) already scales by chromeLineScale(); the insertion
5753
+ // guide must match so it stays a visible bright line at any zoom.
5754
+ var line = 2 * chromeLineScale();
5755
+ var insideBorder = 2 * chromeLineScale();
4438
5756
  var rect = target.anchor.getBoundingClientRect();
4439
5757
  insertionGuide.style.display = "block";
4440
5758
  insertionGuide.style.background = "var(--design-editor-accent-color)";
@@ -4450,23 +5768,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4450
5768
  insertionGuide.style.background =
4451
5769
  "color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)";
4452
5770
  insertionGuide.style.border =
4453
- "2px solid var(--design-editor-accent-color)";
5771
+ insideBorder + "px solid var(--design-editor-accent-color)";
4454
5772
  insertionGuide.style.borderRadius = "2px";
4455
5773
  insertionGuide.style.boxShadow = "none";
4456
5774
  return;
4457
5775
  }
4458
5776
  if (target.axis === "x") {
4459
5777
  var x = target.placement === "before" ? rect.left : rect.right;
4460
- insertionGuide.style.left = x + "px";
5778
+ insertionGuide.style.left = x - line / 2 + "px";
4461
5779
  insertionGuide.style.top = rect.top + "px";
4462
- insertionGuide.style.width = "2px";
5780
+ insertionGuide.style.width = line + "px";
4463
5781
  insertionGuide.style.height = rect.height + "px";
4464
5782
  } else {
4465
5783
  var y = target.placement === "before" ? rect.top : rect.bottom;
4466
5784
  insertionGuide.style.left = rect.left + "px";
4467
- insertionGuide.style.top = y + "px";
5785
+ insertionGuide.style.top = y - line / 2 + "px";
4468
5786
  insertionGuide.style.width = rect.width + "px";
4469
- insertionGuide.style.height = "2px";
5787
+ insertionGuide.style.height = line + "px";
4470
5788
  }
4471
5789
  }
4472
5790
 
@@ -4532,9 +5850,231 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4532
5850
  );
4533
5851
  }
4534
5852
 
4535
- function startMove(e) {
4536
- if (!selectedEl) return;
4537
- if (isLayerInteractionBlocked(selectedEl)) return;
5853
+ // Multi-select group drop: land every member of the group CONSECUTIVELY at
5854
+ // the drop target, preserving their existing document order (standard
5855
+ // design-tool group-drop semantics). `members` must already be in document
5856
+ // order (collectMoveGroupMembers guarantees it). The first member takes the
5857
+ // real drop target; each subsequent member chains "after" the previous one
5858
+ // so the group stays contiguous regardless of the target placement mode.
5859
+ // Persistence reuses the existing per-element visual-structure-change
5860
+ // message — one per member, posted in order, which the host composes
5861
+ // sequentially against its synchronous same-tick content refs (the same
5862
+ // established multi-message pattern as the auto-layout conversion +
5863
+ // structure change pairing in onUp). The host handler collapses its
5864
+ // selection to each moved node as it processes each message, so a final
5865
+ // marquee-selection message restores the full multi-selection afterwards
5866
+ // (requirement: selection stays intact after a group drop).
5867
+ function applyGroupStructureDrop(members: Element[], target, ev): void {
5868
+ var container = dropContainerForTarget(target);
5869
+ var previous: Element | null = null;
5870
+ for (var i = 0; i < members.length; i += 1) {
5871
+ var member = members[i];
5872
+ var memberTarget =
5873
+ i === 0
5874
+ ? target
5875
+ : target.dropMode === "absolute-container"
5876
+ ? target
5877
+ : {
5878
+ anchor: previous,
5879
+ placement: "after",
5880
+ axis: target.axis,
5881
+ dropMode: "flow-insert",
5882
+ };
5883
+ var prevParent = member.parentElement;
5884
+ var prevNextSibling = member.nextSibling;
5885
+ // Board-text auto-color: adapt before the DOM move so the re-parent
5886
+ // check sees the ORIGINAL parent (see adaptAutoTextColorForNest).
5887
+ adaptAutoTextColorForNest(member, container);
5888
+ applyRuntimeReorder(member, memberTarget);
5889
+ postVisualStructureChange(member, memberTarget, {
5890
+ prevParent: prevParent,
5891
+ prevNextSibling: prevNextSibling,
5892
+ });
5893
+ previous = member;
5894
+ }
5895
+ postElementMarqueeSelect(members, false, ev);
5896
+ }
5897
+
5898
+ // ── Alignment / smart-guide snapping (Figma parity) ───────────────────────
5899
+ //
5900
+ // Minimal, dependency-free port of the overview canvas's edge/center snap
5901
+ // routine (shared/canvas-math.ts computeMoveSnap) for in-iframe element
5902
+ // dragging. The bridge's pointer coordinates and getBoundingClientRect()
5903
+ // values are already in the same iframe-local, zoom-normalized coordinate
5904
+ // space (the host CSS-scales the whole iframe, not individual elements),
5905
+ // so — unlike the overview canvas, which divides a screen-px threshold by
5906
+ // its own camera zoom — no extra scale correction is needed here.
5907
+ var SNAP_THRESHOLD_PX = 6;
5908
+ var SNAP_CANDIDATE_CAP = 200;
5909
+
5910
+ // Accepts either a real DOMRect (getBoundingClientRect()) or a plain
5911
+ // {left, top, width, height} object (the moving element's live drag rect,
5912
+ // which doesn't have its own DOMRect during the drag since it's derived
5913
+ // from pointer deltas) — right/bottom are always derived from
5914
+ // left/top/width/height so both shapes work identically.
5915
+ function rectBounds(rect) {
5916
+ return {
5917
+ left: rect.left,
5918
+ top: rect.top,
5919
+ right: rect.left + rect.width,
5920
+ bottom: rect.top + rect.height,
5921
+ centerX: rect.left + rect.width / 2,
5922
+ centerY: rect.top + rect.height / 2,
5923
+ };
5924
+ }
5925
+
5926
+ // Collects candidate rects to snap against: visible sibling elements that
5927
+ // share the dragged element's offsetParent, plus the parent's own content
5928
+ // box. Capped and computed once (one getBoundingClientRect pass) at drag
5929
+ // start rather than per move event, per the perf requirement below.
5930
+ // `excludeEls` (optional): other members of a multi-select group drag —
5931
+ // they move together with dragEl, so snapping against them would chase a
5932
+ // moving target.
5933
+ function collectSnapCandidateRects(dragEl, excludeEls) {
5934
+ var rects = [];
5935
+ var excluded: Element[] = excludeEls || [];
5936
+ var parent = dragEl && dragEl.parentElement;
5937
+ if (parent) {
5938
+ var parentRect = parent.getBoundingClientRect();
5939
+ if (parentRect.width > 0 && parentRect.height > 0) {
5940
+ rects.push(rectBounds(parentRect));
5941
+ }
5942
+ }
5943
+ var offsetParent = dragEl && (dragEl as HTMLElement).offsetParent;
5944
+ if (parent) {
5945
+ var siblings = Array.prototype.slice.call(parent.children);
5946
+ for (
5947
+ var i = 0;
5948
+ i < siblings.length && rects.length < SNAP_CANDIDATE_CAP;
5949
+ i += 1
5950
+ ) {
5951
+ var sibling = siblings[i];
5952
+ if (
5953
+ !sibling ||
5954
+ sibling === dragEl ||
5955
+ excluded.indexOf(sibling) !== -1 ||
5956
+ sibling.nodeType !== 1 ||
5957
+ isOverlayElement(sibling)
5958
+ ) {
5959
+ continue;
5960
+ }
5961
+ if (
5962
+ offsetParent &&
5963
+ (sibling as HTMLElement).offsetParent !== offsetParent
5964
+ ) {
5965
+ continue;
5966
+ }
5967
+ var cs = window.getComputedStyle(sibling);
5968
+ if (cs.display === "none" || cs.visibility === "hidden") continue;
5969
+ var rect = sibling.getBoundingClientRect();
5970
+ if (rect.width <= 0 || rect.height <= 0) continue;
5971
+ rects.push(rectBounds(rect));
5972
+ }
5973
+ }
5974
+ return rects;
5975
+ }
5976
+
5977
+ // Mirrors getAxisSnapCandidates/getBestCandidate/getVerticalGuide/
5978
+ // getHorizontalGuide from shared/canvas-math.ts: for each axis, compare the
5979
+ // moving rect's left/center/right (or top/center/bottom) against every
5980
+ // candidate's same three values and keep the closest match within
5981
+ // threshold. Exported off the IIFE closure via the return-value shape below
5982
+ // so tests can exercise it directly (see the "extractable pure logic"
5983
+ // convention used by motion-preview.bridge.ts).
5984
+ function computeMoveSnapOffset(movingRect, candidates, threshold) {
5985
+ var moving = rectBounds(movingRect);
5986
+ var bestX = null;
5987
+ var bestY = null;
5988
+ for (var i = 0; i < candidates.length; i += 1) {
5989
+ var candidate = candidates[i];
5990
+ var xValues = [moving.left, moving.centerX, moving.right];
5991
+ var xTargets = [candidate.left, candidate.centerX, candidate.right];
5992
+ for (var xi = 0; xi < xValues.length; xi += 1) {
5993
+ for (var xj = 0; xj < xTargets.length; xj += 1) {
5994
+ var offsetX = xTargets[xj] - xValues[xi];
5995
+ var distanceX = Math.abs(offsetX);
5996
+ if (distanceX > threshold) continue;
5997
+ if (!bestX || distanceX < bestX.distance) {
5998
+ bestX = {
5999
+ distance: distanceX,
6000
+ offset: offsetX,
6001
+ guide: {
6002
+ position: xTargets[xj],
6003
+ start: Math.min(moving.top, candidate.top),
6004
+ end: Math.max(moving.bottom, candidate.bottom),
6005
+ },
6006
+ };
6007
+ }
6008
+ }
6009
+ }
6010
+ var yValues = [moving.top, moving.centerY, moving.bottom];
6011
+ var yTargets = [candidate.top, candidate.centerY, candidate.bottom];
6012
+ for (var yi = 0; yi < yValues.length; yi += 1) {
6013
+ for (var yj = 0; yj < yTargets.length; yj += 1) {
6014
+ var offsetY = yTargets[yj] - yValues[yi];
6015
+ var distanceY = Math.abs(offsetY);
6016
+ if (distanceY > threshold) continue;
6017
+ if (!bestY || distanceY < bestY.distance) {
6018
+ bestY = {
6019
+ distance: distanceY,
6020
+ offset: offsetY,
6021
+ guide: {
6022
+ position: yTargets[yj],
6023
+ start: Math.min(moving.left, candidate.left),
6024
+ end: Math.max(moving.right, candidate.right),
6025
+ },
6026
+ };
6027
+ }
6028
+ }
6029
+ }
6030
+ }
6031
+ return {
6032
+ dx: bestX ? bestX.offset : 0,
6033
+ dy: bestY ? bestY.offset : 0,
6034
+ guideV: bestX ? bestX.guide : null,
6035
+ guideH: bestY ? bestY.guide : null,
6036
+ };
6037
+ }
6038
+
6039
+ function showSnapGuides(guideV, guideH) {
6040
+ // Constant-screen-size chrome: guide THICKNESS compensates for the
6041
+ // host's iframe scale (chromeLineScale) so the line stays a crisp 1px
6042
+ // on screen at any zoom; the guide's span/position stays in content
6043
+ // coordinates.
6044
+ var line = 1 * chromeLineScale();
6045
+ if (guideV) {
6046
+ snapGuideV.style.display = "block";
6047
+ snapGuideV.style.width = line + "px";
6048
+ snapGuideV.style.left = Math.round(guideV.position) + "px";
6049
+ snapGuideV.style.top = Math.round(guideV.start) + "px";
6050
+ snapGuideV.style.height = Math.max(1, guideV.end - guideV.start) + "px";
6051
+ } else {
6052
+ snapGuideV.style.display = "none";
6053
+ }
6054
+ if (guideH) {
6055
+ snapGuideH.style.display = "block";
6056
+ snapGuideH.style.height = line + "px";
6057
+ snapGuideH.style.top = Math.round(guideH.position) + "px";
6058
+ snapGuideH.style.left = Math.round(guideH.start) + "px";
6059
+ snapGuideH.style.width = Math.max(1, guideH.end - guideH.start) + "px";
6060
+ } else {
6061
+ snapGuideH.style.display = "none";
6062
+ }
6063
+ }
6064
+
6065
+ function hideSnapGuides() {
6066
+ snapGuideV.style.display = "none";
6067
+ snapGuideH.style.display = "none";
6068
+ }
6069
+
6070
+ // `gestureElParam` (optional): the specific multi-selection member the
6071
+ // pointer went down on when this drag preserves a 2+ selection instead of
6072
+ // collapsing to one element (see beginPotentialShieldDrag's group branch).
6073
+ // Defaults to selectedEl — the selection-overlay drag path.
6074
+ function startMove(e, gestureElParam?: Element) {
6075
+ var gestureEl = gestureElParam || selectedEl;
6076
+ if (!gestureEl) return;
6077
+ if (isLayerInteractionBlocked(gestureEl)) return;
4538
6078
  e.preventDefault();
4539
6079
  e.stopPropagation();
4540
6080
  if (e.stopImmediatePropagation) e.stopImmediatePropagation();
@@ -4543,6 +6083,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4543
6083
  var duplicatedForDrag = false;
4544
6084
  if (
4545
6085
  e.altKey &&
6086
+ selectedEl &&
4546
6087
  selectedEl !== document.body &&
4547
6088
  selectedEl !== document.documentElement
4548
6089
  ) {
@@ -4551,17 +6092,38 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4551
6092
  selectedEl.parentElement.insertBefore(clone, selectedEl.nextSibling);
4552
6093
  selectedEl = clone;
4553
6094
  duplicatedForDrag = true;
6095
+ gestureEl = clone;
4554
6096
  positionOverlay(selectionOverlay, selectedEl);
4555
6097
  postElementSelect(selectedEl, e);
4556
6098
  }
4557
- if (isFlowReorderCandidate(selectedEl)) {
6099
+ // Multi-select group move: every member of the current 2+ selection moves
6100
+ // with the gesture when the drag started on a member. Alt-drag duplicates
6101
+ // stay single-element (the clone is never part of a selection group).
6102
+ var groupEls: Element[] =
6103
+ duplicatedForDrag || e.altKey
6104
+ ? [gestureEl]
6105
+ : collectMoveGroupMembers(gestureEl);
6106
+ if (groupEls.indexOf(gestureEl) === -1) groupEls = [gestureEl];
6107
+ var isGroupDrag = groupEls.length > 1;
6108
+ var groupOthers = groupEls.filter(function (member) {
6109
+ return member !== gestureEl;
6110
+ });
6111
+ if (isGroupDrag) {
6112
+ // beginPotentialShieldDrag armed the host's cross-screen drag state for
6113
+ // a single element before this group drag was detected; clear it. Group
6114
+ // drags stay in-iframe — the host's cross-screen drop only knows how to
6115
+ // move one element, which would tear the group apart.
6116
+ postCrossScreenDrag("cancel");
6117
+ }
6118
+ if (isFlowReorderCandidate(gestureEl)) {
4558
6119
  // Snapshot the element being reordered so a concurrent select-element or
4559
6120
  // clear-selection postMessage cannot mutate the wrong element mid-drag.
4560
- var reorderEl = selectedEl;
6121
+ var reorderEl = gestureEl;
4561
6122
  var currentTarget = reorderTargetForPoint(
4562
6123
  reorderEl,
4563
6124
  e.clientX,
4564
6125
  e.clientY,
6126
+ groupOthers,
4565
6127
  );
4566
6128
  showInsertionGuideFor(currentTarget);
4567
6129
  // Cross-screen drag state: true when the pointer is outside this iframe's
@@ -4584,30 +6146,34 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4584
6146
  var outside = cx < 0 || cy < 0 || cx > vw || cy > vh;
4585
6147
  pointerOutsideIframe = outside;
4586
6148
  // Always notify the host frame so it can track the cursor position,
4587
- // render the ghost, and highlight the target screen.
4588
- (window.parent as Window).postMessage(
4589
- {
4590
- type: "agent-native:cross-screen-drag",
4591
- phase: "move",
4592
- selector: reorderSelector,
4593
- sourceId: reorderSourceId,
4594
- iframeX: cx,
4595
- iframeY: cy,
4596
- viewportW: vw,
4597
- viewportH: vh,
4598
- pointerOffset: reorderPointerOffset,
4599
- styleSnapshot: reorderStyleSnapshot,
4600
- },
4601
- "*",
4602
- );
4603
- if (outside) {
6149
+ // render the ghost, and highlight the target screen. Group drags stay
6150
+ // in-iframe (the host's cross-screen drop moves a single element and
6151
+ // would tear the group apart), so they never arm the host.
6152
+ if (!isGroupDrag) {
6153
+ (window.parent as Window).postMessage(
6154
+ {
6155
+ type: "agent-native:cross-screen-drag",
6156
+ phase: "move",
6157
+ selector: reorderSelector,
6158
+ sourceId: reorderSourceId,
6159
+ iframeX: cx,
6160
+ iframeY: cy,
6161
+ viewportW: vw,
6162
+ viewportH: vh,
6163
+ pointerOffset: reorderPointerOffset,
6164
+ styleSnapshot: reorderStyleSnapshot,
6165
+ },
6166
+ "*",
6167
+ );
6168
+ }
6169
+ if (outside && !isGroupDrag) {
4604
6170
  // Cursor left this iframe — hide the in-iframe insertion guide so
4605
6171
  // it does not render while the host shows a cross-screen drop target.
4606
6172
  hideInsertionGuide();
4607
6173
  showTransformBadge("Move layer", cx, cy);
4608
6174
  } else {
4609
6175
  // Cursor is inside this iframe — use existing in-iframe behavior.
4610
- currentTarget = reorderTargetForPoint(reorderEl, cx, cy);
6176
+ currentTarget = reorderTargetForPoint(reorderEl, cx, cy, groupOthers);
4611
6177
  showInsertionGuideFor(currentTarget);
4612
6178
  showTransformBadge(currentTarget ? "Move layer" : "Move", cx, cy);
4613
6179
  }
@@ -4657,24 +6223,30 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4657
6223
  var cy = ev ? ev.clientY : 0;
4658
6224
  var outsideOnDrop = cx < 0 || cy < 0 || cx > vw || cy > vh;
4659
6225
  // Post the end message so the host can finalize a cross-screen drop.
4660
- (window.parent as Window).postMessage(
4661
- {
4662
- type: "agent-native:cross-screen-drag",
4663
- phase: "end",
4664
- selector: reorderSelector,
4665
- sourceId: reorderSourceId,
4666
- iframeX: cx,
4667
- iframeY: cy,
4668
- viewportW: vw,
4669
- viewportH: vh,
4670
- pointerOffset: reorderPointerOffset,
4671
- styleSnapshot: reorderStyleSnapshot,
4672
- },
4673
- "*",
4674
- );
6226
+ // Group drags never armed the host (see onReorderMove), so posting
6227
+ // end here would trigger a bogus single-element cross-screen move.
6228
+ if (!isGroupDrag) {
6229
+ (window.parent as Window).postMessage(
6230
+ {
6231
+ type: "agent-native:cross-screen-drag",
6232
+ phase: "end",
6233
+ selector: reorderSelector,
6234
+ sourceId: reorderSourceId,
6235
+ iframeX: cx,
6236
+ iframeY: cy,
6237
+ viewportW: vw,
6238
+ viewportH: vh,
6239
+ pointerOffset: reorderPointerOffset,
6240
+ styleSnapshot: reorderStyleSnapshot,
6241
+ },
6242
+ "*",
6243
+ );
6244
+ }
4675
6245
  // When the pointer is outside this iframe at release, the host owns the
4676
6246
  // move (cross-screen drop). Do NOT apply the in-iframe reorder so we
4677
- // avoid a ghost element left in screen A's DOM.
6247
+ // avoid a ghost element left in screen A's DOM. For group drags an
6248
+ // outside release is simply a no-op (nothing moved during a flow
6249
+ // reorder drag, so there is nothing to restore).
4678
6250
  // Use outsideOnDrop only — pointerOutsideIframe is stale when the user
4679
6251
  // briefly exits the iframe and re-enters before releasing. The host
4680
6252
  // already clears cross-screen state on re-entry so checking the
@@ -4703,11 +6275,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4703
6275
  reorderEl,
4704
6276
  currentTarget,
4705
6277
  );
6278
+ } else if (isGroupDrag) {
6279
+ applyGroupStructureDrop(groupEls, currentTarget, ev);
4706
6280
  } else {
4707
6281
  // Capture the pre-drag DOM anchor so we can revert if the parent
4708
6282
  // reports applied===false on the structure-ack.
4709
6283
  var prevParent = reorderEl.parentElement;
4710
6284
  var prevNextSibling = reorderEl.nextSibling;
6285
+ adaptAutoTextColorForNest(
6286
+ reorderEl,
6287
+ dropContainerForTarget(currentTarget),
6288
+ );
4711
6289
  // Optimistically apply the reorder in the DOM for immediate
4712
6290
  // visual feedback; the visual-structure-ack handler will confirm
4713
6291
  // or revert once the parent processes the change.
@@ -4724,28 +6302,63 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4724
6302
  setActiveDragCancel(onReorderEscape);
4725
6303
  return;
4726
6304
  }
4727
- var originalInlinePosition = (selectedEl as HTMLElement).style.position;
4728
- var originalInlineLeft = (selectedEl as HTMLElement).style.left;
4729
- var originalInlineTop = (selectedEl as HTMLElement).style.top;
4730
- var originalInlineOpacity = (selectedEl as HTMLElement).style.opacity;
4731
- ensurePositionable(selectedEl);
4732
- var cs = window.getComputedStyle(selectedEl);
4733
- var originLeft = readPx((selectedEl as HTMLElement).style.left || cs.left);
4734
- var originTop = readPx(selectedEl.style.top || cs.top);
6305
+ // Per-member drag state: inline-style snapshots (for escape-cancel
6306
+ // restore) plus each member's own drag origin. Single-element drags have
6307
+ // exactly one entry; group drags get one per multi-selection member so
6308
+ // the SAME delta can be applied to every member each tick, preserving
6309
+ // the group's relative offsets (Figma group-move semantics).
6310
+ var memberStates = groupEls.map(function (member) {
6311
+ var m = member as HTMLElement;
6312
+ var snapshot = {
6313
+ el: m,
6314
+ originalPosition: m.style.position,
6315
+ originalLeft: m.style.left,
6316
+ originalTop: m.style.top,
6317
+ originalOpacity: m.style.opacity,
6318
+ originLeft: 0,
6319
+ originTop: 0,
6320
+ };
6321
+ ensurePositionable(m);
6322
+ var mcs = window.getComputedStyle(m);
6323
+ snapshot.originLeft = readPx(m.style.left || mcs.left);
6324
+ snapshot.originTop = readPx(m.style.top || mcs.top);
6325
+ return snapshot;
6326
+ });
6327
+ var gestureState =
6328
+ memberStates[groupEls.indexOf(gestureEl)] || memberStates[0];
6329
+ var originalInlineOpacity = gestureState.originalOpacity;
6330
+ var originLeft = gestureState.originLeft;
6331
+ var originTop = gestureState.originTop;
6332
+ function setMembersOpacity(value: string | null): void {
6333
+ memberStates.forEach(function (state) {
6334
+ state.el.style.opacity = value === null ? state.originalOpacity : value;
6335
+ });
6336
+ }
4735
6337
  var startX = e.clientX;
4736
6338
  var startY = e.clientY;
4737
6339
  // Snapshot the element being moved so that a concurrent select-element or
4738
6340
  // clear-selection postMessage cannot swap selectedEl mid-drag and cause
4739
6341
  // mutations on the wrong element or a null-deref in onUp.
4740
- var dragEl = selectedEl;
6342
+ var dragEl = gestureEl;
4741
6343
  var moved = false;
4742
6344
  var DRAG_THRESHOLD = 3;
4743
6345
  var currentAutoLayoutTarget: {
4744
6346
  anchor: Element;
4745
6347
  placement: string;
4746
6348
  axis?: string;
6349
+ needsAutoLayoutConversion?: boolean;
6350
+ conversionTarget?: Element;
4747
6351
  } | null = null;
4748
- if (!duplicatedForDrag) {
6352
+ // Snap candidates (siblings + parent content box) are computed once at
6353
+ // drag start — a single getBoundingClientRect pass per candidate — not
6354
+ // recomputed on every move event. Other group members are excluded: they
6355
+ // move with the drag, so snapping against them would chase a moving
6356
+ // target.
6357
+ var snapCandidateRects = collectSnapCandidateRects(dragEl, groupOthers);
6358
+ var dragElStartRect = (dragEl as HTMLElement).getBoundingClientRect();
6359
+ var dragElStartWidth = dragElStartRect.width;
6360
+ var dragElStartHeight = dragElStartRect.height;
6361
+ if (!duplicatedForDrag && !isGroupDrag) {
4749
6362
  postCrossScreenDrag("start", dragEl, e);
4750
6363
  }
4751
6364
  function onMove(ev) {
@@ -4755,11 +6368,53 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4755
6368
  ) {
4756
6369
  moved = true;
4757
6370
  }
4758
- var nextLeft = originLeft + ev.clientX - startX;
4759
- var nextTop = originTop + ev.clientY - startY;
4760
- (dragEl as HTMLElement).style.left = Math.round(nextLeft) + "px";
4761
- (dragEl as HTMLElement).style.top = Math.round(nextTop) + "px";
4762
- if (!duplicatedForDrag) {
6371
+ var rawDx = ev.clientX - startX;
6372
+ var rawDy = ev.clientY - startY;
6373
+ // Figma dominant-axis lock: while Shift is held, zero out whichever
6374
+ // delta has the smaller magnitude so the element only moves along one
6375
+ // axis. Read live off the move event (not cached at drag start) so
6376
+ // pressing/releasing Shift mid-drag re-evaluates every event, matching
6377
+ // how the resize path reads ev.shiftKey per-move rather than once.
6378
+ if (ev.shiftKey) {
6379
+ if (Math.abs(rawDx) > Math.abs(rawDy)) {
6380
+ rawDy = 0;
6381
+ } else {
6382
+ rawDx = 0;
6383
+ }
6384
+ }
6385
+ var nextLeft = originLeft + rawDx;
6386
+ var nextTop = originTop + rawDy;
6387
+ // Alignment/smart-guide snapping: disabled while Cmd/Ctrl is held
6388
+ // (Figma behavior) and while an auto-layout flow-insert is about to
6389
+ // happen instead of a free absolute placement (handled below once
6390
+ // currentAutoLayoutTarget is known for this tick).
6391
+ var snapBypass = Boolean(ev.metaKey || ev.ctrlKey);
6392
+ var snapResult =
6393
+ !snapBypass && !duplicatedForDrag
6394
+ ? computeMoveSnapOffset(
6395
+ {
6396
+ left: nextLeft,
6397
+ top: nextTop,
6398
+ width: dragElStartWidth,
6399
+ height: dragElStartHeight,
6400
+ },
6401
+ snapCandidateRects,
6402
+ SNAP_THRESHOLD_PX,
6403
+ )
6404
+ : { dx: 0, dy: 0, guideV: null, guideH: null };
6405
+ nextLeft += snapResult.dx;
6406
+ nextTop += snapResult.dy;
6407
+ // Apply the SAME delta to every member (one entry for single drags)
6408
+ // so relative offsets within a multi-selection are preserved. For the
6409
+ // gesture member this reduces exactly to the previous
6410
+ // Math.round(nextLeft/nextTop) single-element behavior.
6411
+ var appliedDx = nextLeft - originLeft;
6412
+ var appliedDy = nextTop - originTop;
6413
+ memberStates.forEach(function (state) {
6414
+ state.el.style.left = Math.round(state.originLeft + appliedDx) + "px";
6415
+ state.el.style.top = Math.round(state.originTop + appliedDy) + "px";
6416
+ });
6417
+ if (!duplicatedForDrag && !isGroupDrag) {
4763
6418
  postCrossScreenDrag("move", dragEl, ev);
4764
6419
  }
4765
6420
  if (
@@ -4768,27 +6423,51 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4768
6423
  ) {
4769
6424
  currentAutoLayoutTarget = null;
4770
6425
  hideInsertionGuide();
4771
- (dragEl as HTMLElement).style.opacity = originalInlineOpacity;
6426
+ setMembersOpacity(null);
4772
6427
  } else {
4773
6428
  currentAutoLayoutTarget = !duplicatedForDrag
4774
- ? autoLayoutInsertionTargetForPoint(dragEl, ev.clientX, ev.clientY)
6429
+ ? autoLayoutInsertionTargetForPoint(
6430
+ dragEl,
6431
+ ev.clientX,
6432
+ ev.clientY,
6433
+ groupOthers,
6434
+ )
4775
6435
  : null;
4776
6436
  if (currentAutoLayoutTarget) {
4777
6437
  showInsertionGuideFor(currentAutoLayoutTarget);
4778
- (dragEl as HTMLElement).style.opacity = "0.4";
6438
+ setMembersOpacity("0.4");
4779
6439
  } else {
4780
6440
  hideInsertionGuide();
4781
- (dragEl as HTMLElement).style.opacity = originalInlineOpacity;
6441
+ setMembersOpacity(null);
4782
6442
  }
4783
6443
  }
4784
- hideTransformBadge();
6444
+ // Snap guides only make sense for a free absolute placement — never at
6445
+ // once alongside the auto-layout flow-insert indicator (the element is
6446
+ // about to be reflowed into a flex/grid slot, not placed at an x/y
6447
+ // coordinate), and never while the pointer has left the iframe (the
6448
+ // host owns a cross-screen drop at that point).
6449
+ if (
6450
+ currentAutoLayoutTarget ||
6451
+ (!duplicatedForDrag && isOutsideIframeViewport(ev.clientX, ev.clientY))
6452
+ ) {
6453
+ hideSnapGuides();
6454
+ } else {
6455
+ showSnapGuides(snapResult.guideV, snapResult.guideH);
6456
+ }
6457
+ showTransformBadge(
6458
+ Math.round(nextLeft) + ", " + Math.round(nextTop),
6459
+ ev.clientX,
6460
+ ev.clientY,
6461
+ );
4785
6462
  refreshOverlays();
4786
6463
  }
4787
6464
  function restoreSourceDragPosition(): void {
4788
- (dragEl as HTMLElement).style.position = originalInlinePosition;
4789
- (dragEl as HTMLElement).style.left = originalInlineLeft;
4790
- (dragEl as HTMLElement).style.top = originalInlineTop;
4791
- (dragEl as HTMLElement).style.opacity = originalInlineOpacity;
6465
+ memberStates.forEach(function (state) {
6466
+ state.el.style.position = state.originalPosition;
6467
+ state.el.style.left = state.originalLeft;
6468
+ state.el.style.top = state.originalTop;
6469
+ state.el.style.opacity = state.originalOpacity;
6470
+ });
4792
6471
  selectedEl = originalSelectedEl;
4793
6472
  positionOverlay(selectionOverlay, selectedEl);
4794
6473
  }
@@ -4802,6 +6481,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4802
6481
  cleanupMoveDrag();
4803
6482
  hideTransformBadge();
4804
6483
  hideInsertionGuide();
6484
+ hideSnapGuides();
4805
6485
  currentAutoLayoutTarget = null;
4806
6486
  if (duplicatedForDrag) {
4807
6487
  if (dragEl && dragEl.parentElement) {
@@ -4812,7 +6492,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4812
6492
  postElementSelect(selectedEl);
4813
6493
  } else if (dragEl && document.documentElement.contains(dragEl)) {
4814
6494
  restoreSourceDragPosition();
4815
- postCrossScreenDrag("cancel");
6495
+ if (!isGroupDrag) postCrossScreenDrag("cancel");
4816
6496
  }
4817
6497
  suppressNextShieldClickBriefly();
4818
6498
  refreshOverlays();
@@ -4827,6 +6507,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4827
6507
  cleanupMoveDrag();
4828
6508
  hideTransformBadge();
4829
6509
  hideInsertionGuide();
6510
+ hideSnapGuides();
4830
6511
  if (!dragEl) return;
4831
6512
  var outsideOnDrop = ev
4832
6513
  ? isOutsideIframeViewport(ev.clientX, ev.clientY)
@@ -4834,11 +6515,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4834
6515
  if (
4835
6516
  ev &&
4836
6517
  !duplicatedForDrag &&
6518
+ !isGroupDrag &&
4837
6519
  (outsideOnDrop || designCanvasBoardSurface)
4838
6520
  ) {
4839
6521
  postCrossScreenDrag("end", dragEl, ev);
4840
6522
  }
4841
6523
  if (ev && !duplicatedForDrag && outsideOnDrop) {
6524
+ // Outside release: the host owns a single-element cross-screen drop;
6525
+ // group drags never armed the host, so an outside release simply
6526
+ // restores every member (cancel semantics).
4842
6527
  restoreSourceDragPosition();
4843
6528
  return;
4844
6529
  }
@@ -4847,6 +6532,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4847
6532
  dragEl,
4848
6533
  ev.clientX,
4849
6534
  ev.clientY,
6535
+ groupOthers,
4850
6536
  );
4851
6537
  if (finalAutoLayoutTarget) {
4852
6538
  currentAutoLayoutTarget = finalAutoLayoutTarget;
@@ -4863,30 +6549,62 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4863
6549
  if (duplicatedForDrag) {
4864
6550
  postVisualDuplicateChange(originalSelectedEl, dragEl);
4865
6551
  } else if (currentAutoLayoutTarget) {
4866
- (dragEl as HTMLElement).style.opacity = originalInlineOpacity;
4867
- var prevParent = dragEl.parentElement;
4868
- var prevNextSibling = dragEl.nextSibling;
4869
- applyRuntimeReorder(dragEl, currentAutoLayoutTarget);
4870
- postVisualStructureChange(dragEl, currentAutoLayoutTarget, {
4871
- prevParent: prevParent,
4872
- prevNextSibling: prevNextSibling,
4873
- });
6552
+ setMembersOpacity(null);
6553
+ // Figma-parity nest-on-drop: the target container may be a plain
6554
+ // rect/div that isn't auto-layout yet (see
6555
+ // autoLayoutInsertionTargetForPoint's needsAutoLayoutConversion).
6556
+ // Convert it to flex BEFORE reparenting/posting the move so the
6557
+ // host applies the two edits in the right order against its
6558
+ // synchronous same-tick content refs (container becomes flex, then
6559
+ // the child moves into it and loses absolute positioning via the
6560
+ // existing "flow-insert" dropMode handling). For group drags the
6561
+ // conversion fires ONCE for the container; every member then nests
6562
+ // consecutively via applyGroupStructureDrop.
6563
+ if (
6564
+ currentAutoLayoutTarget.needsAutoLayoutConversion &&
6565
+ currentAutoLayoutTarget.conversionTarget
6566
+ ) {
6567
+ applyAutoLayoutConversionForDrop(
6568
+ currentAutoLayoutTarget.conversionTarget,
6569
+ groupEls,
6570
+ );
6571
+ }
6572
+ if (isGroupDrag) {
6573
+ applyGroupStructureDrop(groupEls, currentAutoLayoutTarget, ev);
6574
+ } else {
6575
+ var prevParent = dragEl.parentElement;
6576
+ var prevNextSibling = dragEl.nextSibling;
6577
+ adaptAutoTextColorForNest(
6578
+ dragEl,
6579
+ dropContainerForTarget(currentAutoLayoutTarget),
6580
+ );
6581
+ applyRuntimeReorder(dragEl, currentAutoLayoutTarget);
6582
+ postVisualStructureChange(dragEl, currentAutoLayoutTarget, {
6583
+ prevParent: prevParent,
6584
+ prevNextSibling: prevNextSibling,
6585
+ });
6586
+ }
4874
6587
  } else {
4875
- (dragEl as HTMLElement).style.opacity = originalInlineOpacity;
4876
- (window.parent as Window).postMessage(
4877
- {
4878
- type: "visual-style-change",
4879
- selector: getSelector(dragEl),
4880
- styles: {
4881
- position: (dragEl as HTMLElement).style.position,
4882
- left: (dragEl as HTMLElement).style.left,
4883
- top: (dragEl as HTMLElement).style.top,
6588
+ setMembersOpacity(null);
6589
+ // Free absolute placement: one style-change message per member, in
6590
+ // order — the host composes them against its synchronous same-tick
6591
+ // content refs exactly like multi-property style commits.
6592
+ memberStates.forEach(function (state) {
6593
+ (window.parent as Window).postMessage(
6594
+ {
6595
+ type: "visual-style-change",
6596
+ selector: getSelector(state.el),
6597
+ styles: {
6598
+ position: state.el.style.position,
6599
+ left: state.el.style.left,
6600
+ top: state.el.style.top,
6601
+ },
6602
+ payload: getElementInfo(state.el),
4884
6603
  },
4885
- payload: getElementInfo(dragEl),
4886
- },
4887
- "*",
4888
- );
4889
- postCrossScreenDrag("cancel");
6604
+ "*",
6605
+ );
6606
+ });
6607
+ if (!isGroupDrag) postCrossScreenDrag("cancel");
4890
6608
  }
4891
6609
  }
4892
6610
  document.addEventListener(events.move, onMove, true);
@@ -4909,6 +6627,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4909
6627
  var originalInlineTop = resizeEl.style.top;
4910
6628
  var originalInlineWidth = resizeEl.style.width;
4911
6629
  var originalInlineHeight = resizeEl.style.height;
6630
+ var originalInlineBorderWidth = resizeEl.style.borderWidth;
6631
+ var originalInlineFontSize = resizeEl.style.fontSize;
4912
6632
  ensurePositionable(resizeEl);
4913
6633
  var cs = window.getComputedStyle(resizeEl);
4914
6634
  // Bug fix: use CSS width/height (not getBoundingClientRect) for the resize
@@ -4916,6 +6636,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
4916
6636
  // axis-aligned bounding box as the starting size.
4917
6637
  var originW = readPx(resizeEl.style.width || cs.width);
4918
6638
  var originH = readPx(resizeEl.style.height || cs.height);
6639
+ // K-scale (Figma "Scale" tool) parity: capture the element's own border
6640
+ // width and font size once at drag-start so a uniform per-tick scale
6641
+ // factor (derived from width/height growth, see nextRect below) can
6642
+ // multiply them proportionally, exactly like Figma's Scale tool resizes
6643
+ // stroke weight and text size along with the box — a *normal* resize
6644
+ // (scaleToolEnabled false) never touches either. Uses the CSS
6645
+ // borderWidth/fontSize shorthand (not per-side border-*-width) since
6646
+ // canvas-primitive-style.ts / appendCanvasPrimitiveToHtml only ever set a
6647
+ // uniform border on these elements; a hand-authored per-side border is
6648
+ // left untouched (readPx on the shorthand returns 0 for mixed values,
6649
+ // which multiplies to 0 — an explicit non-goal edge case, not silently
6650
+ // wrong: scaleToolEnabled is opt-in and mixed-width borders on a
6651
+ // draggable primitive are not part of this app's authored shapes).
6652
+ var originBorderWidth = readPx(
6653
+ resizeEl.style.borderWidth || cs.borderWidth,
6654
+ );
6655
+ var originFontSize = readPx(resizeEl.style.fontSize || cs.fontSize);
4919
6656
  var origin = {
4920
6657
  left: readPx(resizeEl.style.left || cs.left),
4921
6658
  top: readPx(resizeEl.style.top || cs.top),
@@ -5014,6 +6751,25 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5014
6751
  resizeEl.style.top = Math.round(rect.top) + "px";
5015
6752
  resizeEl.style.width = Math.round(rect.width) + "px";
5016
6753
  resizeEl.style.height = Math.round(rect.height) + "px";
6754
+ if (scaleToolEnabled) {
6755
+ // Uniform scale factor: scaleToolEnabled already forces the
6756
+ // aspect-ratio lock above (nextRect), so width/origin.width and
6757
+ // height/origin.height agree (barring the min-size clamp's rounding)
6758
+ // — width is the simpler, always-defined choice.
6759
+ var kScaleFactor = rect.width / Math.max(1, origin.width);
6760
+ if (originBorderWidth > 0) {
6761
+ resizeEl.style.borderWidth =
6762
+ Math.max(
6763
+ 0,
6764
+ Math.round(originBorderWidth * kScaleFactor * 100) / 100,
6765
+ ) + "px";
6766
+ }
6767
+ if (originFontSize > 0) {
6768
+ resizeEl.style.fontSize =
6769
+ Math.max(1, Math.round(originFontSize * kScaleFactor * 100) / 100) +
6770
+ "px";
6771
+ }
6772
+ }
5017
6773
  showTransformBadge(
5018
6774
  Math.round(rect.width) + " x " + Math.round(rect.height),
5019
6775
  ev.clientX,
@@ -5036,6 +6792,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5036
6792
  resizeEl.style.top = originalInlineTop;
5037
6793
  resizeEl.style.width = originalInlineWidth;
5038
6794
  resizeEl.style.height = originalInlineHeight;
6795
+ resizeEl.style.borderWidth = originalInlineBorderWidth;
6796
+ resizeEl.style.fontSize = originalInlineFontSize;
5039
6797
  selectedEl = resizeEl;
5040
6798
  positionOverlay(selectionOverlay, selectedEl);
5041
6799
  }
@@ -5052,17 +6810,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5052
6810
  cleanupResizeDrag();
5053
6811
  hideTransformBadge();
5054
6812
  if (!resizeEl) return;
6813
+ var styles: Record<string, string> = {
6814
+ position: resizeEl.style.position,
6815
+ left: resizeEl.style.left,
6816
+ top: resizeEl.style.top,
6817
+ width: resizeEl.style.width,
6818
+ height: resizeEl.style.height,
6819
+ };
6820
+ // Only include borderWidth/fontSize when the K-scale tool actually
6821
+ // changed them (originBorderWidth/originFontSize > 0 AND
6822
+ // scaleToolEnabled) — a normal resize must never introduce these keys,
6823
+ // matching the "normal resize unchanged" requirement.
6824
+ if (scaleToolEnabled && originBorderWidth > 0) {
6825
+ styles.borderWidth = resizeEl.style.borderWidth;
6826
+ }
6827
+ if (scaleToolEnabled && originFontSize > 0) {
6828
+ styles.fontSize = resizeEl.style.fontSize;
6829
+ }
5055
6830
  (window.parent as Window).postMessage(
5056
6831
  {
5057
6832
  type: "visual-style-change",
5058
6833
  selector: getSelector(resizeEl),
5059
- styles: {
5060
- position: resizeEl.style.position,
5061
- left: resizeEl.style.left,
5062
- top: resizeEl.style.top,
5063
- width: resizeEl.style.width,
5064
- height: resizeEl.style.height,
5065
- },
6834
+ styles: styles,
5066
6835
  payload: getElementInfo(resizeEl),
5067
6836
  },
5068
6837
  "*",
@@ -5232,6 +7001,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5232
7001
  if (Math.hypot(ev.clientX - startX, ev.clientY - startY) <= 3) return;
5233
7002
  clearPendingShieldDrag();
5234
7003
  didStartDrag = true;
7004
+ // Multi-select group move: when the drag starts on a member of the
7005
+ // current 2+ selection, PRESERVE the whole selection (no
7006
+ // selectTarget collapse) and move the group together — Figma
7007
+ // behavior. A plain click (no drag) still collapses to the clicked
7008
+ // element via onUp below (existing disambiguation). Alt-drag
7009
+ // duplication stays single-element.
7010
+ var groupGestureMember = !e.altKey
7011
+ ? groupMemberForGestureTarget(dragTarget)
7012
+ : null;
7013
+ if (
7014
+ groupGestureMember &&
7015
+ collectMoveGroupMembers(groupGestureMember).length > 1
7016
+ ) {
7017
+ suppressNextShieldClickBriefly();
7018
+ startMove(ev, groupGestureMember);
7019
+ return;
7020
+ }
5235
7021
  selectTarget(dragTarget, ev);
5236
7022
  suppressNextShieldClickBriefly();
5237
7023
  startMove(ev);
@@ -5400,6 +7186,27 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5400
7186
  true,
5401
7187
  );
5402
7188
 
7189
+ // Space-pan release: keydown forwarding above arms the parent's temporary
7190
+ // hand tool (see postDesignHotkey/"design-hotkey"), but the parent also
7191
+ // needs the matching keyup to release it — without this, holding Space
7192
+ // inside the preview iframe would arm panning but never let go. Forwarded
7193
+ // as its own message (not reusing "design-hotkey", which the parent only
7194
+ // ever re-dispatches as a synthetic keydown) so the parent can drive its
7195
+ // real keyup-driven release logic.
7196
+ document.addEventListener(
7197
+ "keyup",
7198
+ function (e) {
7199
+ if (e.key !== " " || e.code !== "Space") return;
7200
+ if (activeTextEditEl || isEditorTypingTarget(e.target)) return;
7201
+ stopNativeInteraction(e);
7202
+ (window.parent as Window).postMessage(
7203
+ { type: "design-hotkey-up", key: e.key, code: e.code },
7204
+ "*",
7205
+ );
7206
+ },
7207
+ true,
7208
+ );
7209
+
5403
7210
  function hasFigmaClipboardPayload(value) {
5404
7211
  return /<[^>]+\sdata-(metadata|buffer)=["'][^"']*\((figmeta|figma)\)[^"']*["']/i.test(
5405
7212
  String(value || ""),
@@ -5507,11 +7314,56 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5507
7314
  programmaticFlag && !isRejectedRawTextEditTarget(eventTarget)
5508
7315
  ? eventTarget
5509
7316
  : null;
5510
- var target =
5511
- findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY)) ||
5512
- findTextEditTarget(eventTarget) ||
5513
- rawTargetFallback;
5514
- if (!target || target.nodeType !== 1) return;
7317
+ // Programmatic edits (e.g. begin-text-edit on a just-created text node)
7318
+ // already carry the exact node to edit as e.target. A freshly-created text
7319
+ // node is 0×0, so elementFromEditorPoint at its synthesized edge point
7320
+ // resolves to whatever is underneath — the parent screen container
7321
+ // (<main>) and editing would bind to the ENTIRE screen instead of the new
7322
+ // node (keystrokes land in the wrong element, the node stays empty, focus is
7323
+ // lost). So for the programmatic path, honor the explicit target first and
7324
+ // never re-resolve from a point.
7325
+ var target = programmaticFlag
7326
+ ? // Prefer the raw explicit node (rawTargetFallback === eventTarget) over
7327
+ // findTextEditTarget, which climbs UP to the highest inline-editable
7328
+ // ancestor (→ <main>) and would put the whole screen into edit mode.
7329
+ rawTargetFallback || findTextEditTarget(eventTarget)
7330
+ : findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY)) ||
7331
+ findTextEditTarget(eventTarget) ||
7332
+ rawTargetFallback;
7333
+ if (!target || target.nodeType !== 1) {
7334
+ // Figma parity: double-clicking a non-text element descends one level
7335
+ // into the current selection instead of doing nothing — select the
7336
+ // hit-tested element under the pointer (selectionTargetForHit already
7337
+ // returns the raw, deeper hit when it falls inside the current
7338
+ // selection, and climbs to the nearest stable-source ancestor
7339
+ // otherwise, so this reuses the same selection-filtering rules a
7340
+ // normal click uses). Skip this for the programmatic path: there is no
7341
+ // real pointer position to hit-test, and we already tried the explicit
7342
+ // target above.
7343
+ if (!programmaticFlag) {
7344
+ var descendHit = elementFromEditorPoint(e.clientX, e.clientY);
7345
+ if (
7346
+ descendHit &&
7347
+ descendHit !== document.body &&
7348
+ descendHit !== document.documentElement &&
7349
+ !isLayerInteractionBlocked(descendHit)
7350
+ ) {
7351
+ var previousSelectedElForDescend = selectedEl;
7352
+ var descendTarget = selectionTargetForHit(descendHit);
7353
+ if (descendTarget && !isLayerInteractionBlocked(descendTarget)) {
7354
+ selectedEl = descendTarget;
7355
+ positionOverlay(selectionOverlay, selectedEl);
7356
+ preservePreviousSelectedElementForShiftClick(
7357
+ previousSelectedElForDescend,
7358
+ selectedEl,
7359
+ e,
7360
+ );
7361
+ postElementSelect(selectedEl, e);
7362
+ }
7363
+ }
7364
+ }
7365
+ return;
7366
+ }
5515
7367
  // Anchor the selection identity to the nearest source-backed element. Text
5516
7368
  // editing still operates on the actual target text node, but a later
5517
7369
  // style edit posts from selectedEl, so it must point at a patchable
@@ -5665,10 +7517,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5665
7517
  scheduleTextEditingChromeUpdate();
5666
7518
  return;
5667
7519
  }
5668
- // T21: forward Cmd/Ctrl+B and Cmd/Ctrl+I within the edit session to
5669
- // execCommand bold/italic on the current selection. normalizeNestedIdenticalSpans
5670
- // (T12) cleans up any span nesting execCommand leaves behind when the
5671
- // session commits.
7520
+ // T21: forward Cmd/Ctrl+B, Cmd/Ctrl+I, and Cmd/Ctrl+U within the edit
7521
+ // session to execCommand bold/italic/underline on the current selection.
7522
+ // normalizeNestedIdenticalSpans (T12) cleans up any span nesting
7523
+ // execCommand leaves behind when the session commits.
5672
7524
  var metaOrCtrl = ev.metaKey || ev.ctrlKey;
5673
7525
  if (metaOrCtrl && !ev.altKey && ev.key.toLowerCase() === "b") {
5674
7526
  ev.preventDefault();
@@ -5682,6 +7534,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5682
7534
  scheduleTextEditingChromeUpdate();
5683
7535
  return;
5684
7536
  }
7537
+ if (metaOrCtrl && !ev.altKey && ev.key.toLowerCase() === "u") {
7538
+ ev.preventDefault();
7539
+ document.execCommand("underline");
7540
+ scheduleTextEditingChromeUpdate();
7541
+ return;
7542
+ }
5685
7543
  }
5686
7544
 
5687
7545
  function onPaste(ev) {
@@ -5708,7 +7566,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5708
7566
  target.addEventListener("mouseup", onSelectionChange, true);
5709
7567
  document.addEventListener("selectionchange", onSelectionChange);
5710
7568
  target.focus();
5711
- placeTextCaretFromPoint(target, e.clientX, e.clientY);
7569
+ if (programmaticTextEdit) {
7570
+ // The synthesized point sits at the (0×0) node's edge and resolves to the
7571
+ // parent element, so caretRangeFromPoint would drop the caret OUTSIDE the
7572
+ // editable node. Collapse to the end of the target's own contents instead.
7573
+ try {
7574
+ var progRange = document.createRange();
7575
+ progRange.selectNodeContents(target);
7576
+ progRange.collapse(false);
7577
+ var progSel = window.getSelection();
7578
+ progSel.removeAllRanges();
7579
+ progSel.addRange(progRange);
7580
+ } catch {
7581
+ /* selection APIs unavailable — focus() alone still enables typing */
7582
+ }
7583
+ } else {
7584
+ placeTextCaretFromPoint(target, e.clientX, e.clientY);
7585
+ }
5712
7586
  }
5713
7587
 
5714
7588
  shieldOverlay.addEventListener("dblclick", beginTextEditingFromEvent, true);
@@ -5751,7 +7625,21 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5751
7625
  if (hoveringSelectedSpacingSurface) {
5752
7626
  clearSpacingHoverTimer();
5753
7627
  selectedSpacingHovered = true;
7628
+ lastSpacingPointerPoint = { x: e.clientX, y: e.clientY };
5754
7629
  updateSpacingOverlay(selectedEl);
7630
+ // Reliable padding/gap hover: hit-test the handle geometry
7631
+ // directly from the pointer position instead of depending on the
7632
+ // pointermove's event target being the region node (see
7633
+ // spacingHandleKeyAtPoint). Shows/updates the "Npx" value box
7634
+ // while hovering the handle line; clears it when the pointer
7635
+ // leaves the tolerance zone.
7636
+ var pointSpacingKey = spacingHandleKeyAtPoint(e.clientX, e.clientY);
7637
+ if (pointSpacingKey) {
7638
+ activateSpacingHandle(pointSpacingKey);
7639
+ } else if (hoveredSpacingHandleKey) {
7640
+ hoveredSpacingHandleKey = "";
7641
+ updateSpacingOverlay(selectedEl);
7642
+ }
5755
7643
  } else {
5756
7644
  scheduleSpacingHoverClear(e);
5757
7645
  }
@@ -5897,10 +7785,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5897
7785
  '"]',
5898
7786
  );
5899
7787
  if (!nodeTarget || nodeTarget.nodeType !== 1) return;
5900
- // Resolve the actual editable text leaf (same logic as findTextEditTarget).
5901
- var textTarget: HTMLElement | null =
5902
- (findTextEditTarget(nodeTarget) as HTMLElement | null) ||
5903
- (nodeTarget as HTMLElement);
7788
+ // Edit the EXACT node identified by nodeId. Do NOT run it through
7789
+ // findTextEditTarget here that helper climbs UP to the highest
7790
+ // inline-editable ancestor, which for a text node inside a text-heavy
7791
+ // screen resolves all the way to <main>, putting the ENTIRE screen into
7792
+ // edit mode instead of this node (keystrokes land in the wrong element).
7793
+ var textTarget: HTMLElement | null = nodeTarget as HTMLElement;
5904
7794
  if (!textTarget || textTarget.nodeType !== 1) return;
5905
7795
  // If we are already editing this element, do nothing.
5906
7796
  if (activeTextEditEl && activeTextEditEl === textTarget) return;
@@ -5941,6 +7831,76 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
5941
7831
  scaleToolEnabled = !!e.data.enabled;
5942
7832
  return;
5943
7833
  }
7834
+ // gradient-edit-target / gradient-edit-clear: see the gradientEditTarget
7835
+ // doc comment above (near parseLinearGradientCss) for the full parent
7836
+ // wiring contract. `nodeId` must be a `data-agent-native-node-id` value;
7837
+ // `cssValue` is the live gradient CSS (only linear-gradient(...) renders
7838
+ // handles — anything else is accepted but draws nothing).
7839
+ if (e.data.type === "gradient-edit-target") {
7840
+ var gradientTargetNodeId =
7841
+ typeof e.data.nodeId === "string" ? e.data.nodeId : "";
7842
+ var gradientTargetCssValue =
7843
+ typeof e.data.cssValue === "string" ? e.data.cssValue : "";
7844
+ if (!gradientTargetNodeId || !gradientTargetCssValue) {
7845
+ gradientEditTarget = null;
7846
+ hideGradientOverlay();
7847
+ return;
7848
+ }
7849
+ gradientEditTarget = {
7850
+ nodeId: gradientTargetNodeId,
7851
+ cssValue: gradientTargetCssValue,
7852
+ };
7853
+ positionGradientOverlay();
7854
+ return;
7855
+ }
7856
+ if (e.data.type === "gradient-edit-clear") {
7857
+ gradientEditTarget = null;
7858
+ hideGradientOverlay();
7859
+ return;
7860
+ }
7861
+ // state-preview: force-render one element's interaction-state styling by
7862
+ // setting/removing the `data-an-state-preview="<state>"` attribute — see
7863
+ // `shared/interaction-states.ts`'s "Forced-preview mechanism" doc comment
7864
+ // for the full contract this implements. This bridge does ZERO CSS work:
7865
+ // the twin `[data-agent-native-node-id="…"][data-an-state-preview="…"]`
7866
+ // rule already lives in the persisted `<style data-agent-native-states>`
7867
+ // block (written by `duplicateStatePreviewRules`), so setting the
7868
+ // attribute is the entire preview mechanism. `state: null` (or a missing/
7869
+ // empty `nodeId`) clears any currently-previewing element.
7870
+ if (e.data.type === "state-preview") {
7871
+ var statePreviewTargetNodeId =
7872
+ typeof e.data.nodeId === "string" ? e.data.nodeId : "";
7873
+ var statePreviewState =
7874
+ typeof e.data.state === "string" ? e.data.state : "";
7875
+ // Clear the PREVIOUS target first — only one element force-previews a
7876
+ // state at a time, and the new message may target a different node
7877
+ // (e.g. the selection changed) or clear entirely.
7878
+ if (statePreviewNodeId) {
7879
+ var previousStatePreviewEl = document.querySelector(
7880
+ '[data-agent-native-node-id="' +
7881
+ String(statePreviewNodeId)
7882
+ .replace(/\\/g, "\\\\")
7883
+ .replace(/"/g, '\\"') +
7884
+ '"]',
7885
+ ) as HTMLElement | null;
7886
+ if (previousStatePreviewEl) {
7887
+ previousStatePreviewEl.removeAttribute("data-an-state-preview");
7888
+ }
7889
+ statePreviewNodeId = null;
7890
+ }
7891
+ if (!statePreviewTargetNodeId || !statePreviewState) return;
7892
+ var statePreviewEl = document.querySelector(
7893
+ '[data-agent-native-node-id="' +
7894
+ String(statePreviewTargetNodeId)
7895
+ .replace(/\\/g, "\\\\")
7896
+ .replace(/"/g, '\\"') +
7897
+ '"]',
7898
+ ) as HTMLElement | null;
7899
+ if (!statePreviewEl) return;
7900
+ statePreviewEl.setAttribute("data-an-state-preview", statePreviewState);
7901
+ statePreviewNodeId = statePreviewTargetNodeId;
7902
+ return;
7903
+ }
5944
7904
  if (e.data.type === "agent-native:cancel-active-drag") {
5945
7905
  cancelActiveBridgeDrag();
5946
7906
  return;
@@ -6083,8 +8043,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
6083
8043
  } catch (_err) {}
6084
8044
  }
6085
8045
  if (!target) return;
6086
- selectedSpacingHovered = false;
6087
- hoveredSpacingHandleKey = "";
8046
+ // Only reset the spacing hover state when the replay actually CHANGES
8047
+ // the selection. The host re-sends select-element on every
8048
+ // application-state poll tick (~1-2s) even when nothing changed; the
8049
+ // old unconditional reset silently killed the padding/gap hover state
8050
+ // — the "Npx" value box and the hatch band vanished within a poll tick
8051
+ // whenever the cursor RESTED on a handle (the user only ever saw the
8052
+ // badge flash, i.e. "the value box never shows"). A same-element
8053
+ // replay must be a no-op for hover state.
8054
+ if (target !== selectedEl) {
8055
+ selectedSpacingHovered = false;
8056
+ hoveredSpacingHandleKey = "";
8057
+ }
6088
8058
  selectedEl = target;
6089
8059
  positionOverlay(selectionOverlay, target);
6090
8060
  if (hoveredEl === selectedEl) highlightOverlay.style.display = "none";
@@ -6308,8 +8278,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
6308
8278
  }
6309
8279
  });
6310
8280
 
6311
- window.addEventListener("scroll", refreshOverlays, true);
6312
- window.addEventListener("resize", refreshOverlays);
8281
+ // rAF-coalesced on purpose: scroll events can fire several times per frame
8282
+ // (nested scrollers, high-rate trackpads), and running the full overlay
8283
+ // pipeline synchronously per event — with its interleaved rect reads and
8284
+ // overlay style writes — forces repeated synchronous reflows while a
8285
+ // selection is active. Coalescing to one refreshOverlays() per frame keeps
8286
+ // overlays visually locked to the content (rAF runs before paint) while
8287
+ // bounding the per-frame cost regardless of the incoming event rate.
8288
+ window.addEventListener("scroll", scheduleRefreshOverlays, true);
8289
+ window.addEventListener("resize", scheduleRefreshOverlays);
6313
8290
  applyEditorChromeScale();
6314
8291
 
6315
8292
  // One-time ready signal: tells the host that every message listener above is