@agent-native/core 0.85.7 → 0.86.1

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 (1132) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +133 -1
  3. package/corpus/core/README.md +0 -11
  4. package/corpus/core/docs/content/cloneable-saas.mdx +0 -1
  5. package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
  6. package/corpus/core/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
  7. package/corpus/core/docs/content/locales/ar-SA/sharing.mdx +1 -2
  8. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
  9. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +23 -7
  10. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +1 -1
  11. package/corpus/core/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
  12. package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
  13. package/corpus/core/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
  14. package/corpus/core/docs/content/locales/de-DE/sharing.mdx +1 -2
  15. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +2 -3
  16. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +23 -7
  17. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +1 -1
  18. package/corpus/core/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
  19. package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
  20. package/corpus/core/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
  21. package/corpus/core/docs/content/locales/es-ES/sharing.mdx +1 -2
  22. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +2 -3
  23. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +23 -7
  24. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +1 -1
  25. package/corpus/core/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
  26. package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
  27. package/corpus/core/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
  28. package/corpus/core/docs/content/locales/fr-FR/sharing.mdx +1 -2
  29. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
  30. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +23 -7
  31. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +1 -1
  32. package/corpus/core/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
  33. package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
  34. package/corpus/core/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
  35. package/corpus/core/docs/content/locales/hi-IN/sharing.mdx +1 -2
  36. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
  37. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +23 -7
  38. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +1 -1
  39. package/corpus/core/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
  40. package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
  41. package/corpus/core/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
  42. package/corpus/core/docs/content/locales/ja-JP/sharing.mdx +1 -2
  43. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
  44. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +23 -7
  45. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +1 -1
  46. package/corpus/core/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
  47. package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
  48. package/corpus/core/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
  49. package/corpus/core/docs/content/locales/ko-KR/sharing.mdx +1 -2
  50. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
  51. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +23 -7
  52. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +1 -1
  53. package/corpus/core/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
  54. package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
  55. package/corpus/core/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
  56. package/corpus/core/docs/content/locales/pt-BR/sharing.mdx +1 -2
  57. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
  58. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +23 -7
  59. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +1 -1
  60. package/corpus/core/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
  61. package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
  62. package/corpus/core/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
  63. package/corpus/core/docs/content/locales/zh-CN/sharing.mdx +1 -2
  64. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
  65. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +23 -7
  66. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +1 -1
  67. package/corpus/core/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
  68. package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
  69. package/corpus/core/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
  70. package/corpus/core/docs/content/locales/zh-TW/sharing.mdx +1 -2
  71. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
  72. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +23 -7
  73. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +1 -1
  74. package/corpus/core/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
  75. package/corpus/core/docs/content/plan-plugin.mdx +12 -11
  76. package/corpus/core/docs/content/sharing.mdx +1 -2
  77. package/corpus/core/docs/content/skills-guide.mdx +4 -3
  78. package/corpus/core/docs/content/template-plan.mdx +37 -6
  79. package/corpus/core/docs/content/tracking.mdx +1 -1
  80. package/corpus/core/docs/content/workspace-connections.mdx +1 -1
  81. package/corpus/core/package.json +7 -6
  82. package/corpus/core/src/agent/run-store.ts +1 -1
  83. package/corpus/core/src/chat-threads/store.ts +73 -11
  84. package/corpus/core/src/checkpoints/store.ts +13 -2
  85. package/corpus/core/src/cli/create.ts +3 -4
  86. package/corpus/core/src/cli/design-connect.ts +469 -9
  87. package/corpus/core/src/cli/index.ts +13 -0
  88. package/corpus/core/src/cli/plan-local.ts +281 -24
  89. package/corpus/core/src/cli/skills.ts +177 -21
  90. package/corpus/core/src/cli/templates-meta.ts +0 -13
  91. package/corpus/core/src/cli/visualize-repo.ts +715 -0
  92. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  93. package/corpus/core/src/client/AssistantChat.tsx +194 -21
  94. package/corpus/core/src/client/ErrorBoundary.tsx +67 -12
  95. package/corpus/core/src/client/ErrorReportActions.tsx +69 -0
  96. package/corpus/core/src/client/FeedbackButton.tsx +8 -4
  97. package/corpus/core/src/client/agent-chat-adapter.ts +6 -5
  98. package/corpus/core/src/client/app-providers.tsx +2 -2
  99. package/corpus/core/src/client/collab/index.ts +31 -0
  100. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +16 -1
  101. package/corpus/core/src/client/components/PresenceBar.tsx +14 -1
  102. package/corpus/core/src/client/components/RecentEditHighlights.tsx +219 -0
  103. package/corpus/core/src/client/components/RemoteSelectionRings.tsx +40 -6
  104. package/corpus/core/src/client/db-admin/DbAdminPage.tsx +41 -13
  105. package/corpus/core/src/client/db-admin/SqlEditor.tsx +10 -3
  106. package/corpus/core/src/client/db-admin/TableEditor.tsx +12 -5
  107. package/corpus/core/src/client/db-admin/useAgentSync.ts +10 -3
  108. package/corpus/core/src/client/db-admin/useDbAdmin.ts +65 -16
  109. package/corpus/core/src/client/error-reporting.ts +100 -0
  110. package/corpus/core/src/client/index.ts +59 -0
  111. package/corpus/core/src/client/org/OrgSwitcher.tsx +0 -4
  112. package/corpus/core/src/client/org/RequireActiveOrg.tsx +12 -0
  113. package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +49 -32
  114. package/corpus/core/src/client/rich-markdown-editor/index.ts +6 -0
  115. package/corpus/core/src/client/rich-markdown-editor/surgical-apply.ts +192 -0
  116. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +59 -8
  117. package/corpus/core/src/client/session-replay.ts +181 -15
  118. package/corpus/core/src/client/sse-event-processor.ts +90 -1
  119. package/corpus/core/src/client/use-action.ts +123 -25
  120. package/corpus/core/src/client/use-agent-engine-configured.ts +7 -1
  121. package/corpus/core/src/client/use-chat-threads.ts +18 -7
  122. package/corpus/core/src/client/use-db-sync.ts +104 -19
  123. package/corpus/core/src/client/visual-style-controls.tsx +1088 -0
  124. package/corpus/core/src/collab/agent-presence.ts +191 -62
  125. package/corpus/core/src/collab/awareness.ts +83 -6
  126. package/corpus/core/src/collab/client.ts +89 -146
  127. package/corpus/core/src/collab/index.ts +34 -0
  128. package/corpus/core/src/collab/recent-edits.ts +171 -0
  129. package/corpus/core/src/collab/undo.ts +432 -0
  130. package/corpus/core/src/collab/ydoc-manager.ts +68 -0
  131. package/corpus/core/src/connections/catalog.ts +2 -3
  132. package/corpus/core/src/db/create-get-db.ts +86 -1
  133. package/corpus/core/src/db/ensure-additive-columns.ts +430 -0
  134. package/corpus/core/src/db/index.ts +6 -0
  135. package/corpus/core/src/db/migrations.ts +236 -35
  136. package/corpus/core/src/db-admin/index.ts +26 -0
  137. package/corpus/core/src/db-admin/operations.ts +87 -49
  138. package/corpus/core/src/jobs/scheduler.ts +17 -14
  139. package/corpus/core/src/localization/default-messages.ts +4 -0
  140. package/corpus/core/src/observability/traces.ts +182 -1
  141. package/corpus/core/src/org/context.ts +79 -2
  142. package/corpus/core/src/server/agent-chat-plugin.ts +10 -6
  143. package/corpus/core/src/server/auth-marketing.ts +0 -11
  144. package/corpus/core/src/server/auth.ts +11 -4
  145. package/corpus/core/src/server/builder-design-systems.ts +328 -9
  146. package/corpus/core/src/server/collab-plugin.ts +56 -13
  147. package/corpus/core/src/server/design-token-utils.ts +1 -1
  148. package/corpus/core/src/server/fusion-app.ts +447 -0
  149. package/corpus/core/src/server/index.ts +28 -0
  150. package/corpus/core/src/server/onboarding-html.ts +3 -4
  151. package/corpus/core/src/server/poll-events.ts +14 -2
  152. package/corpus/core/src/server/poll.ts +219 -12
  153. package/corpus/core/src/server/schema-prompt.ts +28 -9
  154. package/corpus/core/src/settings/store.ts +33 -2
  155. package/corpus/core/src/sharing/actions/set-resource-visibility.ts +2 -0
  156. package/corpus/core/src/sharing/actions/share-resource.ts +3 -0
  157. package/corpus/core/src/sharing/actions/unshare-resource.ts +2 -0
  158. package/corpus/core/src/styles/agent-native.css +0 -5
  159. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  160. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  161. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  162. package/corpus/core/src/templates/default/app/root.tsx +47 -0
  163. package/corpus/core/src/templates/default/package.json +3 -3
  164. package/corpus/core/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  165. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  166. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  167. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  168. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  169. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  170. package/corpus/core/src/tracking/providers.ts +22 -1
  171. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +15 -0
  172. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +20 -5
  173. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +8 -0
  174. package/corpus/templates/analytics/AGENTS.md +35 -6
  175. package/corpus/templates/analytics/actions/delete-db-admin-connection.ts +19 -0
  176. package/corpus/templates/analytics/actions/list-db-admin-connections.ts +18 -0
  177. package/corpus/templates/analytics/actions/navigate.ts +28 -4
  178. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  179. package/corpus/templates/analytics/actions/save-db-admin-connection.ts +33 -0
  180. package/corpus/templates/analytics/actions/view-screen.ts +65 -0
  181. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +23 -1
  182. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  183. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -0
  184. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +16 -0
  185. package/corpus/templates/analytics/app/i18n/zh-TW.ts +36 -1
  186. package/corpus/templates/analytics/app/i18n-data.ts +361 -10
  187. package/corpus/templates/analytics/app/pages/Agents.tsx +512 -0
  188. package/corpus/templates/analytics/app/pages/DataSources.tsx +11 -1
  189. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +76 -40
  190. package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +5 -0
  191. package/corpus/templates/analytics/app/routes/agents.tsx +10 -0
  192. package/corpus/templates/analytics/changelog/2026-07-03-agent-admin-tools-page.md +6 -0
  193. package/corpus/templates/analytics/changelog/2026-07-03-agent-llm-observability-dashboard.md +6 -0
  194. package/corpus/templates/analytics/changelog/2026-07-03-connect-other-app-databases-from-analytics.md +6 -0
  195. package/corpus/templates/analytics/changelog/2026-07-03-session-recordings-now-include-response-bodies-for-5xx-error.md +6 -0
  196. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +34 -0
  197. package/corpus/templates/analytics/docs/schemas/tracked-events.md +6 -0
  198. package/corpus/templates/analytics/package.json +3 -3
  199. package/corpus/templates/analytics/seeds/dashboards/agent-observability-llm.json +173 -0
  200. package/corpus/templates/analytics/server/db/schema.ts +27 -0
  201. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +23 -5
  202. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +58 -13
  203. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +23 -0
  204. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +5 -0
  205. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +458 -0
  206. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +120 -8
  207. package/corpus/templates/analytics/server/lib/session-replay.ts +3 -2
  208. package/corpus/templates/analytics/server/plugins/db.ts +128 -2
  209. package/corpus/templates/analytics/server/routes/[base]/_agent-native/analytics-db-admin/[...path].ts +1 -0
  210. package/corpus/templates/analytics/server/routes/_agent-native/analytics-db-admin/[...path].ts +160 -0
  211. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +45 -1
  212. package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +10 -0
  213. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +82 -19
  214. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +15 -0
  215. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +8 -0
  216. package/corpus/templates/assets/package.json +3 -3
  217. package/corpus/templates/assets/server/plugins/db.ts +63 -2
  218. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +15 -0
  219. package/corpus/templates/brain/package.json +3 -3
  220. package/corpus/templates/brain/server/plugins/db.ts +75 -2
  221. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +15 -0
  222. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +8 -0
  223. package/corpus/templates/calendar/package.json +3 -3
  224. package/corpus/templates/calendar/server/plugins/db.ts +64 -2
  225. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +15 -0
  226. package/corpus/templates/chat/package.json +3 -3
  227. package/corpus/templates/clips/actions/finalize-recording.ts +50 -40
  228. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
  229. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -3
  230. package/corpus/templates/clips/changelog/2026-07-03-mobile-recording-pages-keep-videos-in-a-16-9-player-with-act.md +6 -0
  231. package/corpus/templates/clips/package.json +3 -3
  232. package/corpus/templates/clips/server/lib/recording-upload-state.ts +56 -0
  233. package/corpus/templates/clips/server/plugins/db.ts +68 -1
  234. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +27 -3
  235. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +15 -0
  236. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +8 -0
  237. package/corpus/templates/content/actions/edit-document.ts +48 -1
  238. package/corpus/templates/content/actions/list-documents.ts +101 -94
  239. package/corpus/templates/content/actions/search-documents.ts +19 -3
  240. package/corpus/templates/content/actions/update-document.ts +54 -1
  241. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -7
  242. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +97 -2
  243. package/corpus/templates/content/app/root.tsx +9 -0
  244. package/corpus/templates/content/changelog/2026-07-03-agent-document-edits-now-appear-as-a-live-collaborator-prese.md +6 -0
  245. package/corpus/templates/content/changelog/2026-07-03-document-search-and-list-views-load-faster-on-large-workspac.md +6 -0
  246. package/corpus/templates/content/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  247. package/corpus/templates/content/package.json +3 -3
  248. package/corpus/templates/content/server/plugins/db.ts +57 -1
  249. package/corpus/templates/design/.agents/skills/full-app-build/SKILL.md +140 -0
  250. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +20 -0
  251. package/corpus/templates/design/AGENTS.md +63 -19
  252. package/corpus/templates/design/actions/add-fusion-screens.ts +91 -0
  253. package/corpus/templates/design/actions/apply-a11y-fix.ts +5 -1
  254. package/corpus/templates/design/actions/apply-component-prop-edit.ts +10 -2
  255. package/corpus/templates/design/actions/apply-fusion-edits.ts +161 -0
  256. package/corpus/templates/design/actions/apply-visual-edit.ts +34 -1
  257. package/corpus/templates/design/actions/create-component.ts +5 -1
  258. package/corpus/templates/design/actions/create-fusion-app.ts +202 -0
  259. package/corpus/templates/design/actions/deploy-fusion-app.ts +123 -0
  260. package/corpus/templates/design/actions/edit-design.ts +13 -16
  261. package/corpus/templates/design/actions/get-design-system.ts +21 -0
  262. package/corpus/templates/design/actions/get-fusion-deploy-status.ts +86 -0
  263. package/corpus/templates/design/actions/index-design-system-with-builder.ts +14 -37
  264. package/corpus/templates/design/actions/list-fusion-edits.ts +96 -0
  265. package/corpus/templates/design/actions/list-local-files.ts +133 -0
  266. package/corpus/templates/design/actions/push-fusion-app.ts +62 -0
  267. package/corpus/templates/design/actions/queue-fusion-edit.ts +112 -0
  268. package/corpus/templates/design/actions/read-local-file.ts +139 -0
  269. package/corpus/templates/design/actions/send-fusion-message.ts +69 -0
  270. package/corpus/templates/design/actions/sync-fusion-app.ts +196 -0
  271. package/corpus/templates/design/actions/write-local-file.ts +115 -14
  272. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +12 -0
  273. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +379 -0
  274. package/corpus/templates/design/app/components/design/EditPanel.tsx +149 -73
  275. package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +328 -0
  276. package/corpus/templates/design/app/components/design/LayersPanel.tsx +148 -30
  277. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +1 -1
  278. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +567 -6
  279. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -98
  280. package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +85 -0
  281. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +466 -0
  282. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +114 -0
  283. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +339 -0
  284. package/corpus/templates/design/app/components/design/code-workbench/editor/Breadcrumbs.tsx +42 -0
  285. package/corpus/templates/design/app/components/design/code-workbench/editor/BufferSync.tsx +128 -0
  286. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +372 -0
  287. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +268 -0
  288. package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +208 -0
  289. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +61 -0
  290. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme-palette.ts +191 -0
  291. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme.ts +29 -0
  292. package/corpus/templates/design/app/components/design/code-workbench/editor/status-bar-lang.ts +21 -0
  293. package/corpus/templates/design/app/components/design/code-workbench/editor/tab-drag.ts +27 -0
  294. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +161 -0
  295. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +555 -0
  296. package/corpus/templates/design/app/components/design/code-workbench/explorer/file-icons.tsx +115 -0
  297. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +65 -0
  298. package/corpus/templates/design/app/components/design/code-workbench/format/prettier-format.ts +182 -0
  299. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +133 -0
  300. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +563 -0
  301. package/corpus/templates/design/app/components/design/code-workbench/quickinput/fuzzy.ts +170 -0
  302. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +508 -0
  303. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +280 -0
  304. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +782 -0
  305. package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +39 -0
  306. package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +196 -0
  307. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +177 -0
  308. package/corpus/templates/design/app/components/design/code-workbench/workspace/tree.ts +110 -0
  309. package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +103 -0
  310. package/corpus/templates/design/app/components/design/code-workbench-theme.ts +68 -0
  311. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +127 -10
  312. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +52 -29
  313. package/corpus/templates/design/app/components/design/inspector/index.ts +9 -0
  314. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +56 -0
  315. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +80 -1
  316. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +6 -16
  317. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +6 -0
  318. package/corpus/templates/design/app/i18n/zh-TW.ts +17 -15
  319. package/corpus/templates/design/app/i18n-data.ts +185 -160
  320. package/corpus/templates/design/app/pages/DesignEditor.tsx +1474 -175
  321. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +32 -14
  322. package/corpus/templates/design/app/pages/Index.tsx +90 -17
  323. package/corpus/templates/design/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  324. package/corpus/templates/design/changelog/2026-07-03-the-ai-now-edits-like-a-collaborator-on-canvas-its-cursor-gl.md +6 -0
  325. package/corpus/templates/design/changelog/2026-07-03-the-code-editor-is-easier-to-use-with-top-sidebar-tabs-clear.md +6 -0
  326. package/corpus/templates/design/changelog/2026-07-03-the-code-panel-is-now-a-full-vs-code-style-editor-with-a-fil.md +6 -0
  327. package/corpus/templates/design/package.json +4 -3
  328. package/corpus/templates/design/server/db/schema.ts +31 -0
  329. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +16 -106
  330. package/corpus/templates/design/server/lib/fusion-screens.ts +246 -0
  331. package/corpus/templates/design/server/plugins/db.ts +89 -2
  332. package/corpus/templates/design/shared/collab-selection.ts +52 -0
  333. package/corpus/templates/design/shared/full-app.ts +126 -0
  334. package/corpus/templates/design/shared/pen-path.ts +296 -0
  335. package/corpus/templates/dispatch/package.json +3 -3
  336. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +15 -0
  337. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +8 -0
  338. package/corpus/templates/forms/package.json +3 -3
  339. package/corpus/templates/forms/server/plugins/db.ts +62 -2
  340. package/corpus/templates/macros/package.json +3 -3
  341. package/corpus/templates/macros/server/plugins/db.ts +63 -2
  342. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +15 -0
  343. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +8 -0
  344. package/corpus/templates/mail/app/root.tsx +60 -1
  345. package/corpus/templates/mail/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  346. package/corpus/templates/mail/package.json +3 -3
  347. package/corpus/templates/mail/server/lib/contact-frequency.ts +42 -15
  348. package/corpus/templates/mail/server/plugins/db.ts +63 -2
  349. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +15 -0
  350. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +9 -3
  351. package/corpus/templates/plan/.agents/skills/visualize-repo/SKILL.md +92 -0
  352. package/corpus/templates/plan/actions/export-visual-plan.ts +5 -0
  353. package/corpus/templates/plan/actions/get-local-plan-folder.ts +4 -22
  354. package/corpus/templates/plan/actions/get-plan-version.ts +6 -16
  355. package/corpus/templates/plan/actions/list-plan-versions.ts +48 -20
  356. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +41 -1
  357. package/corpus/templates/plan/actions/promote-local-plan-folder.ts +4 -22
  358. package/corpus/templates/plan/actions/update-local-plan-comments.ts +4 -2
  359. package/corpus/templates/plan/actions/update-local-plan-folder.ts +4 -22
  360. package/corpus/templates/plan/actions/update-visual-plan.ts +60 -1
  361. package/corpus/templates/plan/agent-native.app-skill.json +10 -0
  362. package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +138 -39
  363. package/corpus/templates/plan/app/components/editor/usePlanUndoStack.ts +165 -23
  364. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +80 -4
  365. package/corpus/templates/plan/app/global.css +11 -9
  366. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +10 -1
  367. package/corpus/templates/plan/app/hooks/use-plan-presence.ts +66 -0
  368. package/corpus/templates/plan/app/i18n/ar-SA.ts +3 -0
  369. package/corpus/templates/plan/app/i18n/de-DE.ts +4 -0
  370. package/corpus/templates/plan/app/i18n/en-US.ts +4 -0
  371. package/corpus/templates/plan/app/i18n/es-ES.ts +4 -0
  372. package/corpus/templates/plan/app/i18n/fr-FR.ts +4 -0
  373. package/corpus/templates/plan/app/i18n/hi-IN.ts +3 -0
  374. package/corpus/templates/plan/app/i18n/ja-JP.ts +4 -0
  375. package/corpus/templates/plan/app/i18n/ko-KR.ts +4 -0
  376. package/corpus/templates/plan/app/i18n/pt-BR.ts +4 -0
  377. package/corpus/templates/plan/app/i18n/zh-CN.ts +3 -0
  378. package/corpus/templates/plan/app/i18n/zh-TW.ts +3 -0
  379. package/corpus/templates/plan/app/pages/PlansPage.tsx +289 -66
  380. package/corpus/templates/plan/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  381. package/corpus/templates/plan/changelog/2026-07-03-local-visual-plans-and-recaps-can-save-comments-without-sign.md +6 -0
  382. package/corpus/templates/plan/changelog/2026-07-03-plan-tabs-render-nested-text-instead-of-raw-json.md +6 -0
  383. package/corpus/templates/plan/changelog/2026-07-03-plans-are-now-fully-real-time-collaborative-multiple-people-.md +6 -0
  384. package/corpus/templates/plan/changelog/2026-07-03-plans-open-and-refresh-faster-especially-long-lived-plans-wi.md +6 -0
  385. package/corpus/templates/plan/changelog/2026-07-03-undo-now-survives-agent-edits-cmd-z-keeps-working-on-your-ow.md +6 -0
  386. package/corpus/templates/plan/package.json +5 -3
  387. package/corpus/templates/plan/server/db/schema.ts +12 -0
  388. package/corpus/templates/plan/server/lib/local-plan-kind.ts +25 -0
  389. package/corpus/templates/plan/server/lib/plan-versions.ts +83 -1
  390. package/corpus/templates/plan/server/lib/public-action-paths.ts +0 -2
  391. package/corpus/templates/plan/server/plans.ts +26 -3
  392. package/corpus/templates/plan/server/plugins/db.ts +105 -2
  393. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +8 -4
  394. package/corpus/templates/slides/AGENTS.md +11 -3
  395. package/corpus/templates/slides/actions/_agent-presence.ts +48 -0
  396. package/corpus/templates/slides/actions/add-slide.ts +16 -18
  397. package/corpus/templates/slides/actions/get-design-system.ts +21 -0
  398. package/corpus/templates/slides/actions/import-file.ts +15 -7
  399. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +14 -37
  400. package/corpus/templates/slides/actions/patch-deck.ts +4 -2
  401. package/corpus/templates/slides/actions/update-slide.ts +114 -77
  402. package/corpus/templates/slides/actions/view-screen.ts +39 -0
  403. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +32 -8
  404. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +47 -3
  405. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -39
  406. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +507 -72
  407. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +346 -0
  408. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +56 -6
  409. package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +46 -96
  410. package/corpus/templates/slides/app/components/layout/Layout.tsx +6 -1
  411. package/corpus/templates/slides/app/components/visual-editor/SaveStatusIndicator.tsx +8 -1
  412. package/corpus/templates/slides/app/context/DeckContext.tsx +589 -176
  413. package/corpus/templates/slides/app/hooks/use-deck-presence.ts +84 -42
  414. package/corpus/templates/slides/app/i18n/ar-SA.ts +54 -7
  415. package/corpus/templates/slides/app/i18n/de-DE.ts +53 -8
  416. package/corpus/templates/slides/app/i18n/en-US.ts +54 -7
  417. package/corpus/templates/slides/app/i18n/es-ES.ts +53 -8
  418. package/corpus/templates/slides/app/i18n/fr-FR.ts +53 -8
  419. package/corpus/templates/slides/app/i18n/hi-IN.ts +54 -7
  420. package/corpus/templates/slides/app/i18n/ja-JP.ts +53 -8
  421. package/corpus/templates/slides/app/i18n/ko-KR.ts +53 -7
  422. package/corpus/templates/slides/app/i18n/pt-BR.ts +53 -8
  423. package/corpus/templates/slides/app/i18n/zh-CN.ts +54 -7
  424. package/corpus/templates/slides/app/i18n/zh-TW.ts +54 -7
  425. package/corpus/templates/slides/app/lib/canvas-zoom.ts +16 -1
  426. package/corpus/templates/slides/app/pages/DeckEditor.tsx +28 -16
  427. package/corpus/templates/slides/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  428. package/corpus/templates/slides/changelog/2026-07-03-figma-style-slide-editing.md +6 -0
  429. package/corpus/templates/slides/changelog/2026-07-03-see-the-ai-working-with-you-when-the-agent-edits-a-slide-it-.md +6 -0
  430. package/corpus/templates/slides/changelog/2026-07-03-slide-editing-now-keeps-thumbnails-speaker-notes-styling-and.md +6 -0
  431. package/corpus/templates/slides/changelog/2026-07-03-undo-redo-is-now-precise-and-safe-with-collaborators-it-only.md +6 -0
  432. package/corpus/templates/slides/package.json +3 -3
  433. package/corpus/templates/slides/server/handlers/decks.ts +31 -3
  434. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +16 -104
  435. package/corpus/templates/slides/server/plugins/db.ts +65 -2
  436. package/corpus/templates/videos/netlify.toml +1 -12
  437. package/dist/agent/run-store.js +1 -1
  438. package/dist/agent/run-store.js.map +1 -1
  439. package/dist/chat-threads/store.d.ts +9 -1
  440. package/dist/chat-threads/store.d.ts.map +1 -1
  441. package/dist/chat-threads/store.js +63 -11
  442. package/dist/chat-threads/store.js.map +1 -1
  443. package/dist/checkpoints/store.d.ts.map +1 -1
  444. package/dist/checkpoints/store.js +10 -2
  445. package/dist/checkpoints/store.js.map +1 -1
  446. package/dist/cli/create.d.ts.map +1 -1
  447. package/dist/cli/create.js +3 -5
  448. package/dist/cli/create.js.map +1 -1
  449. package/dist/cli/design-connect.d.ts +49 -1
  450. package/dist/cli/design-connect.d.ts.map +1 -1
  451. package/dist/cli/design-connect.js +414 -9
  452. package/dist/cli/design-connect.js.map +1 -1
  453. package/dist/cli/index.js +12 -0
  454. package/dist/cli/index.js.map +1 -1
  455. package/dist/cli/plan-local.d.ts +24 -0
  456. package/dist/cli/plan-local.d.ts.map +1 -1
  457. package/dist/cli/plan-local.js +188 -20
  458. package/dist/cli/plan-local.js.map +1 -1
  459. package/dist/cli/skills.d.ts +3 -1
  460. package/dist/cli/skills.d.ts.map +1 -1
  461. package/dist/cli/skills.js +173 -21
  462. package/dist/cli/skills.js.map +1 -1
  463. package/dist/cli/templates-meta.d.ts.map +1 -1
  464. package/dist/cli/templates-meta.js +0 -14
  465. package/dist/cli/templates-meta.js.map +1 -1
  466. package/dist/cli/visualize-repo.d.ts +49 -0
  467. package/dist/cli/visualize-repo.d.ts.map +1 -0
  468. package/dist/cli/visualize-repo.js +561 -0
  469. package/dist/cli/visualize-repo.js.map +1 -0
  470. package/dist/client/AgentPanel.d.ts.map +1 -1
  471. package/dist/client/AgentPanel.js +2 -1
  472. package/dist/client/AgentPanel.js.map +1 -1
  473. package/dist/client/AssistantChat.d.ts.map +1 -1
  474. package/dist/client/AssistantChat.js +175 -18
  475. package/dist/client/AssistantChat.js.map +1 -1
  476. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  477. package/dist/client/ErrorBoundary.js +37 -2
  478. package/dist/client/ErrorBoundary.js.map +1 -1
  479. package/dist/client/ErrorReportActions.d.ts +13 -0
  480. package/dist/client/ErrorReportActions.d.ts.map +1 -0
  481. package/dist/client/ErrorReportActions.js +14 -0
  482. package/dist/client/ErrorReportActions.js.map +1 -0
  483. package/dist/client/FeedbackButton.d.ts +3 -1
  484. package/dist/client/FeedbackButton.d.ts.map +1 -1
  485. package/dist/client/FeedbackButton.js +6 -5
  486. package/dist/client/FeedbackButton.js.map +1 -1
  487. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  488. package/dist/client/agent-chat-adapter.js +6 -5
  489. package/dist/client/agent-chat-adapter.js.map +1 -1
  490. package/dist/client/app-providers.d.ts +2 -2
  491. package/dist/client/app-providers.js.map +1 -1
  492. package/dist/client/collab/index.d.ts +4 -1
  493. package/dist/client/collab/index.d.ts.map +1 -1
  494. package/dist/client/collab/index.js +3 -0
  495. package/dist/client/collab/index.js.map +1 -1
  496. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  497. package/dist/client/components/LiveCursorOverlay.js +11 -3
  498. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  499. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  500. package/dist/client/components/PresenceBar.js +6 -1
  501. package/dist/client/components/PresenceBar.js.map +1 -1
  502. package/dist/client/components/RecentEditHighlights.d.ts +43 -0
  503. package/dist/client/components/RecentEditHighlights.d.ts.map +1 -0
  504. package/dist/client/components/RecentEditHighlights.js +146 -0
  505. package/dist/client/components/RecentEditHighlights.js.map +1 -0
  506. package/dist/client/components/RemoteSelectionRings.d.ts +9 -0
  507. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
  508. package/dist/client/components/RemoteSelectionRings.js +22 -8
  509. package/dist/client/components/RemoteSelectionRings.js.map +1 -1
  510. package/dist/client/db-admin/DbAdminPage.d.ts +9 -1
  511. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -1
  512. package/dist/client/db-admin/DbAdminPage.js +17 -13
  513. package/dist/client/db-admin/DbAdminPage.js.map +1 -1
  514. package/dist/client/db-admin/SqlEditor.d.ts +3 -1
  515. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
  516. package/dist/client/db-admin/SqlEditor.js +3 -3
  517. package/dist/client/db-admin/SqlEditor.js.map +1 -1
  518. package/dist/client/db-admin/TableEditor.d.ts +3 -1
  519. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  520. package/dist/client/db-admin/TableEditor.js +6 -6
  521. package/dist/client/db-admin/TableEditor.js.map +1 -1
  522. package/dist/client/db-admin/useAgentSync.d.ts +3 -2
  523. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  524. package/dist/client/db-admin/useAgentSync.js +8 -4
  525. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  526. package/dist/client/db-admin/useDbAdmin.d.ts +11 -7
  527. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -1
  528. package/dist/client/db-admin/useDbAdmin.js +45 -20
  529. package/dist/client/db-admin/useDbAdmin.js.map +1 -1
  530. package/dist/client/error-reporting.d.ts +19 -0
  531. package/dist/client/error-reporting.d.ts.map +1 -0
  532. package/dist/client/error-reporting.js +71 -0
  533. package/dist/client/error-reporting.js.map +1 -0
  534. package/dist/client/index.d.ts +8 -1
  535. package/dist/client/index.d.ts.map +1 -1
  536. package/dist/client/index.js +9 -0
  537. package/dist/client/index.js.map +1 -1
  538. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  539. package/dist/client/org/OrgSwitcher.js +1 -3
  540. package/dist/client/org/OrgSwitcher.js.map +1 -1
  541. package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
  542. package/dist/client/org/RequireActiveOrg.js +2 -1
  543. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  544. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
  545. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  546. package/dist/client/rich-markdown-editor/SharedRichEditor.js +8 -2
  547. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  548. package/dist/client/rich-markdown-editor/index.d.ts +1 -0
  549. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  550. package/dist/client/rich-markdown-editor/index.js +1 -0
  551. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  552. package/dist/client/rich-markdown-editor/surgical-apply.d.ts +66 -0
  553. package/dist/client/rich-markdown-editor/surgical-apply.d.ts.map +1 -0
  554. package/dist/client/rich-markdown-editor/surgical-apply.js +147 -0
  555. package/dist/client/rich-markdown-editor/surgical-apply.js.map +1 -0
  556. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +18 -2
  557. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  558. package/dist/client/rich-markdown-editor/useCollabReconcile.js +39 -8
  559. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  560. package/dist/client/session-replay.d.ts +10 -1
  561. package/dist/client/session-replay.d.ts.map +1 -1
  562. package/dist/client/session-replay.js +134 -7
  563. package/dist/client/session-replay.js.map +1 -1
  564. package/dist/client/sse-event-processor.d.ts.map +1 -1
  565. package/dist/client/sse-event-processor.js +81 -1
  566. package/dist/client/sse-event-processor.js.map +1 -1
  567. package/dist/client/use-action.d.ts +24 -0
  568. package/dist/client/use-action.d.ts.map +1 -1
  569. package/dist/client/use-action.js +109 -27
  570. package/dist/client/use-action.js.map +1 -1
  571. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  572. package/dist/client/use-agent-engine-configured.js +7 -1
  573. package/dist/client/use-agent-engine-configured.js.map +1 -1
  574. package/dist/client/use-chat-threads.d.ts.map +1 -1
  575. package/dist/client/use-chat-threads.js +11 -3
  576. package/dist/client/use-chat-threads.js.map +1 -1
  577. package/dist/client/use-db-sync.d.ts +36 -0
  578. package/dist/client/use-db-sync.d.ts.map +1 -1
  579. package/dist/client/use-db-sync.js +68 -12
  580. package/dist/client/use-db-sync.js.map +1 -1
  581. package/dist/client/visual-style-controls.d.ts +90 -0
  582. package/dist/client/visual-style-controls.d.ts.map +1 -0
  583. package/dist/client/visual-style-controls.js +499 -0
  584. package/dist/client/visual-style-controls.js.map +1 -0
  585. package/dist/collab/agent-presence.d.ts +51 -5
  586. package/dist/collab/agent-presence.d.ts.map +1 -1
  587. package/dist/collab/agent-presence.js +144 -58
  588. package/dist/collab/agent-presence.js.map +1 -1
  589. package/dist/collab/awareness-store.d.ts +32 -0
  590. package/dist/collab/awareness-store.d.ts.map +1 -0
  591. package/dist/collab/awareness-store.js +0 -0
  592. package/dist/collab/awareness-store.js.map +1 -0
  593. package/dist/collab/awareness.d.ts +12 -1
  594. package/dist/collab/awareness.d.ts.map +1 -1
  595. package/dist/collab/awareness.js +53 -5
  596. package/dist/collab/awareness.js.map +1 -1
  597. package/dist/collab/client.d.ts +2 -0
  598. package/dist/collab/client.d.ts.map +1 -1
  599. package/dist/collab/client.js +79 -119
  600. package/dist/collab/client.js.map +1 -1
  601. package/dist/collab/index.d.ts +3 -1
  602. package/dist/collab/index.d.ts.map +1 -1
  603. package/dist/collab/index.js +5 -1
  604. package/dist/collab/index.js.map +1 -1
  605. package/dist/collab/recent-edits.d.ts +87 -0
  606. package/dist/collab/recent-edits.d.ts.map +1 -0
  607. package/dist/collab/recent-edits.js +116 -0
  608. package/dist/collab/recent-edits.js.map +1 -0
  609. package/dist/collab/routes.d.ts +1 -1
  610. package/dist/collab/undo.d.ts +121 -0
  611. package/dist/collab/undo.d.ts.map +1 -0
  612. package/dist/collab/undo.js +244 -0
  613. package/dist/collab/undo.js.map +1 -0
  614. package/dist/collab/ydoc-manager.d.ts +10 -0
  615. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  616. package/dist/collab/ydoc-manager.js +57 -0
  617. package/dist/collab/ydoc-manager.js.map +1 -1
  618. package/dist/connections/catalog.d.ts +2 -2
  619. package/dist/connections/catalog.d.ts.map +1 -1
  620. package/dist/connections/catalog.js +1 -1
  621. package/dist/connections/catalog.js.map +1 -1
  622. package/dist/db/create-get-db.d.ts +22 -0
  623. package/dist/db/create-get-db.d.ts.map +1 -1
  624. package/dist/db/create-get-db.js +73 -1
  625. package/dist/db/create-get-db.js.map +1 -1
  626. package/dist/db/ensure-additive-columns.d.ts +93 -0
  627. package/dist/db/ensure-additive-columns.d.ts.map +1 -0
  628. package/dist/db/ensure-additive-columns.js +355 -0
  629. package/dist/db/ensure-additive-columns.js.map +1 -0
  630. package/dist/db/index.d.ts +1 -0
  631. package/dist/db/index.d.ts.map +1 -1
  632. package/dist/db/index.js +1 -0
  633. package/dist/db/index.js.map +1 -1
  634. package/dist/db/migrations.d.ts +70 -0
  635. package/dist/db/migrations.d.ts.map +1 -1
  636. package/dist/db/migrations.js +196 -33
  637. package/dist/db/migrations.js.map +1 -1
  638. package/dist/db-admin/index.d.ts +3 -0
  639. package/dist/db-admin/index.d.ts.map +1 -0
  640. package/dist/db-admin/index.js +2 -0
  641. package/dist/db-admin/index.js.map +1 -0
  642. package/dist/db-admin/operations.d.ts +26 -5
  643. package/dist/db-admin/operations.d.ts.map +1 -1
  644. package/dist/db-admin/operations.js +66 -55
  645. package/dist/db-admin/operations.js.map +1 -1
  646. package/dist/jobs/scheduler.js +17 -10
  647. package/dist/jobs/scheduler.js.map +1 -1
  648. package/dist/localization/default-messages.d.ts +3 -0
  649. package/dist/localization/default-messages.d.ts.map +1 -1
  650. package/dist/localization/default-messages.js +3 -0
  651. package/dist/localization/default-messages.js.map +1 -1
  652. package/dist/notifications/routes.d.ts +2 -2
  653. package/dist/observability/routes.d.ts +2 -2
  654. package/dist/observability/traces.d.ts.map +1 -1
  655. package/dist/observability/traces.js +155 -1
  656. package/dist/observability/traces.js.map +1 -1
  657. package/dist/org/context.d.ts +7 -0
  658. package/dist/org/context.d.ts.map +1 -1
  659. package/dist/org/context.js +54 -2
  660. package/dist/org/context.js.map +1 -1
  661. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  662. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  663. package/dist/server/agent-chat-plugin.js +10 -6
  664. package/dist/server/agent-chat-plugin.js.map +1 -1
  665. package/dist/server/auth-marketing.d.ts.map +1 -1
  666. package/dist/server/auth-marketing.js +0 -10
  667. package/dist/server/auth-marketing.js.map +1 -1
  668. package/dist/server/auth.d.ts.map +1 -1
  669. package/dist/server/auth.js +6 -4
  670. package/dist/server/auth.js.map +1 -1
  671. package/dist/server/builder-design-systems.d.ts +61 -0
  672. package/dist/server/builder-design-systems.d.ts.map +1 -1
  673. package/dist/server/builder-design-systems.js +219 -14
  674. package/dist/server/builder-design-systems.js.map +1 -1
  675. package/dist/server/collab-plugin.d.ts.map +1 -1
  676. package/dist/server/collab-plugin.js +45 -13
  677. package/dist/server/collab-plugin.js.map +1 -1
  678. package/dist/server/design-token-utils.d.ts +1 -1
  679. package/dist/server/design-token-utils.js +1 -1
  680. package/dist/server/design-token-utils.js.map +1 -1
  681. package/dist/server/fusion-app.d.ts +107 -0
  682. package/dist/server/fusion-app.d.ts.map +1 -0
  683. package/dist/server/fusion-app.js +371 -0
  684. package/dist/server/fusion-app.js.map +1 -0
  685. package/dist/server/index.d.ts +2 -1
  686. package/dist/server/index.d.ts.map +1 -1
  687. package/dist/server/index.js +2 -1
  688. package/dist/server/index.js.map +1 -1
  689. package/dist/server/onboarding-html.d.ts.map +1 -1
  690. package/dist/server/onboarding-html.js +3 -4
  691. package/dist/server/onboarding-html.js.map +1 -1
  692. package/dist/server/poll-events.d.ts +2 -0
  693. package/dist/server/poll-events.d.ts.map +1 -1
  694. package/dist/server/poll-events.js +7 -2
  695. package/dist/server/poll-events.js.map +1 -1
  696. package/dist/server/poll.d.ts +47 -3
  697. package/dist/server/poll.d.ts.map +1 -1
  698. package/dist/server/poll.js +176 -10
  699. package/dist/server/poll.js.map +1 -1
  700. package/dist/server/schema-prompt.d.ts.map +1 -1
  701. package/dist/server/schema-prompt.js +24 -7
  702. package/dist/server/schema-prompt.js.map +1 -1
  703. package/dist/settings/store.d.ts.map +1 -1
  704. package/dist/settings/store.js +32 -3
  705. package/dist/settings/store.js.map +1 -1
  706. package/dist/sharing/actions/set-resource-visibility.js +2 -0
  707. package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
  708. package/dist/sharing/actions/share-resource.d.ts.map +1 -1
  709. package/dist/sharing/actions/share-resource.js +3 -0
  710. package/dist/sharing/actions/share-resource.js.map +1 -1
  711. package/dist/sharing/actions/unshare-resource.js +2 -0
  712. package/dist/sharing/actions/unshare-resource.js.map +1 -1
  713. package/dist/styles/agent-native.css +0 -5
  714. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  715. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  716. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  717. package/dist/templates/default/app/root.tsx +47 -0
  718. package/dist/templates/default/package.json +3 -3
  719. package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  720. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  721. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  722. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  723. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  724. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  725. package/dist/tracking/providers.d.ts.map +1 -1
  726. package/dist/tracking/providers.js +17 -1
  727. package/dist/tracking/providers.js.map +1 -1
  728. package/docs/content/cloneable-saas.mdx +0 -1
  729. package/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
  730. package/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
  731. package/docs/content/locales/ar-SA/sharing.mdx +1 -2
  732. package/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
  733. package/docs/content/locales/ar-SA/template-plan.mdx +23 -7
  734. package/docs/content/locales/ar-SA/tracking.mdx +1 -1
  735. package/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
  736. package/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
  737. package/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
  738. package/docs/content/locales/de-DE/sharing.mdx +1 -2
  739. package/docs/content/locales/de-DE/skills-guide.mdx +2 -3
  740. package/docs/content/locales/de-DE/template-plan.mdx +23 -7
  741. package/docs/content/locales/de-DE/tracking.mdx +1 -1
  742. package/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
  743. package/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
  744. package/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
  745. package/docs/content/locales/es-ES/sharing.mdx +1 -2
  746. package/docs/content/locales/es-ES/skills-guide.mdx +2 -3
  747. package/docs/content/locales/es-ES/template-plan.mdx +23 -7
  748. package/docs/content/locales/es-ES/tracking.mdx +1 -1
  749. package/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
  750. package/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
  751. package/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
  752. package/docs/content/locales/fr-FR/sharing.mdx +1 -2
  753. package/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
  754. package/docs/content/locales/fr-FR/template-plan.mdx +23 -7
  755. package/docs/content/locales/fr-FR/tracking.mdx +1 -1
  756. package/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
  757. package/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
  758. package/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
  759. package/docs/content/locales/hi-IN/sharing.mdx +1 -2
  760. package/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
  761. package/docs/content/locales/hi-IN/template-plan.mdx +23 -7
  762. package/docs/content/locales/hi-IN/tracking.mdx +1 -1
  763. package/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
  764. package/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
  765. package/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
  766. package/docs/content/locales/ja-JP/sharing.mdx +1 -2
  767. package/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
  768. package/docs/content/locales/ja-JP/template-plan.mdx +23 -7
  769. package/docs/content/locales/ja-JP/tracking.mdx +1 -1
  770. package/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
  771. package/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
  772. package/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
  773. package/docs/content/locales/ko-KR/sharing.mdx +1 -2
  774. package/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
  775. package/docs/content/locales/ko-KR/template-plan.mdx +23 -7
  776. package/docs/content/locales/ko-KR/tracking.mdx +1 -1
  777. package/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
  778. package/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
  779. package/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
  780. package/docs/content/locales/pt-BR/sharing.mdx +1 -2
  781. package/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
  782. package/docs/content/locales/pt-BR/template-plan.mdx +23 -7
  783. package/docs/content/locales/pt-BR/tracking.mdx +1 -1
  784. package/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
  785. package/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
  786. package/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
  787. package/docs/content/locales/zh-CN/sharing.mdx +1 -2
  788. package/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
  789. package/docs/content/locales/zh-CN/template-plan.mdx +23 -7
  790. package/docs/content/locales/zh-CN/tracking.mdx +1 -1
  791. package/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
  792. package/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
  793. package/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
  794. package/docs/content/locales/zh-TW/sharing.mdx +1 -2
  795. package/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
  796. package/docs/content/locales/zh-TW/template-plan.mdx +23 -7
  797. package/docs/content/locales/zh-TW/tracking.mdx +1 -1
  798. package/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
  799. package/docs/content/plan-plugin.mdx +12 -11
  800. package/docs/content/sharing.mdx +1 -2
  801. package/docs/content/skills-guide.mdx +4 -3
  802. package/docs/content/template-plan.mdx +37 -6
  803. package/docs/content/tracking.mdx +1 -1
  804. package/docs/content/workspace-connections.mdx +1 -1
  805. package/package.json +7 -6
  806. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  807. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  808. package/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  809. package/src/templates/default/app/root.tsx +47 -0
  810. package/src/templates/default/package.json +3 -3
  811. package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  812. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  813. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  814. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  815. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  816. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  817. package/corpus/core/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  818. package/corpus/core/docs/content/locales/de-DE/template-videos.mdx +0 -336
  819. package/corpus/core/docs/content/locales/es-ES/template-videos.mdx +0 -336
  820. package/corpus/core/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  821. package/corpus/core/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  822. package/corpus/core/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  823. package/corpus/core/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  824. package/corpus/core/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  825. package/corpus/core/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  826. package/corpus/core/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  827. package/corpus/core/docs/content/template-videos.mdx +0 -332
  828. package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +0 -697
  829. package/corpus/templates/slides/app/components/editor/CodeEditor.tsx +0 -49
  830. package/corpus/templates/videos/.agents/skills/actions/SKILL.md +0 -432
  831. package/corpus/templates/videos/.agents/skills/animation-tracks/SKILL.md +0 -99
  832. package/corpus/templates/videos/.agents/skills/capture-learnings/SKILL.md +0 -76
  833. package/corpus/templates/videos/.agents/skills/composition-management/SKILL.md +0 -106
  834. package/corpus/templates/videos/.agents/skills/create-skill/SKILL.md +0 -221
  835. package/corpus/templates/videos/.agents/skills/delegate-to-agent/SKILL.md +0 -263
  836. package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +0 -112
  837. package/corpus/templates/videos/.agents/skills/security/SKILL.md +0 -263
  838. package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +0 -88
  839. package/corpus/templates/videos/.agents/skills/shadcn-ui/SKILL.md +0 -95
  840. package/corpus/templates/videos/.builder/ANIMATED_PROPERTIES.md +0 -335
  841. package/corpus/templates/videos/.builder/KEYFRAME_SYNC.md +0 -166
  842. package/corpus/templates/videos/.builder/SEAMLESS_SYNC.md +0 -211
  843. package/corpus/templates/videos/.builder/rules/deploy-app.mdc +0 -22
  844. package/corpus/templates/videos/.builder/rules/organize-ui.mdc +0 -9
  845. package/corpus/templates/videos/.builder/skills/remotion/SKILL.md +0 -29
  846. package/corpus/templates/videos/.builder/skills/remotion/rules/animations.md +0 -27
  847. package/corpus/templates/videos/.builder/skills/remotion/rules/assets.md +0 -43
  848. package/corpus/templates/videos/.builder/skills/remotion/rules/audio.md +0 -58
  849. package/corpus/templates/videos/.builder/skills/remotion/rules/compositions.md +0 -129
  850. package/corpus/templates/videos/.builder/skills/remotion/rules/fonts.md +0 -47
  851. package/corpus/templates/videos/.builder/skills/remotion/rules/images.md +0 -41
  852. package/corpus/templates/videos/.builder/skills/remotion/rules/parameters.md +0 -59
  853. package/corpus/templates/videos/.builder/skills/remotion/rules/sequencing.md +0 -54
  854. package/corpus/templates/videos/.builder/skills/remotion/rules/tailwind.md +0 -11
  855. package/corpus/templates/videos/.builder/skills/remotion/rules/text-animations.md +0 -18
  856. package/corpus/templates/videos/.builder/skills/remotion/rules/timing.md +0 -103
  857. package/corpus/templates/videos/.builder/skills/remotion/rules/transparent-videos.md +0 -35
  858. package/corpus/templates/videos/.builder/skills/remotion/rules/trimming.md +0 -38
  859. package/corpus/templates/videos/.builder/skills/remotion/rules/videos.md +0 -54
  860. package/corpus/templates/videos/.dockerignore +0 -62
  861. package/corpus/templates/videos/.env.example +0 -4
  862. package/corpus/templates/videos/.oxfmtrc.json +0 -8
  863. package/corpus/templates/videos/AGENTS.md +0 -46
  864. package/corpus/templates/videos/CHANGELOG.md +0 -10
  865. package/corpus/templates/videos/DEVELOPING.md +0 -143
  866. package/corpus/templates/videos/README.md +0 -33
  867. package/corpus/templates/videos/actions/analyze-brand-assets.ts +0 -151
  868. package/corpus/templates/videos/actions/apply-design-system.ts +0 -35
  869. package/corpus/templates/videos/actions/create-design-system.ts +0 -84
  870. package/corpus/templates/videos/actions/create-folder.ts +0 -48
  871. package/corpus/templates/videos/actions/db-connect.ts +0 -45
  872. package/corpus/templates/videos/actions/db-health.ts +0 -24
  873. package/corpus/templates/videos/actions/db-status.ts +0 -28
  874. package/corpus/templates/videos/actions/delete-composition.ts +0 -272
  875. package/corpus/templates/videos/actions/delete-folder.ts +0 -27
  876. package/corpus/templates/videos/actions/generate-animated-component.ts +0 -470
  877. package/corpus/templates/videos/actions/get-composition.ts +0 -36
  878. package/corpus/templates/videos/actions/get-design-system.ts +0 -35
  879. package/corpus/templates/videos/actions/import-code.ts +0 -67
  880. package/corpus/templates/videos/actions/import-design-project.ts +0 -42
  881. package/corpus/templates/videos/actions/import-document.ts +0 -125
  882. package/corpus/templates/videos/actions/import-from-url.ts +0 -18
  883. package/corpus/templates/videos/actions/import-github.ts +0 -182
  884. package/corpus/templates/videos/actions/list-compositions.ts +0 -32
  885. package/corpus/templates/videos/actions/list-design-systems.ts +0 -66
  886. package/corpus/templates/videos/actions/list-folders.ts +0 -61
  887. package/corpus/templates/videos/actions/move-composition-to-folder.ts +0 -59
  888. package/corpus/templates/videos/actions/navigate.ts +0 -40
  889. package/corpus/templates/videos/actions/rename-folder.ts +0 -41
  890. package/corpus/templates/videos/actions/run.ts +0 -2
  891. package/corpus/templates/videos/actions/save-composition.ts +0 -114
  892. package/corpus/templates/videos/actions/set-default-design-system.ts +0 -40
  893. package/corpus/templates/videos/actions/update-composition.ts +0 -81
  894. package/corpus/templates/videos/actions/update-design-system.ts +0 -68
  895. package/corpus/templates/videos/actions/validate-compositions.ts +0 -502
  896. package/corpus/templates/videos/actions/view-screen.ts +0 -48
  897. package/corpus/templates/videos/app/components/CameraControls.tsx +0 -442
  898. package/corpus/templates/videos/app/components/CameraToolbar.tsx +0 -437
  899. package/corpus/templates/videos/app/components/CloudUpgrade.tsx +0 -287
  900. package/corpus/templates/videos/app/components/CollabPresenceBar.tsx +0 -81
  901. package/corpus/templates/videos/app/components/CompSettingsEditor.tsx +0 -107
  902. package/corpus/templates/videos/app/components/ComponentLibrarySidebar.tsx +0 -457
  903. package/corpus/templates/videos/app/components/ComponentPropsPanel.tsx +0 -227
  904. package/corpus/templates/videos/app/components/ComponentsSidebar.tsx +0 -70
  905. package/corpus/templates/videos/app/components/CompositionCard.tsx +0 -147
  906. package/corpus/templates/videos/app/components/CurrentElementPanel.tsx +0 -835
  907. package/corpus/templates/videos/app/components/CursorControls.tsx +0 -664
  908. package/corpus/templates/videos/app/components/CursorPositioningOverlay.tsx +0 -238
  909. package/corpus/templates/videos/app/components/LibraryFolderRow.tsx +0 -235
  910. package/corpus/templates/videos/app/components/MotionCurveSelect.tsx +0 -61
  911. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +0 -192
  912. package/corpus/templates/videos/app/components/PropsEditor.tsx +0 -210
  913. package/corpus/templates/videos/app/components/QuestionFlow.tsx +0 -40
  914. package/corpus/templates/videos/app/components/Sidebar.tsx +0 -609
  915. package/corpus/templates/videos/app/components/StudioHeader.tsx +0 -109
  916. package/corpus/templates/videos/app/components/Timeline.tsx +0 -2067
  917. package/corpus/templates/videos/app/components/TrackPropertiesPanel.tsx +0 -922
  918. package/corpus/templates/videos/app/components/TweaksPanel.tsx +0 -277
  919. package/corpus/templates/videos/app/components/VideoPlayer.tsx +0 -487
  920. package/corpus/templates/videos/app/components/design-system/DesignSystemCard.tsx +0 -134
  921. package/corpus/templates/videos/app/components/design-system/DesignSystemPreview.tsx +0 -176
  922. package/corpus/templates/videos/app/components/design-system/DesignSystemSetup.tsx +0 -707
  923. package/corpus/templates/videos/app/components/keyframes/KeyframeActionButtons.tsx +0 -78
  924. package/corpus/templates/videos/app/components/keyframes/KeyframeNavigation.tsx +0 -59
  925. package/corpus/templates/videos/app/components/keyframes/PropertyNumberInput.tsx +0 -56
  926. package/corpus/templates/videos/app/components/layout/Header.tsx +0 -64
  927. package/corpus/templates/videos/app/components/layout/HeaderActions.tsx +0 -84
  928. package/corpus/templates/videos/app/components/layout/Layout.tsx +0 -132
  929. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -180
  930. package/corpus/templates/videos/app/components/ui/accordion.tsx +0 -56
  931. package/corpus/templates/videos/app/components/ui/alert-dialog.tsx +0 -139
  932. package/corpus/templates/videos/app/components/ui/alert.tsx +0 -59
  933. package/corpus/templates/videos/app/components/ui/aspect-ratio.tsx +0 -5
  934. package/corpus/templates/videos/app/components/ui/avatar.tsx +0 -48
  935. package/corpus/templates/videos/app/components/ui/badge.tsx +0 -37
  936. package/corpus/templates/videos/app/components/ui/breadcrumb.tsx +0 -115
  937. package/corpus/templates/videos/app/components/ui/button.tsx +0 -57
  938. package/corpus/templates/videos/app/components/ui/calendar.tsx +0 -68
  939. package/corpus/templates/videos/app/components/ui/card.tsx +0 -86
  940. package/corpus/templates/videos/app/components/ui/carousel.tsx +0 -260
  941. package/corpus/templates/videos/app/components/ui/chart.tsx +0 -375
  942. package/corpus/templates/videos/app/components/ui/checkbox.tsx +0 -28
  943. package/corpus/templates/videos/app/components/ui/collapsible.tsx +0 -9
  944. package/corpus/templates/videos/app/components/ui/command.tsx +0 -154
  945. package/corpus/templates/videos/app/components/ui/context-menu.tsx +0 -202
  946. package/corpus/templates/videos/app/components/ui/dialog.tsx +0 -130
  947. package/corpus/templates/videos/app/components/ui/drawer.tsx +0 -116
  948. package/corpus/templates/videos/app/components/ui/dropdown-menu.tsx +0 -212
  949. package/corpus/templates/videos/app/components/ui/form.tsx +0 -177
  950. package/corpus/templates/videos/app/components/ui/hover-card.tsx +0 -27
  951. package/corpus/templates/videos/app/components/ui/input-otp.tsx +0 -69
  952. package/corpus/templates/videos/app/components/ui/input.tsx +0 -22
  953. package/corpus/templates/videos/app/components/ui/label.tsx +0 -24
  954. package/corpus/templates/videos/app/components/ui/menubar.tsx +0 -234
  955. package/corpus/templates/videos/app/components/ui/navigation-menu.tsx +0 -128
  956. package/corpus/templates/videos/app/components/ui/pagination.tsx +0 -121
  957. package/corpus/templates/videos/app/components/ui/popover.tsx +0 -44
  958. package/corpus/templates/videos/app/components/ui/progress.tsx +0 -26
  959. package/corpus/templates/videos/app/components/ui/radio-group.tsx +0 -42
  960. package/corpus/templates/videos/app/components/ui/resizable.tsx +0 -43
  961. package/corpus/templates/videos/app/components/ui/scroll-area.tsx +0 -46
  962. package/corpus/templates/videos/app/components/ui/select.tsx +0 -158
  963. package/corpus/templates/videos/app/components/ui/separator.tsx +0 -29
  964. package/corpus/templates/videos/app/components/ui/sheet.tsx +0 -160
  965. package/corpus/templates/videos/app/components/ui/sidebar.tsx +0 -782
  966. package/corpus/templates/videos/app/components/ui/skeleton.tsx +0 -15
  967. package/corpus/templates/videos/app/components/ui/slider.tsx +0 -26
  968. package/corpus/templates/videos/app/components/ui/sonner.tsx +0 -58
  969. package/corpus/templates/videos/app/components/ui/spinner.tsx +0 -17
  970. package/corpus/templates/videos/app/components/ui/switch.tsx +0 -27
  971. package/corpus/templates/videos/app/components/ui/table.tsx +0 -117
  972. package/corpus/templates/videos/app/components/ui/tabs.tsx +0 -53
  973. package/corpus/templates/videos/app/components/ui/textarea.tsx +0 -23
  974. package/corpus/templates/videos/app/components/ui/toast.tsx +0 -127
  975. package/corpus/templates/videos/app/components/ui/toaster.tsx +0 -33
  976. package/corpus/templates/videos/app/components/ui/toggle-group.tsx +0 -59
  977. package/corpus/templates/videos/app/components/ui/toggle.tsx +0 -43
  978. package/corpus/templates/videos/app/components/ui/tooltip.tsx +0 -30
  979. package/corpus/templates/videos/app/components/ui/use-toast.ts +0 -3
  980. package/corpus/templates/videos/app/config/constants.ts +0 -204
  981. package/corpus/templates/videos/app/contexts/CompositionContext.tsx +0 -437
  982. package/corpus/templates/videos/app/contexts/CurrentElementContext.tsx +0 -247
  983. package/corpus/templates/videos/app/contexts/PlaybackContext.tsx +0 -65
  984. package/corpus/templates/videos/app/contexts/TimelineContext.tsx +0 -433
  985. package/corpus/templates/videos/app/entry.client.tsx +0 -19
  986. package/corpus/templates/videos/app/entry.server.tsx +0 -10
  987. package/corpus/templates/videos/app/global.css +0 -171
  988. package/corpus/templates/videos/app/hooks/use-composition-collab.ts +0 -269
  989. package/corpus/templates/videos/app/hooks/use-composition-design-system.ts +0 -47
  990. package/corpus/templates/videos/app/hooks/use-database-compositions.ts +0 -144
  991. package/corpus/templates/videos/app/hooks/use-db-status.ts +0 -30
  992. package/corpus/templates/videos/app/hooks/use-design-systems.ts +0 -21
  993. package/corpus/templates/videos/app/hooks/use-folders.ts +0 -199
  994. package/corpus/templates/videos/app/hooks/use-mobile.tsx +0 -21
  995. package/corpus/templates/videos/app/hooks/use-navigation-state.ts +0 -52
  996. package/corpus/templates/videos/app/hooks/use-question-flow.ts +0 -14
  997. package/corpus/templates/videos/app/hooks/use-toast.ts +0 -188
  998. package/corpus/templates/videos/app/hooks/useUnsavedChanges.ts +0 -48
  999. package/corpus/templates/videos/app/i18n/ar-SA.ts +0 -446
  1000. package/corpus/templates/videos/app/i18n/de-DE.ts +0 -470
  1001. package/corpus/templates/videos/app/i18n/en-US.ts +0 -452
  1002. package/corpus/templates/videos/app/i18n/es-ES.ts +0 -468
  1003. package/corpus/templates/videos/app/i18n/fr-FR.ts +0 -469
  1004. package/corpus/templates/videos/app/i18n/hi-IN.ts +0 -446
  1005. package/corpus/templates/videos/app/i18n/index.ts +0 -34
  1006. package/corpus/templates/videos/app/i18n/ja-JP.ts +0 -460
  1007. package/corpus/templates/videos/app/i18n/ko-KR.ts +0 -453
  1008. package/corpus/templates/videos/app/i18n/pt-BR.ts +0 -461
  1009. package/corpus/templates/videos/app/i18n/zh-CN.ts +0 -437
  1010. package/corpus/templates/videos/app/i18n/zh-TW.ts +0 -437
  1011. package/corpus/templates/videos/app/lib/database-compositions.ts +0 -67
  1012. package/corpus/templates/videos/app/lib/design-systems.ts +0 -234
  1013. package/corpus/templates/videos/app/lib/tab-id.ts +0 -1
  1014. package/corpus/templates/videos/app/lib/utils.ts +0 -1
  1015. package/corpus/templates/videos/app/pages/ComponentLibrary.tsx +0 -134
  1016. package/corpus/templates/videos/app/pages/ComponentLibraryView.tsx +0 -362
  1017. package/corpus/templates/videos/app/pages/CompositionView.tsx +0 -715
  1018. package/corpus/templates/videos/app/pages/DesignSystems.tsx +0 -180
  1019. package/corpus/templates/videos/app/pages/Index.tsx +0 -258
  1020. package/corpus/templates/videos/app/pages/NewComposition.tsx +0 -95
  1021. package/corpus/templates/videos/app/pages/NotFound.tsx +0 -36
  1022. package/corpus/templates/videos/app/remotion/CameraHost.tsx +0 -223
  1023. package/corpus/templates/videos/app/remotion/animations/useElementAnimations.ts +0 -231
  1024. package/corpus/templates/videos/app/remotion/componentRegistry.ts +0 -473
  1025. package/corpus/templates/videos/app/remotion/components/AnimatedElement.tsx +0 -436
  1026. package/corpus/templates/videos/app/remotion/components/InteractiveElement.tsx +0 -159
  1027. package/corpus/templates/videos/app/remotion/components/InteractiveZoneEditor.tsx +0 -273
  1028. package/corpus/templates/videos/app/remotion/compositions/BlankComposition.tsx +0 -204
  1029. package/corpus/templates/videos/app/remotion/compositions/index.ts +0 -2
  1030. package/corpus/templates/videos/app/remotion/createComponentCursorTrack.ts +0 -132
  1031. package/corpus/templates/videos/app/remotion/docs/CURSOR_TRACKS.md +0 -285
  1032. package/corpus/templates/videos/app/remotion/easingFunctions.ts +0 -255
  1033. package/corpus/templates/videos/app/remotion/hooks/createInteractiveComposition.tsx +0 -283
  1034. package/corpus/templates/videos/app/remotion/hooks/useCursorHistory.ts +0 -72
  1035. package/corpus/templates/videos/app/remotion/hooks/useCursorTypeFromHover.ts +0 -35
  1036. package/corpus/templates/videos/app/remotion/hooks/useEditableZones.tsx +0 -267
  1037. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimation.ts +0 -90
  1038. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimationSmooth.ts +0 -146
  1039. package/corpus/templates/videos/app/remotion/hooks/useInteractiveComponent.ts +0 -711
  1040. package/corpus/templates/videos/app/remotion/hooks/useRegisterInteractiveElement.ts +0 -56
  1041. package/corpus/templates/videos/app/remotion/library-components/Button.tsx +0 -252
  1042. package/corpus/templates/videos/app/remotion/library-components/Card.tsx +0 -241
  1043. package/corpus/templates/videos/app/remotion/library-components/CodePanel.tsx +0 -1005
  1044. package/corpus/templates/videos/app/remotion/library-components/FileItem.tsx +0 -196
  1045. package/corpus/templates/videos/app/remotion/library-components/FolderItem.tsx +0 -199
  1046. package/corpus/templates/videos/app/remotion/library-components/PrimaryButton.tsx +0 -177
  1047. package/corpus/templates/videos/app/remotion/library-components/SecondaryButton.tsx +0 -167
  1048. package/corpus/templates/videos/app/remotion/library-components/SectionHeader.tsx +0 -108
  1049. package/corpus/templates/videos/app/remotion/library-components/TEMPLATE.tsx +0 -211
  1050. package/corpus/templates/videos/app/remotion/library-components/index.ts +0 -10
  1051. package/corpus/templates/videos/app/remotion/registry.ts +0 -91
  1052. package/corpus/templates/videos/app/remotion/testing/animationTestUtils.ts +0 -366
  1053. package/corpus/templates/videos/app/remotion/trackAnimation.ts +0 -179
  1054. package/corpus/templates/videos/app/remotion/trackHelpers.ts +0 -110
  1055. package/corpus/templates/videos/app/remotion/ui-components/Cursor.tsx +0 -182
  1056. package/corpus/templates/videos/app/remotion/ui-components/InteractiveButton.tsx +0 -116
  1057. package/corpus/templates/videos/app/remotion/ui-components/InteractiveCard.tsx +0 -126
  1058. package/corpus/templates/videos/app/remotion/ui-components/index.ts +0 -16
  1059. package/corpus/templates/videos/app/remotion/utils/animationHelpers.ts +0 -444
  1060. package/corpus/templates/videos/app/remotion/utils/interactiveElements.ts +0 -235
  1061. package/corpus/templates/videos/app/remotion/utils/springAnimation.ts +0 -76
  1062. package/corpus/templates/videos/app/root.tsx +0 -165
  1063. package/corpus/templates/videos/app/routes/$.tsx +0 -10
  1064. package/corpus/templates/videos/app/routes/_index.tsx +0 -25
  1065. package/corpus/templates/videos/app/routes/c.$compositionId.tsx +0 -10
  1066. package/corpus/templates/videos/app/routes/components.tsx +0 -10
  1067. package/corpus/templates/videos/app/routes/design-systems.tsx +0 -10
  1068. package/corpus/templates/videos/app/routes/extensions.$id.$slug.tsx +0 -2
  1069. package/corpus/templates/videos/app/routes/extensions.$id.tsx +0 -5
  1070. package/corpus/templates/videos/app/routes/extensions._index.tsx +0 -5
  1071. package/corpus/templates/videos/app/routes/extensions.tsx +0 -5
  1072. package/corpus/templates/videos/app/routes/settings.tsx +0 -88
  1073. package/corpus/templates/videos/app/routes/team.tsx +0 -11
  1074. package/corpus/templates/videos/app/routes.ts +0 -4
  1075. package/corpus/templates/videos/app/state.ts +0 -77
  1076. package/corpus/templates/videos/app/types/elementAnimations.ts +0 -121
  1077. package/corpus/templates/videos/app/types.ts +0 -200
  1078. package/corpus/templates/videos/app/utils/compositionHelpers.ts +0 -445
  1079. package/corpus/templates/videos/app/utils/debug.ts +0 -63
  1080. package/corpus/templates/videos/app/utils/keyframeUtils.ts +0 -331
  1081. package/corpus/templates/videos/app/utils/timelineCoordinates.ts +0 -79
  1082. package/corpus/templates/videos/app/vite-env.d.ts +0 -6
  1083. package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
  1084. package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
  1085. package/corpus/templates/videos/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
  1086. package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +0 -6
  1087. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
  1088. package/corpus/templates/videos/components.json +0 -20
  1089. package/corpus/templates/videos/data/sync-config.json +0 -3
  1090. package/corpus/templates/videos/docs/examples/create-composition.example.ts +0 -128
  1091. package/corpus/templates/videos/drizzle.config.ts +0 -3
  1092. package/corpus/templates/videos/learnings.defaults.md +0 -5
  1093. package/corpus/templates/videos/package.json +0 -114
  1094. package/corpus/templates/videos/public/agent-native-icon-dark.svg +0 -10
  1095. package/corpus/templates/videos/public/agent-native-icon-light.svg +0 -10
  1096. package/corpus/templates/videos/public/agent-native-logo-dark.svg +0 -21
  1097. package/corpus/templates/videos/public/agent-native-logo-light.svg +0 -21
  1098. package/corpus/templates/videos/public/favicon.svg +0 -1
  1099. package/corpus/templates/videos/public/icon-180.svg +0 -1
  1100. package/corpus/templates/videos/public/icon-192.svg +0 -1
  1101. package/corpus/templates/videos/public/icon-512.svg +0 -1
  1102. package/corpus/templates/videos/public/manifest.json +0 -21
  1103. package/corpus/templates/videos/public/placeholder.svg +0 -1
  1104. package/corpus/templates/videos/public/robots.txt +0 -14
  1105. package/corpus/templates/videos/react-router.config.ts +0 -7
  1106. package/corpus/templates/videos/server/db/index.ts +0 -37
  1107. package/corpus/templates/videos/server/db/schema.ts +0 -55
  1108. package/corpus/templates/videos/server/middleware/auth.ts +0 -15
  1109. package/corpus/templates/videos/server/plugins/agent-chat.ts +0 -29
  1110. package/corpus/templates/videos/server/plugins/auth.ts +0 -14
  1111. package/corpus/templates/videos/server/plugins/collab.ts +0 -11
  1112. package/corpus/templates/videos/server/plugins/core-routes.ts +0 -22
  1113. package/corpus/templates/videos/server/plugins/db.ts +0 -160
  1114. package/corpus/templates/videos/server/routes/[...page].get.ts +0 -5
  1115. package/corpus/templates/videos/server/routes/[...page].head.ts +0 -1
  1116. package/corpus/templates/videos/server/routes/api/save-composition-defaults.post.ts +0 -274
  1117. package/corpus/templates/videos/shared/api.ts +0 -75
  1118. package/corpus/templates/videos/ssr-entry.ts +0 -15
  1119. package/corpus/templates/videos/tsconfig.json +0 -21
  1120. package/corpus/templates/videos/vite.config.ts +0 -19
  1121. package/corpus/templates/videos/vitest.config.ts +0 -16
  1122. package/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  1123. package/docs/content/locales/de-DE/template-videos.mdx +0 -336
  1124. package/docs/content/locales/es-ES/template-videos.mdx +0 -336
  1125. package/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  1126. package/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  1127. package/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  1128. package/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  1129. package/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  1130. package/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  1131. package/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  1132. package/docs/content/template-videos.mdx +0 -332
@@ -23,12 +23,17 @@ import {
23
23
  usePresence,
24
24
  useFollowUser,
25
25
  LiveCursorOverlay,
26
+ RemoteSelectionRings,
27
+ RecentEditHighlights,
28
+ useRecentEdits,
26
29
  useT,
27
30
  useChangeVersion,
28
31
  setAgentChatContextItem,
29
32
  removeAgentChatContextItem,
30
33
  useAvatarUrl,
31
34
  type CollabUser,
35
+ type AttributedRecentEdit,
36
+ type OtherPresence,
32
37
  type PromptComposerSubmitOptions,
33
38
  } from "@agent-native/core/client";
34
39
  import type { TweakDefinition } from "@shared/api";
@@ -57,6 +62,7 @@ import {
57
62
  DESIGN_CAPABILITY_NAMES,
58
63
  hasCapability,
59
64
  } from "@shared/design-source-capabilities";
65
+ import { FULL_APP_BUILDING_ENABLED, readFusionApp } from "@shared/full-app";
60
66
  import { shouldUseLiveFileContent } from "@shared/html-content";
61
67
  import {
62
68
  compile as compileMotionTimeline,
@@ -64,6 +70,16 @@ import {
64
70
  } from "@shared/motion-compiler";
65
71
  import type { MotionTrack } from "@shared/motion-timeline";
66
72
  import { sortMotionKeyframes } from "@shared/motion-timeline";
73
+ import {
74
+ createCornerNode,
75
+ getPenPathGeometry,
76
+ parsePenNodes,
77
+ serializePenNodes,
78
+ serializePenPath,
79
+ type PenNode,
80
+ type PenPath,
81
+ type PenPoint,
82
+ } from "@shared/pen-path";
67
83
  import {
68
84
  resolveTweaksToCssVars,
69
85
  type TweakSelections,
@@ -155,9 +171,11 @@ import {
155
171
  CanvasContextMenu,
156
172
  type CanvasContextMenuHandle,
157
173
  } from "@/components/design/CanvasContextMenu";
158
- import { type CodeWorkbenchActiveFile } from "@/components/design/CodeWorkbenchHost";
174
+ import { type CodeWorkbenchActiveFile } from "@/components/design/code-workbench/CodeWorkbench";
159
175
  import {
160
176
  DesignCanvas,
177
+ type CreatePrimitiveSpec,
178
+ type CreationTool,
161
179
  type IframeContextMenuPayload,
162
180
  type IframeHotkeyPayload,
163
181
  type MotionTrackWire,
@@ -174,12 +192,15 @@ import {
174
192
  type InspectCodeData,
175
193
  type InspectorTab,
176
194
  type ScreenGeometrySelection,
195
+ type StyleChangeMeta,
177
196
  } from "@/components/design/EditPanel";
197
+ import { FusionAppBanner } from "@/components/design/FusionAppBanner";
178
198
  import type { ExportSettingsValue } from "@/components/design/inspector";
179
199
  import { InspectorAiActions } from "@/components/design/inspector/InspectorAiActions";
180
200
  import {
181
201
  LayersPanel,
182
202
  type LayersPanelFile,
203
+ type LayersPanelHandle,
183
204
  type LayersPanelMoveIntent,
184
205
  type LayersPanelNode,
185
206
  } from "@/components/design/LayersPanel";
@@ -199,6 +220,8 @@ import {
199
220
  type CanvasPrimitiveInsert,
200
221
  type FrameGeometry,
201
222
  type MultiScreenCanvasTool,
223
+ type Point,
224
+ type VectorEditOverlayState,
202
225
  } from "@/components/design/MultiScreenCanvas";
203
226
  import { QuestionFlow } from "@/components/design/QuestionFlow";
204
227
  import type { ReviewPanelProps } from "@/components/design/ReviewPanel";
@@ -504,6 +527,59 @@ export function getSelectedScreenGeometryForInspector(args: {
504
527
  };
505
528
  }
506
529
 
530
+ /**
531
+ * Vector-edit foundations (P5 integration): resolves the canvas-space point
532
+ * where a screen's own screen-content-local (0,0) sits — i.e.
533
+ * `VectorEditOverlayState.originCanvas` — for a committed pen path owned by
534
+ * `screenId`.
535
+ *
536
+ * Mirrors the exact same fallback-merge (`getInitialFrameGeometry(index,
537
+ * ...)` overridden by any persisted `canvasFrameGeometryById` entry) that
538
+ * `getSelectedScreenGeometryForInspector` above and MultiScreenCanvas's own
539
+ * internal frame-geometry resolution both use, so the overlay lines up with
540
+ * whatever frame position is actually rendered on screen.
541
+ *
542
+ * The reserved board file (`__board__.html`, see shared/board-file.ts) is the
543
+ * one exception: `handleBoardDrawPrimitive`/`persistDraftPrimitive` commit
544
+ * board primitives with a 1:1 `{x:0,y:0,width:1,height:1}` frame (no offset
545
+ * subtraction — see MultiScreenCanvas's persistDraftPrimitive), so a board
546
+ * pen path's nodes are already in absolute canvas coordinates and
547
+ * `originCanvas` is `{0, 0}`.
548
+ *
549
+ * Returns `null` when the screen can't be resolved (not in `overviewScreens`
550
+ * and not the board file) — the caller should not enter vector-edit mode in
551
+ * that case.
552
+ */
553
+ export function getScreenFrameOriginCanvas(args: {
554
+ screenId: string;
555
+ overviewScreens: Array<{
556
+ id: string;
557
+ width?: number;
558
+ height?: number;
559
+ }>;
560
+ canvasFrameGeometryById: CanvasFrameGeometryById;
561
+ boardFileId?: string | null;
562
+ }): Point | null {
563
+ if (args.boardFileId && args.screenId === args.boardFileId) {
564
+ return { x: 0, y: 0 };
565
+ }
566
+ const screenIndex = args.overviewScreens.findIndex(
567
+ (screen) => screen.id === args.screenId,
568
+ );
569
+ if (screenIndex < 0) return null;
570
+ const screen = args.overviewScreens[screenIndex];
571
+ if (!screen) return null;
572
+ const fallbackGeometry = getInitialFrameGeometry(screenIndex, {
573
+ width: screen.width ?? 1280,
574
+ height: screen.height ?? 2560,
575
+ });
576
+ const persistedGeometry = args.canvasFrameGeometryById[args.screenId] ?? {};
577
+ return {
578
+ x: persistedGeometry.x ?? fallbackGeometry.x,
579
+ y: persistedGeometry.y ?? fallbackGeometry.y,
580
+ };
581
+ }
582
+
507
583
  function fileIdFromLayerSelectionId(
508
584
  layerId: string,
509
585
  fileIds: Set<string>,
@@ -744,6 +820,28 @@ export function shouldReplacePreviewAfterVisualStyleCommit(args: {
744
820
  return !args.runtimeApplied && !args.runtimeStyleApplied;
745
821
  }
746
822
 
823
+ /**
824
+ * PF12: decide whether a style change from EditPanel (ScrubInput scrub tick /
825
+ * DesignColorPicker drag tick) should skip the expensive source commit
826
+ * (commitVisualStyles / commitStylesToSelectedLayers — projection parse, HTML
827
+ * patch, Yjs write, history entry) and instead only try the cheap live
828
+ * iframe preview.
829
+ *
830
+ * Only a genuine mid-gesture "preview" tick against a single selected element
831
+ * qualifies: `meta` is undefined for every existing call site that never
832
+ * passed gesture metadata (keyboard edits, agent edits, discrete commits),
833
+ * so this returns false for all of them — preserving prior full-commit
834
+ * behavior exactly. Multi-layer-selection edits (`selectedLayerCount > 1`)
835
+ * have no equivalent cheap multi-element preview channel, so those
836
+ * conservatively keep committing on every tick, same as before PF12.
837
+ */
838
+ export function shouldSkipVisualStyleCommitForPreview(args: {
839
+ phase?: "preview" | "commit";
840
+ selectedLayerCount: number;
841
+ }): boolean {
842
+ return args.phase === "preview" && args.selectedLayerCount <= 1;
843
+ }
844
+
747
845
  export function getLayerMoveIterationOrder<T>(
748
846
  orderedIds: readonly T[],
749
847
  placement: "before" | "after" | "inside",
@@ -1174,6 +1272,40 @@ function isSingleScreenAnnotationTool(tool: DesignTool): boolean {
1174
1272
  return tool === "draw" || tool === "comment";
1175
1273
  }
1176
1274
 
1275
+ /**
1276
+ * P4/single-screen placement: maps the editor's current tool + view mode to
1277
+ * the `CreationTool` DesignCanvas's single-screen click-to-place overlay
1278
+ * understands, or `null` when the overlay should not mount.
1279
+ *
1280
+ * Only mounts while actually focused on a single screen (`viewMode ===
1281
+ * "single"`) with an active file — overview mode keeps using
1282
+ * MultiScreenCanvas's own draft-primitive drawing, which this overlay does
1283
+ * not replace. `rect` (the DesignTool id) maps to CreationTool's
1284
+ * `"rectangle"`; `polygon`/`star`/`frame`/`draw`/`hand`/`comment`/`scale`/
1285
+ * `move` have no single-screen click-to-place equivalent yet and map to
1286
+ * `null` so the overlay never mounts for them (existing tool behavior for
1287
+ * those is unaffected).
1288
+ */
1289
+ export function getSingleScreenCreationTool(args: {
1290
+ activeTool: DesignTool;
1291
+ viewMode: "single" | "overview";
1292
+ hasActiveFile: boolean;
1293
+ }): CreationTool | null {
1294
+ if (args.viewMode !== "single" || !args.hasActiveFile) return null;
1295
+ switch (args.activeTool) {
1296
+ case "rect":
1297
+ return "rectangle";
1298
+ case "ellipse":
1299
+ case "line":
1300
+ case "arrow":
1301
+ case "text":
1302
+ case "pen":
1303
+ return args.activeTool;
1304
+ default:
1305
+ return null;
1306
+ }
1307
+ }
1308
+
1177
1309
  interface DesignFile {
1178
1310
  id: string;
1179
1311
  filename: string;
@@ -2433,6 +2565,208 @@ function reassignDuplicatedNodeIds(content: string): string {
2433
2565
  );
2434
2566
  }
2435
2567
 
2568
+ /** Mirrors DesignCanvas's own local CREATION_DEFAULT_LINE_LENGTH (single-
2569
+ * screen creation overlay) so a single-screen pen click gets a visible
2570
+ * minimal path of the same default length a line/arrow click would use,
2571
+ * rather than a zero-length point. */
2572
+ const SINGLE_SCREEN_PEN_CLICK_DEFAULT_LENGTH = 120;
2573
+
2574
+ /**
2575
+ * P4/single-screen placement: converts a `CreatePrimitiveSpec` emitted by
2576
+ * DesignCanvas's single-screen click-to-place overlay (coordinates already in
2577
+ * screen-content space, matching `appendCanvasPrimitiveToHtml`'s own
2578
+ * coordinate system) into the `CanvasPrimitiveInsert` shape the overview path
2579
+ * (`handleCreatePrimitive` / `appendCanvasPrimitiveToHtml`) already knows how
2580
+ * to persist. This deliberately reuses the exact same insert shape as
2581
+ * `draftPrimitiveToInsert` produces in MultiScreenCanvas.tsx so single-screen
2582
+ * and overview creation commit through one shared persistence path instead of
2583
+ * two.
2584
+ *
2585
+ * Pen is the one deferred case (see DesignCanvas's SingleScreenCreationOverlay
2586
+ * doc comment): a single-screen pen click only ever gets a single start
2587
+ * point, so — rather than force overview to get full multi-click pen
2588
+ * authoring — this commits a minimal open 2-node path (start point to a
2589
+ * fixed-length point to the right, matching the line/arrow click default
2590
+ * length) so the user gets a real, editable pen path immediately. True
2591
+ * single-screen multi-click pen authoring remains a follow-up.
2592
+ */
2593
+ export function createPrimitiveInsertFromSpec(
2594
+ spec: CreatePrimitiveSpec,
2595
+ nodeId: string,
2596
+ ): CanvasPrimitiveInsert | null {
2597
+ if (spec.tool === "line" || spec.tool === "arrow") {
2598
+ const points = spec.points;
2599
+ if (!points || points.length < 2) return null;
2600
+ const [start, end] = points;
2601
+ if (!start || !end) return null;
2602
+ const left = Math.min(start.x, end.x);
2603
+ const top = Math.min(start.y, end.y);
2604
+ const width = Math.max(1, Math.abs(end.x - start.x));
2605
+ const height = Math.max(1, Math.abs(end.y - start.y));
2606
+ return {
2607
+ kind: spec.tool,
2608
+ nodeId,
2609
+ geometry: { x: left, y: top, width, height },
2610
+ points,
2611
+ };
2612
+ }
2613
+
2614
+ if (spec.tool === "pen") {
2615
+ const start = spec.points?.[0];
2616
+ if (!start) return null;
2617
+ const end = {
2618
+ x: start.x + SINGLE_SCREEN_PEN_CLICK_DEFAULT_LENGTH,
2619
+ y: start.y,
2620
+ };
2621
+ const penPath: PenPath = {
2622
+ nodes: [createCornerNode(start), createCornerNode(end)],
2623
+ closed: false,
2624
+ };
2625
+ const geometry = getPenPathGeometry(penPath);
2626
+ return {
2627
+ kind: "path",
2628
+ nodeId,
2629
+ geometry,
2630
+ points: [start, end],
2631
+ pathData: serializePenPath(penPath),
2632
+ };
2633
+ }
2634
+
2635
+ const rect = spec.rect;
2636
+ if (!rect) return null;
2637
+ return {
2638
+ kind: spec.tool,
2639
+ nodeId,
2640
+ geometry: {
2641
+ x: rect.x,
2642
+ y: rect.y,
2643
+ width: Math.max(1, rect.width),
2644
+ height: Math.max(1, rect.height),
2645
+ },
2646
+ autoSize: spec.tool === "text" ? spec.fromClick : undefined,
2647
+ };
2648
+ }
2649
+
2650
+ /**
2651
+ * Reconstructs a structured `PenPath` (nodes + optional handles) from a `d`
2652
+ * string produced by `serializePenPath` (shared/pen-path.ts). This is the
2653
+ * deliberate INVERSE of that exact serializer — not a general SVG path
2654
+ * parser — so it only needs to understand the small grammar
2655
+ * `serializePenPath` actually emits: `M x y`, then per-segment `L x y` (both
2656
+ * handles coincide with their anchors) or `C c1x c1y c2x c2y x y`
2657
+ * (`c1`/`c2` are the FROM node's `handleOut` / TO node's `handleIn`,
2658
+ * respectively — see `serializeSegment`), optionally followed by a trailing
2659
+ * `Z` for a closed path (whose preceding segment is the wrap-around from the
2660
+ * last node back to the first, not a new node).
2661
+ *
2662
+ * This exists because `CanvasPrimitiveInsert` (MultiScreenCanvas.tsx) only
2663
+ * carries the already-flattened `pathData` string across the overview
2664
+ * commit boundary, not the richer `DraftPrimitive.penPath` MultiScreenCanvas
2665
+ * keeps internally — so committing a pen path drawn in OVERVIEW mode has no
2666
+ * other source for the structured node/handle data `data-an-pen-nodes`
2667
+ * needs. Single-screen pen placement (`createPrimitiveInsertFromSpec` above)
2668
+ * builds its `PenPath` directly and never needs this reverse parse.
2669
+ *
2670
+ * Returns `null` for anything that doesn't match the expected grammar
2671
+ * (empty/malformed `d`) rather than throwing, so a call site can always fall
2672
+ * back to skipping `data-an-pen-nodes` for that element.
2673
+ */
2674
+ export function parsePenPathFromSerializedD(d: string): PenPath | null {
2675
+ const trimmed = d.trim();
2676
+ if (!trimmed) return null;
2677
+ const tokens = trimmed.match(/[MLCZ]|-?\d+(?:\.\d+)?/gi);
2678
+ if (!tokens || tokens.length === 0) return null;
2679
+
2680
+ const readNumbers = (count: number): PenPoint[] => {
2681
+ const points: PenPoint[] = [];
2682
+ for (let i = 0; i < count; i += 2) {
2683
+ const x = Number(tokens[cursor + i]);
2684
+ const y = Number(tokens[cursor + i + 1]);
2685
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
2686
+ throw new Error("invalid pen path number");
2687
+ }
2688
+ points.push({ x, y });
2689
+ }
2690
+ return points;
2691
+ };
2692
+
2693
+ let cursor = 0;
2694
+ const command = tokens[cursor];
2695
+ if (command?.toUpperCase() !== "M") return null;
2696
+ cursor += 1;
2697
+
2698
+ try {
2699
+ const [start] = readNumbers(2);
2700
+ if (!start) return null;
2701
+ cursor += 2;
2702
+ const nodes: PenNode[] = [{ point: start }];
2703
+ let closed = false;
2704
+
2705
+ while (cursor < tokens.length) {
2706
+ const token = tokens[cursor]?.toUpperCase();
2707
+ if (token === "Z") {
2708
+ cursor += 1;
2709
+ continue;
2710
+ }
2711
+ if (token === "L") {
2712
+ cursor += 1;
2713
+ const [to] = readNumbers(2);
2714
+ if (!to) return null;
2715
+ cursor += 2;
2716
+ // Same wrap-around case as the "C" branch below: a closed path's
2717
+ // final segment (immediately followed by Z) returns to the FIRST
2718
+ // node rather than describing a new one.
2719
+ const nextIsClose = tokens[cursor]?.toUpperCase() === "Z";
2720
+ if (nextIsClose) {
2721
+ closed = true;
2722
+ } else {
2723
+ nodes.push({ point: to });
2724
+ }
2725
+ continue;
2726
+ }
2727
+ if (token === "C") {
2728
+ cursor += 1;
2729
+ const coords = readNumbers(6);
2730
+ cursor += 6;
2731
+ const [c1, c2, to] = coords;
2732
+ if (!c1 || !c2 || !to) return null;
2733
+ const fromNode = nodes[nodes.length - 1];
2734
+ if (!fromNode) return null;
2735
+ if (!samePenPoint(c1, fromNode.point)) {
2736
+ fromNode.handleOut = c1;
2737
+ }
2738
+ // A closed path's final "C"/"L" segment (immediately followed by Z)
2739
+ // wraps from the last real node back to the FIRST node — it is not a
2740
+ // new node. Apply its handles to the existing first node instead of
2741
+ // pushing a duplicate.
2742
+ const nextIsClose = tokens[cursor]?.toUpperCase() === "Z";
2743
+ if (nextIsClose) {
2744
+ closed = true;
2745
+ const firstNode = nodes[0];
2746
+ if (firstNode && !samePenPoint(c2, firstNode.point)) {
2747
+ firstNode.handleIn = c2;
2748
+ }
2749
+ } else {
2750
+ const node: PenNode = { point: to };
2751
+ if (!samePenPoint(c2, to)) node.handleIn = c2;
2752
+ nodes.push(node);
2753
+ }
2754
+ continue;
2755
+ }
2756
+ // Unknown token — grammar mismatch, bail rather than guess.
2757
+ return null;
2758
+ }
2759
+
2760
+ return { nodes, closed };
2761
+ } catch {
2762
+ return null;
2763
+ }
2764
+ }
2765
+
2766
+ function samePenPoint(a: PenPoint, b: PenPoint): boolean {
2767
+ return Math.abs(a.x - b.x) < 0.05 && Math.abs(a.y - b.y) < 0.05;
2768
+ }
2769
+
2436
2770
  function primitiveLayerName(primitive: CanvasPrimitiveInsert): string {
2437
2771
  switch (primitive.kind) {
2438
2772
  case "frame":
@@ -2715,6 +3049,108 @@ function appendCanvasPrimitiveToHtml(
2715
3049
  }
2716
3050
  }
2717
3051
 
3052
+ /**
3053
+ * Vector-edit foundations: stamps `data-an-pen-nodes` (the compact
3054
+ * serializePenNodes encoding — see shared/pen-path.ts) onto the committed
3055
+ * pen-path SVG element identified by `data-agent-native-node-id === nodeId`,
3056
+ * so the structured node/handle data survives independently of the
3057
+ * flattened `d` attribute and can later be re-hydrated into vector edit
3058
+ * mode. Returns `content` unchanged (never null) if the node can't be found
3059
+ * or `content` fails to parse — this is a best-effort enrichment step, never
3060
+ * a hard requirement for the primitive to commit successfully.
3061
+ */
3062
+ function setPenNodesAttributeOnElement(
3063
+ content: string,
3064
+ nodeId: string,
3065
+ penPath: PenPath,
3066
+ ): string {
3067
+ if (typeof window === "undefined") return content;
3068
+ try {
3069
+ const doc = new DOMParser().parseFromString(content, "text/html");
3070
+ // nodeId always comes from uniqueLayerId(...) (alphanumeric/hyphen/UUID
3071
+ // chars only, never quotes), but escape defensively anyway since this
3072
+ // value is interpolated into a CSS attribute-selector string.
3073
+ const safeNodeId = nodeId.replace(/["\\]/g, "\\$&");
3074
+ const element = doc.querySelector(
3075
+ `[data-agent-native-node-id="${safeNodeId}"]`,
3076
+ );
3077
+ if (!element) return content;
3078
+ element.setAttribute("data-an-pen-nodes", serializePenNodes(penPath));
3079
+ return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
3080
+ } catch {
3081
+ return content;
3082
+ }
3083
+ }
3084
+
3085
+ /**
3086
+ * Vector-edit foundations: writes an edited PenPath back onto its committed
3087
+ * SVG element — the `<path>` child's `d` (serializePenPath, matching what
3088
+ * appendCanvasPrimitiveToHtml's explicitPathData branch produces) AND the
3089
+ * `<svg>` root's `data-an-pen-nodes` (serializePenNodes, the structured
3090
+ * round-trip source), plus the root's `viewBox`/left/top/width/height so the
3091
+ * element's bounding box stays correct after anchors/handles moved it.
3092
+ * `nodeId` is the element's `data-agent-native-node-id` value (screen-content
3093
+ * space — same coordinate system the path's own nodes are already in, see
3094
+ * parsePenPathFromSerializedD's doc comment for that finding).
3095
+ *
3096
+ * Returns `content` unchanged (never null/throws) if the element can't be
3097
+ * found, isn't an SVG pen-path root, or has no `<path>` child — the caller
3098
+ * treats that as a no-op commit rather than losing the user's edit.
3099
+ */
3100
+ function writeBackVectorEditedPenPath(
3101
+ content: string,
3102
+ nodeId: string,
3103
+ penPath: PenPath,
3104
+ ): string {
3105
+ if (typeof window === "undefined") return content;
3106
+ try {
3107
+ const doc = new DOMParser().parseFromString(content, "text/html");
3108
+ const safeNodeId = nodeId.replace(/["\\]/g, "\\$&");
3109
+ const svg = doc.querySelector(
3110
+ `[data-agent-native-node-id="${safeNodeId}"]`,
3111
+ );
3112
+ const path = svg?.querySelector("path");
3113
+ if (!svg || !path) return content;
3114
+
3115
+ const d = serializePenPath(penPath);
3116
+ const geometry = getPenPathGeometry(penPath);
3117
+ const isClosed = Boolean(penPath.closed && penPath.nodes.length > 1);
3118
+
3119
+ path.setAttribute("d", d);
3120
+ if (isClosed && path.getAttribute("fill") === "none") {
3121
+ path.setAttribute("fill", "#D9D9D9");
3122
+ } else if (!isClosed) {
3123
+ path.setAttribute("fill", "none");
3124
+ }
3125
+
3126
+ svg.setAttribute("data-an-pen-nodes", serializePenNodes(penPath));
3127
+ svg.setAttribute(
3128
+ "viewBox",
3129
+ `${geometry.x} ${geometry.y} ${geometry.width} ${geometry.height}`,
3130
+ );
3131
+ const existingStyle = svg.getAttribute("style") ?? "";
3132
+ const rotationMatch = existingStyle.match(/transform:[^;]+/);
3133
+ svg.setAttribute(
3134
+ "style",
3135
+ [
3136
+ "position:absolute",
3137
+ `left:${Math.round(geometry.x)}px`,
3138
+ `top:${Math.round(geometry.y)}px`,
3139
+ `width:${Math.max(1, Math.round(geometry.width))}px`,
3140
+ `height:${Math.max(1, Math.round(geometry.height))}px`,
3141
+ "overflow:visible",
3142
+ rotationMatch?.[0] ?? "",
3143
+ ]
3144
+ .filter(Boolean)
3145
+ .join(";"),
3146
+ );
3147
+
3148
+ return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
3149
+ } catch {
3150
+ return content;
3151
+ }
3152
+ }
3153
+
2718
3154
  function polygonPointsForHtmlShape(
2719
3155
  kind: "polygon" | "star",
2720
3156
  width: number,
@@ -4237,11 +4673,11 @@ type DesignLeftPanel =
4237
4673
  | "import"
4238
4674
  | "code";
4239
4675
 
4240
- const SHOW_DESIGN_CODE_LEFT_PANEL = false;
4676
+ const SHOW_DESIGN_CODE_LEFT_PANEL = true;
4241
4677
 
4242
- const CodeWorkbenchHost = lazy(() =>
4243
- import("@/components/design/CodeWorkbenchHost").then((module) => ({
4244
- default: module.CodeWorkbenchHost,
4678
+ const CodeWorkbench = lazy(() =>
4679
+ import("@/components/design/code-workbench/CodeWorkbench").then((module) => ({
4680
+ default: module.CodeWorkbench,
4245
4681
  })),
4246
4682
  );
4247
4683
 
@@ -5196,6 +5632,48 @@ function cloneCanvasFrameGeometry(
5196
5632
  );
5197
5633
  }
5198
5634
 
5635
+ /**
5636
+ * Freshness guard for geometry undo/redo. Returns the ids of frames that a
5637
+ * geometry history entry touched whose LIVE geometry no longer matches what the
5638
+ * entry expects (`expected` = the state this entry previously wrote). A
5639
+ * non-empty result means a concurrent peer/agent moved those frames since the
5640
+ * snapshot was captured, so replaying the entry's stored "before"/"after" would
5641
+ * clobber their change. Frames absent from live geometry (deleted) are treated
5642
+ * as changed. Only compares the frames the entry itself changed, so unrelated
5643
+ * concurrent edits to OTHER frames don't block this undo.
5644
+ */
5645
+ function frameGeometryEquals(
5646
+ a: CanvasFrameGeometry | undefined,
5647
+ b: CanvasFrameGeometry | undefined,
5648
+ ): boolean {
5649
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
5650
+ }
5651
+
5652
+ function staleGeometryFrameIds(
5653
+ entry: GeometryHistoryEntry,
5654
+ live: CanvasFrameGeometryById,
5655
+ expected: CanvasFrameGeometryById,
5656
+ ): string[] {
5657
+ // A frame is "touched" when ANY geometry field differs between before and
5658
+ // after — moves (x/y), rotation, and z-order count, not just viewport size.
5659
+ const touched = new Set<string>(
5660
+ [...Object.keys(entry.before), ...Object.keys(entry.after)].filter(
5661
+ (frameId) =>
5662
+ !frameGeometryEquals(entry.before[frameId], entry.after[frameId]),
5663
+ ),
5664
+ );
5665
+ const stale: string[] = [];
5666
+ for (const frameId of touched) {
5667
+ const expectedGeo = expected[frameId];
5668
+ const liveGeo = live[frameId];
5669
+ if (!expectedGeo) continue; // entry didn't establish this frame's geometry
5670
+ if (!liveGeo || JSON.stringify(liveGeo) !== JSON.stringify(expectedGeo)) {
5671
+ stale.push(frameId);
5672
+ }
5673
+ }
5674
+ return stale;
5675
+ }
5676
+
5199
5677
  function viewportSizeFromFrameGeometry(
5200
5678
  geometry: CanvasFrameGeometry | undefined,
5201
5679
  ) {
@@ -5363,6 +5841,18 @@ export default function DesignEditor() {
5363
5841
  const [selectedElement, setSelectedElement] = useState<ElementInfo | null>(
5364
5842
  null,
5365
5843
  );
5844
+ // Vector-edit mode (P5 integration): active while the user is editing a
5845
+ // committed pen path's anchors/handles on the overview canvas. `path` is
5846
+ // the LIVE working copy (path-local coordinates, matching pen-path.ts);
5847
+ // `originCanvas` is recomputed from the owning screen's current frame
5848
+ // geometry on every render (see vectorEditOverlayState below) rather than
5849
+ // stored here, so dragging/resizing the screen frame while editing doesn't
5850
+ // leave the overlay pinned to a stale position. null when not editing.
5851
+ const [vectorEditingState, setVectorEditingState] = useState<{
5852
+ screenId: string;
5853
+ nodeId: string;
5854
+ path: PenPath;
5855
+ } | null>(null);
5366
5856
  const [pendingVisualStyleEdits, setPendingVisualStyleEdits] = useState<
5367
5857
  PendingVisualStyleEdit[]
5368
5858
  >([]);
@@ -5806,7 +6296,7 @@ export default function DesignEditor() {
5806
6296
  const setWidth =
5807
6297
  side === "left" ? setLeftSidebarWidth : setRightSidebarWidth;
5808
6298
  const minWidth = side === "left" ? (codePanelOpen ? 520 : 220) : 240;
5809
- const maxWidth = side === "left" ? (codePanelOpen ? 860 : 420) : 390;
6299
+ const maxWidth = side === "left" ? (codePanelOpen ? 1100 : 420) : 390;
5810
6300
  const target =
5811
6301
  side === "left"
5812
6302
  ? leftSidebarContentRef.current
@@ -6309,7 +6799,10 @@ export default function DesignEditor() {
6309
6799
  pendingQuestionsVisible,
6310
6800
  ]);
6311
6801
 
6312
- // Current user info for collaborative presence
6802
+ // Current user info for collaborative presence. The avatar (if the user has
6803
+ // uploaded one) is plumbed through so peers see the user's face on cursors,
6804
+ // selection rings, edit highlights, and the presence bar.
6805
+ const currentUserAvatarUrl = useAvatarUrl(session?.email);
6313
6806
  const currentUser: CollabUser | undefined = useMemo(
6314
6807
  () =>
6315
6808
  session?.email
@@ -6317,9 +6810,12 @@ export default function DesignEditor() {
6317
6810
  name: session.name?.trim() || emailToName(session.email),
6318
6811
  email: session.email,
6319
6812
  color: emailToColor(session.email),
6813
+ ...(currentUserAvatarUrl
6814
+ ? { avatarUrl: currentUserAvatarUrl }
6815
+ : {}),
6320
6816
  }
6321
6817
  : undefined,
6322
- [session?.email, session?.name],
6818
+ [session?.email, session?.name, currentUserAvatarUrl],
6323
6819
  );
6324
6820
  const signInToSaveHref = buildSignInHrefForDesignIntent("save");
6325
6821
  const signInToShareHref = buildSignInHrefForDesignIntent("share");
@@ -6997,6 +7493,14 @@ export default function DesignEditor() {
6997
7493
  () => getCanvasFrameGeometry(designDataJson),
6998
7494
  [designDataJson],
6999
7495
  );
7496
+ // Freshest live geometry for the geometry-undo freshness guard. Read from a
7497
+ // ref (not a render-time closure) so undo/redo compare against the geometry a
7498
+ // concurrent peer/agent may have just written, not the value captured when
7499
+ // handleUndo's callback was memoized.
7500
+ const liveFrameGeometryRef = useRef(canvasFrameGeometryById);
7501
+ useEffect(() => {
7502
+ liveFrameGeometryRef.current = canvasFrameGeometryById;
7503
+ }, [canvasFrameGeometryById]);
7000
7504
 
7001
7505
  // ── Board file ─────────────────────────────────────────────────────────────
7002
7506
  // The board is a reserved design_file (filename "__board__.html") whose id is
@@ -8049,6 +8553,37 @@ export default function DesignEditor() {
8049
8553
  user: currentUser,
8050
8554
  });
8051
8555
 
8556
+ // ── Overview presence (Task 1) ──────────────────────────────────────────────
8557
+ //
8558
+ // In single-screen mode the collab doc above already drives selection rings +
8559
+ // recent-edit highlights over the one active iframe. Overview (MultiScreenCanvas)
8560
+ // has no single active collab doc, so we open a SEPARATE, presence-only
8561
+ // subscription here. Design decisions:
8562
+ //
8563
+ // • Subscription model: a SINGLE subscription for the most-relevant file —
8564
+ // `activeFileId` (which in overview defaults to the selected/first screen
8565
+ // and is advanced to the worked file by the agent's view-screen / navigate
8566
+ // actions). We deliberately do NOT open one subscription per visible frame:
8567
+ // an overview board can hold many screens, and N polling docs would be
8568
+ // wasteful. Actions publish agent presence to the FILE id (see
8569
+ // edit-design.ts / apply-visual-edit.ts → agentEnterDocument(file.id)), so
8570
+ // this one doc surfaces the agent's `selection` + `recentEdits` for the file
8571
+ // it is editing.
8572
+ // • Only mounts in overview so we never run two live docs for the same file.
8573
+ const overviewPresenceFileId =
8574
+ viewMode === "overview"
8575
+ ? (activeFileId ?? overviewScreens[0]?.id ?? null)
8576
+ : null;
8577
+ const { awareness: overviewAwareness, ydoc: overviewYdoc } =
8578
+ useCollaborativeDoc({
8579
+ docId:
8580
+ isSignedIn && canEditDesign && overviewPresenceFileId
8581
+ ? overviewPresenceFileId
8582
+ : null,
8583
+ requestSource: TAB_ID,
8584
+ user: currentUser,
8585
+ });
8586
+
8052
8587
  // Track collab-sourced content for the active file.
8053
8588
  // When Y.Doc is synced and has content, use it as the source of truth
8054
8589
  // instead of the DB-fetched content so live remote edits appear instantly.
@@ -8508,6 +9043,10 @@ export default function DesignEditor() {
8508
9043
 
8509
9044
  // Canvas container ref for cursor overlay coordinate mapping.
8510
9045
  const canvasContextMenuRef = useRef<CanvasContextMenuHandle | null>(null);
9046
+ // L12: imperative handle so Cmd+R and the canvas context-menu Rename item
9047
+ // can start the layers panel's real inline rename editor on the selected
9048
+ // layer (see beginRename in LayersPanel.tsx).
9049
+ const layersPanelRef = useRef<LayersPanelHandle | null>(null);
8511
9050
  const canvasContainerRef = useRef<HTMLDivElement>(null);
8512
9051
  const activeEditorDragRef = useRef(false);
8513
9052
 
@@ -8698,6 +9237,315 @@ export default function DesignEditor() {
8698
9237
  };
8699
9238
  }, [activeFileId, zoom, setPresence]);
8700
9239
 
9240
+ // ── Remote selection rings + lingering edit highlights on the canvas ────────
9241
+ //
9242
+ // The agent (and human peers) publish selection descriptors + recentEdits into
9243
+ // awareness. Both are resolved by locating the target element INSIDE the active
9244
+ // screen's iframe, then transforming the iframe-local rect into parent-viewport
9245
+ // coordinates. The iframe sits inside a `transform: scale(zoom)` wrapper, so its
9246
+ // on-screen size differs from its internal layout size; we derive the scale from
9247
+ // `boundingRect.width / iframe.clientWidth` (robust to any wrapper transform)
9248
+ // instead of reading the zoom value directly. Cross-origin/unmounted iframes
9249
+ // return null (the ring/highlight is silently skipped).
9250
+
9251
+ // Map an iframe-local DOMRect (element or Range) to the parent viewport.
9252
+ const mapIframeRectToViewport = useCallback(
9253
+ (iframe: HTMLIFrameElement, rect: DOMRect): DOMRect | null => {
9254
+ const frameRect = iframe.getBoundingClientRect();
9255
+ if (frameRect.width === 0 || frameRect.height === 0) return null;
9256
+ // Effective on-screen scale of the iframe's internal coordinate system.
9257
+ const layoutWidth = iframe.clientWidth || frameRect.width;
9258
+ const layoutHeight = iframe.clientHeight || frameRect.height;
9259
+ const scaleX = layoutWidth ? frameRect.width / layoutWidth : 1;
9260
+ const scaleY = layoutHeight ? frameRect.height / layoutHeight : 1;
9261
+ return new DOMRect(
9262
+ frameRect.left + rect.left * scaleX,
9263
+ frameRect.top + rect.top * scaleY,
9264
+ rect.width * scaleX,
9265
+ rect.height * scaleY,
9266
+ );
9267
+ },
9268
+ [],
9269
+ );
9270
+
9271
+ // Resolve a CSS selector to a viewport rect inside a specific iframe. The
9272
+ // iframe is the coordinate anchor, so the same logic serves both the
9273
+ // single-screen preview iframe and each overview frame iframe (see the
9274
+ // overview resolvers below, which pass the frame the agent is editing).
9275
+ const resolveSelectorRectInIframe = useCallback(
9276
+ (iframe: HTMLIFrameElement | null, selector: string): DOMRect | null => {
9277
+ if (!iframe) return null;
9278
+ let doc: Document | null = null;
9279
+ try {
9280
+ doc = iframe.contentDocument;
9281
+ } catch {
9282
+ return null; // cross-origin — cannot inspect
9283
+ }
9284
+ if (!doc) return null;
9285
+ let el: Element | null = null;
9286
+ try {
9287
+ el = doc.querySelector(selector);
9288
+ } catch {
9289
+ return null; // invalid selector
9290
+ }
9291
+ if (!el) return null;
9292
+ return mapIframeRectToViewport(iframe, el.getBoundingClientRect());
9293
+ },
9294
+ [mapIframeRectToViewport],
9295
+ );
9296
+
9297
+ // Resolve a text quote to a viewport rect by walking a specific iframe body.
9298
+ const resolveTextQuoteRectInIframe = useCallback(
9299
+ (iframe: HTMLIFrameElement | null, quote: string): DOMRect | null => {
9300
+ const needle = quote.trim();
9301
+ if (!needle) return null;
9302
+ if (!iframe) return null;
9303
+ let doc: Document | null = null;
9304
+ try {
9305
+ doc = iframe.contentDocument;
9306
+ } catch {
9307
+ return null;
9308
+ }
9309
+ if (!doc?.body) return null;
9310
+ const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT);
9311
+ let node: Node | null = walker.nextNode();
9312
+ while (node) {
9313
+ const text = node.nodeValue ?? "";
9314
+ const idx = text.indexOf(needle);
9315
+ if (idx !== -1 && node.parentElement) {
9316
+ try {
9317
+ const range = doc.createRange();
9318
+ range.setStart(node, idx);
9319
+ range.setEnd(node, idx + needle.length);
9320
+ const rect = range.getBoundingClientRect();
9321
+ if (rect.width > 0 || rect.height > 0) {
9322
+ return mapIframeRectToViewport(iframe, rect);
9323
+ }
9324
+ } catch {
9325
+ // fall back to the parent element rect below
9326
+ }
9327
+ return mapIframeRectToViewport(
9328
+ iframe,
9329
+ node.parentElement.getBoundingClientRect(),
9330
+ );
9331
+ }
9332
+ node = walker.nextNode();
9333
+ }
9334
+ return null;
9335
+ },
9336
+ [mapIframeRectToViewport],
9337
+ );
9338
+
9339
+ // Resolve a CSS selector to a viewport rect inside the active screen iframe.
9340
+ const resolveSelectorRect = useCallback(
9341
+ (selector: string): DOMRect | null =>
9342
+ resolveSelectorRectInIframe(canvasIframeRef.current, selector),
9343
+ [canvasIframeRef, resolveSelectorRectInIframe],
9344
+ );
9345
+
9346
+ // Resolve a text quote to a viewport rect by walking the iframe body's text.
9347
+ const resolveTextQuoteRect = useCallback(
9348
+ (quote: string): DOMRect | null =>
9349
+ resolveTextQuoteRectInIframe(canvasIframeRef.current, quote),
9350
+ [canvasIframeRef, resolveTextQuoteRectInIframe],
9351
+ );
9352
+
9353
+ // resolveRect for RemoteSelectionRings — descriptor is a CSS selector string.
9354
+ const resolveSelectionRect = useCallback(
9355
+ (descriptor: string): DOMRect | null => resolveSelectorRect(descriptor),
9356
+ [resolveSelectorRect],
9357
+ );
9358
+
9359
+ // resolveRect for RecentEditHighlights — dispatch on descriptor kind.
9360
+ const resolveRecentEditRect = useCallback(
9361
+ (edit: AttributedRecentEdit): DOMRect | null => {
9362
+ const d = edit.descriptor;
9363
+ if (d.kind === "selector" && typeof d.selector === "string") {
9364
+ return resolveSelectorRect(d.selector);
9365
+ }
9366
+ if (d.kind === "text" && typeof d.quote === "string") {
9367
+ return resolveTextQuoteRect(d.quote);
9368
+ }
9369
+ // "paths" and whole-"doc" descriptors have no canvas region here.
9370
+ return null;
9371
+ },
9372
+ [resolveSelectorRect, resolveTextQuoteRect],
9373
+ );
9374
+
9375
+ const recentEdits = useRecentEdits(others);
9376
+
9377
+ // Re-key the presence array on zoom so the overlays recompute their rects when
9378
+ // the canvas zooms (the container itself doesn't resize, so the overlays'
9379
+ // ResizeObserver wouldn't otherwise fire). Cheap: a new array reference only.
9380
+ const othersForOverlays = useMemo<OtherPresence[]>(
9381
+ () => others.slice(),
9382
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9383
+ [others, zoom],
9384
+ );
9385
+ const recentEditsForOverlays = useMemo<AttributedRecentEdit[]>(
9386
+ () => recentEdits.slice(),
9387
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9388
+ [recentEdits, zoom],
9389
+ );
9390
+
9391
+ // ── Synthesized AI cursor (Task 3) ──────────────────────────────────────────
9392
+ //
9393
+ // The agent publishes a selection/recentEdit but no cursor. Derive a moving
9394
+ // cursor for it from whatever region currently resolves, normalized against
9395
+ // the canvas container the same way human cursors are (see
9396
+ // handleCanvasPointerMove) so LiveCursorOverlay places it correctly. The 120ms
9397
+ // CSS transition in the overlay animates it smoothly between edit targets.
9398
+ const othersWithAgentCursor = useMemo<OtherPresence[]>(() => {
9399
+ const container = canvasContainerRef.current;
9400
+ if (!container) return othersForOverlays;
9401
+ const containerRect = container.getBoundingClientRect();
9402
+ if (containerRect.width === 0 || containerRect.height === 0) {
9403
+ return othersForOverlays;
9404
+ }
9405
+ return othersForOverlays.map((other) => {
9406
+ // Only synthesize when the agent has no real cursor of its own.
9407
+ if (!other.isAgent || other.presence.cursor) return other;
9408
+ // Prefer the agent's active selection; fall back to its latest recentEdit.
9409
+ let rect: DOMRect | null = null;
9410
+ const selection = other.presence.selection as
9411
+ | string
9412
+ | { selector?: string }
9413
+ | null
9414
+ | undefined;
9415
+ const selector =
9416
+ typeof selection === "string" ? selection : selection?.selector;
9417
+ if (selector) rect = resolveSelectorRect(selector);
9418
+ if (!rect) {
9419
+ const ring = other.presence.recentEdits;
9420
+ if (Array.isArray(ring)) {
9421
+ for (let i = ring.length - 1; i >= 0 && !rect; i--) {
9422
+ const entry = ring[i] as AttributedRecentEdit;
9423
+ if (entry?.descriptor) {
9424
+ rect = resolveRecentEditRect({
9425
+ ...entry,
9426
+ clientId: other.clientId,
9427
+ user: other.user,
9428
+ isAgent: true,
9429
+ });
9430
+ }
9431
+ }
9432
+ }
9433
+ }
9434
+ if (!rect) return other;
9435
+ // Normalize the rect's center to container fractions (matches human cursors).
9436
+ const cx = rect.left + rect.width / 2 - containerRect.left;
9437
+ const cy = rect.top + rect.height / 2 - containerRect.top;
9438
+ return {
9439
+ ...other,
9440
+ presence: {
9441
+ ...other.presence,
9442
+ cursor: {
9443
+ x: Math.max(0, Math.min(1, cx / containerRect.width)),
9444
+ y: Math.max(0, Math.min(1, cy / containerRect.height)),
9445
+ },
9446
+ },
9447
+ };
9448
+ });
9449
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9450
+ }, [
9451
+ othersForOverlays,
9452
+ resolveSelectorRect,
9453
+ resolveRecentEditRect,
9454
+ canvasContainerRef,
9455
+ zoom,
9456
+ ]);
9457
+
9458
+ // ── Overview presence overlays (Task 1) ─────────────────────────────────────
9459
+ //
9460
+ // Element-level rings + recent-edit highlights over the overview board. The
9461
+ // agent's descriptors are published on the overview presence doc above and
9462
+ // resolved INSIDE the frame the agent is editing. Because we resolve against
9463
+ // the frame iframe's `getBoundingClientRect()` (via mapIframeRectToViewport),
9464
+ // the board pan/zoom transform is already baked into the returned viewport
9465
+ // coords — RemoteSelectionRings / RecentEditHighlights convert those to
9466
+ // container-relative against the same `canvasContainerRef`, so we do NOT
9467
+ // duplicate MultiScreenCanvas's transform math.
9468
+ const { others: overviewOthers } = usePresence(
9469
+ overviewAwareness,
9470
+ overviewYdoc?.clientID ?? null,
9471
+ );
9472
+
9473
+ // Find the overview frame iframe for a given file id. Frames tag their iframe
9474
+ // with `data-screen-iframe-id` = screen id (or `<id>::bp-<width>` for an
9475
+ // active breakpoint), so match the exact id or the breakpoint-prefixed form.
9476
+ const getOverviewFrameIframe = useCallback(
9477
+ (fileId: string | null): HTMLIFrameElement | null => {
9478
+ if (!fileId) return null;
9479
+ const container = canvasContainerRef.current;
9480
+ if (!container) return null;
9481
+ const escaped =
9482
+ typeof CSS !== "undefined" && CSS.escape ? CSS.escape(fileId) : fileId;
9483
+ return (
9484
+ container.querySelector<HTMLIFrameElement>(
9485
+ `iframe[data-screen-iframe-id="${escaped}"]`,
9486
+ ) ??
9487
+ container.querySelector<HTMLIFrameElement>(
9488
+ `iframe[data-screen-iframe-id^="${escaped}::bp-"]`,
9489
+ ) ??
9490
+ null
9491
+ );
9492
+ },
9493
+ [canvasContainerRef],
9494
+ );
9495
+
9496
+ // Overview resolvers: the agent's presence rides on `overviewPresenceFileId`,
9497
+ // so resolve every descriptor inside that file's frame iframe.
9498
+ const resolveOverviewSelectionRect = useCallback(
9499
+ (descriptor: string): DOMRect | null =>
9500
+ resolveSelectorRectInIframe(
9501
+ getOverviewFrameIframe(overviewPresenceFileId),
9502
+ descriptor,
9503
+ ),
9504
+ [
9505
+ getOverviewFrameIframe,
9506
+ overviewPresenceFileId,
9507
+ resolveSelectorRectInIframe,
9508
+ ],
9509
+ );
9510
+ const resolveOverviewRecentEditRect = useCallback(
9511
+ (edit: AttributedRecentEdit): DOMRect | null => {
9512
+ const iframe = getOverviewFrameIframe(overviewPresenceFileId);
9513
+ if (!iframe) return null;
9514
+ const d = edit.descriptor;
9515
+ if (d.kind === "selector" && typeof d.selector === "string") {
9516
+ return resolveSelectorRectInIframe(iframe, d.selector);
9517
+ }
9518
+ if (d.kind === "text" && typeof d.quote === "string") {
9519
+ return resolveTextQuoteRectInIframe(iframe, d.quote);
9520
+ }
9521
+ // "paths" and whole-"doc" descriptors have no single canvas region.
9522
+ return null;
9523
+ },
9524
+ [
9525
+ getOverviewFrameIframe,
9526
+ overviewPresenceFileId,
9527
+ resolveSelectorRectInIframe,
9528
+ resolveTextQuoteRectInIframe,
9529
+ ],
9530
+ );
9531
+
9532
+ // Show only the agent's presence in overview (human peers edit inside their
9533
+ // own focused screen; the board treatment is about surfacing agent work). Re-
9534
+ // key on the overview zoom so rings recompute when the board zooms — the
9535
+ // container itself doesn't resize, so the overlays' ResizeObserver wouldn't
9536
+ // otherwise fire (same trick as the single-screen `othersForOverlays`).
9537
+ const overviewAgentOthers = useMemo<OtherPresence[]>(
9538
+ () => overviewOthers.filter((o) => o.isAgent),
9539
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9540
+ [overviewOthers, overviewCanvasZoom],
9541
+ );
9542
+ const overviewRecentEdits = useRecentEdits(overviewAgentOthers);
9543
+ const overviewRecentEditsForOverlays = useMemo<AttributedRecentEdit[]>(
9544
+ () => overviewRecentEdits.slice(),
9545
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9546
+ [overviewRecentEdits, overviewCanvasZoom],
9547
+ );
9548
+
8701
9549
  // Follow mode — clicking an avatar in the toolbar follows that participant.
8702
9550
  const [followingEmail, setFollowingEmail] = useState<string | null>(null);
8703
9551
  const followingId = useMemo(() => {
@@ -9121,19 +9969,42 @@ export default function DesignEditor() {
9121
9969
  const activeCanvasSourceType =
9122
9970
  normalizeDesignSourceType(activeOverviewScreen?.sourceType) ??
9123
9971
  designSourceType;
9972
+ // P4: arms DesignCanvas's single-screen click-to-place overlay only while
9973
+ // focused on a single screen with an active creation tool selected —
9974
+ // `null` in every other case leaves the overlay unmounted (see
9975
+ // getSingleScreenCreationTool's doc comment for the full tool mapping).
9976
+ const activeSingleScreenCreationTool = getSingleScreenCreationTool({
9977
+ activeTool,
9978
+ viewMode,
9979
+ hasActiveFile: Boolean(activeFile),
9980
+ });
9124
9981
  const sourceCapabilities = useMemo(() => {
9125
9982
  const caps = resolveSourceCapabilities(designSourceType);
9126
9983
  return DESIGN_CAPABILITY_NAMES.filter((name) => hasCapability(caps, name));
9127
9984
  }, [designSourceType]);
9128
9985
 
9129
- // Builder-hosted preview URL for fusion-source designs, written into the
9130
- // design data blob by the "Make it real" migration. Threaded into DesignCanvas
9131
- // so the fusion preview renders (and so the bridge trust check can validate
9132
- // the frame's origin against it).
9986
+ // Full-app-building linkage (see shared/full-app.ts). Non-null only for
9987
+ // designs created via the "Full app" creation mode drives FusionAppBanner
9988
+ // and the fusion preview URL fallback below. Flag-independent on read: once
9989
+ // a design has this data (created while the flag was on), the banner and
9990
+ // canvas wiring keep working even if the flag is later flipped off, so
9991
+ // existing full-app designs never regress.
9992
+ const fusionApp = useMemo(
9993
+ () => readFusionApp(designDataJson),
9994
+ [designDataJson],
9995
+ );
9996
+
9997
+ // Builder-hosted preview URL for fusion-source designs. Prefers the flat
9998
+ // `fusionUrl` written by the "Make it real" migration; falls back to the
9999
+ // full-app-building `fusionApp.previewUrl` linkage so container dev-server
10000
+ // screens render the same way once the container reports a preview URL.
10001
+ // Threaded into DesignCanvas so the fusion preview renders (and so the
10002
+ // bridge trust check can validate the frame's origin against it).
9133
10003
  const designFusionUrl = useMemo(() => {
9134
10004
  const raw = (designDataJson as { fusionUrl?: unknown }).fusionUrl;
9135
- return typeof raw === "string" && raw ? raw : undefined;
9136
- }, [designDataJson]);
10005
+ if (typeof raw === "string" && raw) return raw;
10006
+ return fusionApp?.previewUrl;
10007
+ }, [designDataJson, fusionApp]);
9137
10008
 
9138
10009
  // §6.1 — open a component instance's source. open-component-source selects the
9139
10010
  // component root in the editor and emits a navigate app-state; for real-app
@@ -10137,13 +11008,38 @@ export default function DesignEditor() {
10137
11008
  : storedContent;
10138
11009
  })()
10139
11010
  : storedContent);
10140
- const nextContent = appendCanvasPrimitiveToHtml(baseContent, primitive, {
10141
- preserveNegativePosition: targetFile.id === boardFileId,
10142
- });
10143
- if (!nextContent) {
11011
+ const insertedContent = appendCanvasPrimitiveToHtml(
11012
+ baseContent,
11013
+ primitive,
11014
+ { preserveNegativePosition: targetFile.id === boardFileId },
11015
+ );
11016
+ if (!insertedContent) {
10144
11017
  toast.error(t("designEditor.toasts.primitiveInsertFailed"));
10145
11018
  return false;
10146
11019
  }
11020
+ // Vector-edit foundations: stash the structured pen path (nodes +
11021
+ // handles) alongside the flattened `d` so a later double-click/Enter
11022
+ // can re-hydrate it into an editable path instead of only having the
11023
+ // already-flattened curve. `primitive.pathData` is the only carrier of
11024
+ // pen geometry that crosses the MultiScreenCanvas -> DesignEditor
11025
+ // boundary for an OVERVIEW-drawn pen path (see
11026
+ // parsePenPathFromSerializedD's doc comment for why this reconstructs
11027
+ // rather than receives the structured path directly).
11028
+ const nextContent =
11029
+ primitive.kind === "path" && primitive.pathData && primitive.nodeId
11030
+ ? (() => {
11031
+ const reconstructed = parsePenPathFromSerializedD(
11032
+ primitive.pathData!,
11033
+ );
11034
+ return reconstructed
11035
+ ? setPenNodesAttributeOnElement(
11036
+ insertedContent,
11037
+ primitive.nodeId!,
11038
+ reconstructed,
11039
+ )
11040
+ : insertedContent;
11041
+ })()
11042
+ : insertedContent;
10147
11043
  const projectedNodeId = primitive.nodeId
10148
11044
  ? buildCodeLayerProjection(nextContent).nodes.find(
10149
11045
  (node) =>
@@ -10353,6 +11249,109 @@ export default function DesignEditor() {
10353
11249
  [boardFileId, canEditDesign, handleCreatePrimitive, handlePrimitiveCreated],
10354
11250
  );
10355
11251
 
11252
+ /**
11253
+ * P4: DesignCanvas's single-screen click-to-place creation overlay
11254
+ * (`activeCreationTool`/`onCreatePrimitive` below) commits through this
11255
+ * same `handleCreatePrimitive`/`handlePrimitiveCreated` pair overview
11256
+ * drawing already uses — `createPrimitiveInsertFromSpec` just translates
11257
+ * the overlay's screen-content-space spec into the shared
11258
+ * `CanvasPrimitiveInsert` shape first. Always returns the active tool to
11259
+ * "move" afterward (Figma/overview parity) since single-screen pen never
11260
+ * has a multi-click "mid-path" state to preserve (see
11261
+ * createPrimitiveInsertFromSpec's pen doc comment).
11262
+ */
11263
+ const handleSingleScreenCreatePrimitive = useCallback(
11264
+ (spec: CreatePrimitiveSpec) => {
11265
+ if (!activeFile || !canEditDesign) return;
11266
+ const nodeId = uniqueLayerId(spec.tool === "pen" ? "path" : spec.tool);
11267
+ const primitive = createPrimitiveInsertFromSpec(spec, nodeId);
11268
+ if (!primitive) return;
11269
+ const result = handleCreatePrimitive(activeFile.id, primitive);
11270
+ if (!result) return;
11271
+ const resultNodeId = typeof result === "string" ? result : nodeId;
11272
+ handlePrimitiveCreated(activeFile.id, resultNodeId, {
11273
+ selectFrame: false,
11274
+ });
11275
+ },
11276
+ [activeFile, canEditDesign, handleCreatePrimitive, handlePrimitiveCreated],
11277
+ );
11278
+
11279
+ /**
11280
+ * P5/vector-edit: commits the working PenPath from MultiScreenCanvas's
11281
+ * `vectorEdit` overlay back onto its owning screen. `"preview"` phases
11282
+ * (live anchor/handle drag) only update `vectorEditingState.path` — the
11283
+ * overlay renders straight off that in-memory path, so nothing needs to
11284
+ * touch the iframe/content until the gesture actually settles. `"commit"`
11285
+ * phases (mirroring the same preview/commit gesture-signal convention as
11286
+ * ScrubInput/DesignColorPicker's onChangeComplete) additionally write the
11287
+ * new `d` + data-an-pen-nodes back into the owning screen's HTML through
11288
+ * the existing applyFileContentUpdate persistence path, so a single mouse
11289
+ * drag becomes exactly one saved edit (and one undo/history entry) rather
11290
+ * than one per intermediate frame.
11291
+ */
11292
+ const handleVectorEditChange = useCallback(
11293
+ (nextPath: PenPath, phase: "preview" | "commit") => {
11294
+ setVectorEditingState((current) => {
11295
+ if (!current) return current;
11296
+ if (phase === "commit") {
11297
+ const baseContent = getScreenContent(current.screenId);
11298
+ if (baseContent) {
11299
+ const nextContent = writeBackVectorEditedPenPath(
11300
+ baseContent,
11301
+ current.nodeId,
11302
+ nextPath,
11303
+ );
11304
+ if (nextContent !== baseContent) {
11305
+ applyFileContentUpdate(current.screenId, nextContent, {
11306
+ skipPreview: current.screenId !== activeFile?.id,
11307
+ });
11308
+ }
11309
+ }
11310
+ }
11311
+ return { ...current, path: nextPath };
11312
+ });
11313
+ },
11314
+ [activeFile?.id, applyFileContentUpdate, getScreenContent],
11315
+ );
11316
+
11317
+ const handleVectorEditExit = useCallback(() => {
11318
+ setVectorEditingState(null);
11319
+ }, []);
11320
+
11321
+ /**
11322
+ * P5/vector-edit: the VectorEditOverlayState prop threaded to
11323
+ * MultiScreenCanvas. `originCanvas` is recomputed on every render from the
11324
+ * owning screen's CURRENT frame geometry (see getScreenFrameOriginCanvas)
11325
+ * rather than cached in vectorEditingState, so dragging/resizing the
11326
+ * screen frame mid-edit can't leave the overlay pinned to a stale
11327
+ * position. `null` whenever not editing or the origin can't be resolved
11328
+ * (e.g. the owning screen was deleted mid-edit) — MultiScreenCanvas
11329
+ * doesn't render the overlay in that case.
11330
+ */
11331
+ const vectorEditOverlayState = useMemo<VectorEditOverlayState | null>(() => {
11332
+ if (!vectorEditingState) return null;
11333
+ const originCanvas = getScreenFrameOriginCanvas({
11334
+ screenId: vectorEditingState.screenId,
11335
+ overviewScreens,
11336
+ canvasFrameGeometryById,
11337
+ boardFileId,
11338
+ });
11339
+ if (!originCanvas) return null;
11340
+ return {
11341
+ path: vectorEditingState.path,
11342
+ originCanvas,
11343
+ onChange: handleVectorEditChange,
11344
+ onExit: handleVectorEditExit,
11345
+ };
11346
+ }, [
11347
+ boardFileId,
11348
+ canvasFrameGeometryById,
11349
+ handleVectorEditChange,
11350
+ handleVectorEditExit,
11351
+ overviewScreens,
11352
+ vectorEditingState,
11353
+ ]);
11354
+
10356
11355
  const handleOverviewScreenSelectionChange = useCallback(
10357
11356
  (ids: string[]) => {
10358
11357
  const pendingId = pendingOverviewScreenSelectionRef.current;
@@ -10410,11 +11409,24 @@ export default function DesignEditor() {
10410
11409
  });
10411
11410
  }, [canEditDesign]);
10412
11411
 
11412
+ // T14/P4: text/shape/pen tools used to always force a jump to overview —
11413
+ // that's still correct when the user is already IN overview (or has no
11414
+ // screen focused yet), but when a single screen is already focused these
11415
+ // tools should arm single-screen click-to-place instead of yanking the
11416
+ // user out to overview to draw. Overview mode itself is left completely
11417
+ // alone below (same flushSync/setViewMode("overview") as before).
10413
11418
  const handleTextTool = useCallback(() => {
10414
11419
  if (!canEditDesign) return;
10415
11420
  blurActiveDesignEditableTarget();
10416
11421
  flushSync(() => {
10417
11422
  setActiveTool("text");
11423
+ if (viewModeRef.current === "single" && activeFile) {
11424
+ setMode("edit");
11425
+ setDrawMode(false);
11426
+ setPinMode(false);
11427
+ setSelectedElement(null);
11428
+ return;
11429
+ }
10418
11430
  viewModeRef.current = "overview";
10419
11431
  setViewMode("overview");
10420
11432
  setMode("edit");
@@ -10422,7 +11434,7 @@ export default function DesignEditor() {
10422
11434
  setPinMode(false);
10423
11435
  setSelectedElement(null);
10424
11436
  });
10425
- }, [canEditDesign]);
11437
+ }, [activeFile, canEditDesign]);
10426
11438
 
10427
11439
  const handleShapeTool = useCallback(
10428
11440
  (tool: ShapeTool) => {
@@ -10430,6 +11442,13 @@ export default function DesignEditor() {
10430
11442
  blurActiveDesignEditableTarget();
10431
11443
  flushSync(() => {
10432
11444
  setActiveTool(tool);
11445
+ if (viewModeRef.current === "single" && activeFile) {
11446
+ setMode("edit");
11447
+ setDrawMode(false);
11448
+ setPinMode(false);
11449
+ setSelectedElement(null);
11450
+ return;
11451
+ }
10433
11452
  viewModeRef.current = "overview";
10434
11453
  setViewMode("overview");
10435
11454
  setMode("edit");
@@ -10438,7 +11457,7 @@ export default function DesignEditor() {
10438
11457
  setSelectedElement(null);
10439
11458
  });
10440
11459
  },
10441
- [canEditDesign],
11460
+ [activeFile, canEditDesign],
10442
11461
  );
10443
11462
 
10444
11463
  const handleRectTool = useCallback(() => {
@@ -10474,6 +11493,13 @@ export default function DesignEditor() {
10474
11493
  blurActiveDesignEditableTarget();
10475
11494
  flushSync(() => {
10476
11495
  setActiveTool("pen");
11496
+ if (viewModeRef.current === "single" && activeFile) {
11497
+ setMode("edit");
11498
+ setDrawMode(false);
11499
+ setPinMode(false);
11500
+ setSelectedElement(null);
11501
+ return;
11502
+ }
10477
11503
  viewModeRef.current = "overview";
10478
11504
  setViewMode("overview");
10479
11505
  setMode("edit");
@@ -10481,7 +11507,7 @@ export default function DesignEditor() {
10481
11507
  setPinMode(false);
10482
11508
  setSelectedElement(null);
10483
11509
  });
10484
- }, [canEditDesign]);
11510
+ }, [activeFile, canEditDesign]);
10485
11511
 
10486
11512
  const handleHandTool = useCallback(() => {
10487
11513
  if (!canEditDesign) return;
@@ -11721,7 +12747,7 @@ export default function DesignEditor() {
11721
12747
  );
11722
12748
 
11723
12749
  const handleStyleChange = useCallback(
11724
- (property: string, value: string) => {
12750
+ (property: string, value: string, meta?: StyleChangeMeta) => {
11725
12751
  const selector = selectedElement?.selector ?? "body";
11726
12752
  if (
11727
12753
  textEditingState.active &&
@@ -11737,6 +12763,36 @@ export default function DesignEditor() {
11737
12763
  return;
11738
12764
  }
11739
12765
  }
12766
+ // PF12: a mid-gesture scrub/color-drag preview tick (ScrubInput's
12767
+ // `phase: "preview"`, DesignColorPicker's per-tick `onChange`) is cheap
12768
+ // to show live but must NOT run the expensive source commit
12769
+ // (projection parse + HTML patch + history entry) on every tick — only
12770
+ // the gesture's final "commit" (or a caller that never passes meta at
12771
+ // all, e.g. keyboard/agent edits) does that. Route preview ticks
12772
+ // through the same cheap iframe postMessage path the text-range case
12773
+ // above already uses, and skip commitVisualStyles entirely so there is
12774
+ // no source write — and therefore no history entry — for any preview
12775
+ // tick. Multi-layer-selection commits (commitStylesToSelectedLayers)
12776
+ // have no equivalent cheap multi-element preview channel, so previews
12777
+ // for that case conservatively fall through to the existing full-commit
12778
+ // behavior below (unchanged from before PF12).
12779
+ if (
12780
+ shouldSkipVisualStyleCommitForPreview({
12781
+ phase: meta?.phase,
12782
+ selectedLayerCount: selectedLayerTargetsRef.current.length,
12783
+ })
12784
+ ) {
12785
+ const sendStyleChange = (window as any).__designCanvasSendStyle;
12786
+ if (typeof sendStyleChange === "function") {
12787
+ sendStyleChange(selector, property, value, {
12788
+ selectorCandidates: selectedCanvasSelectorCandidates,
12789
+ nodeId: selectedElement?.sourceId,
12790
+ });
12791
+ }
12792
+ // No live bridge available for this preview tick (e.g. inactive
12793
+ // screen) — nothing cheap to do; wait for the gesture's "commit".
12794
+ return;
12795
+ }
11740
12796
  if (commitStylesToSelectedLayers({ [property]: value })) return;
11741
12797
  commitVisualStyles(selector, { [property]: value });
11742
12798
  },
@@ -11768,7 +12824,7 @@ export default function DesignEditor() {
11768
12824
  );
11769
12825
 
11770
12826
  const handleStylesChange = useCallback(
11771
- (styles: Record<string, string>) => {
12827
+ (styles: Record<string, string>, meta?: StyleChangeMeta) => {
11772
12828
  const selector = selectedElement?.selector ?? "body";
11773
12829
  const entries = Object.entries(styles).filter(([, value]) =>
11774
12830
  Boolean(value),
@@ -11797,6 +12853,28 @@ export default function DesignEditor() {
11797
12853
  return;
11798
12854
  }
11799
12855
  }
12856
+ // PF12: same preview/commit split as handleStyleChange — see its
12857
+ // comment for the full undo-safety rationale. A batched multi-property
12858
+ // preview tick (e.g. EditPanel's shadow X/Y/blur/spread popover) is
12859
+ // still just a live preview: send every property to the cheap iframe
12860
+ // bridge and skip the expensive multi-property commitVisualStyles call.
12861
+ if (
12862
+ shouldSkipVisualStyleCommitForPreview({
12863
+ phase: meta?.phase,
12864
+ selectedLayerCount: selectedLayerTargetsRef.current.length,
12865
+ })
12866
+ ) {
12867
+ const sendStyleChange = (window as any).__designCanvasSendStyle;
12868
+ if (typeof sendStyleChange === "function") {
12869
+ entries.forEach(([property, value]) => {
12870
+ sendStyleChange(selector, property, value, {
12871
+ selectorCandidates: selectedCanvasSelectorCandidates,
12872
+ nodeId: selectedElement?.sourceId,
12873
+ });
12874
+ });
12875
+ }
12876
+ return;
12877
+ }
11800
12878
  if (commitStylesToSelectedLayers(Object.fromEntries(entries))) return;
11801
12879
  commitVisualStyles(selector, Object.fromEntries(entries));
11802
12880
  },
@@ -14823,6 +15901,24 @@ export default function DesignEditor() {
14823
15901
  if (!canUseOverviewHistory) return false;
14824
15902
  const entry = geometryUndoStackRef.current.pop();
14825
15903
  if (!entry) return false;
15904
+ // Freshness guard: this entry last wrote `entry.after`. If a peer/agent
15905
+ // has since moved any of the frames it touched, replaying `entry.before`
15906
+ // would silently clobber their change — drop this entry instead. The pop
15907
+ // above already removed it, so undo skips forward to the next entry.
15908
+ const stale = staleGeometryFrameIds(
15909
+ entry,
15910
+ liveFrameGeometryRef.current,
15911
+ entry.after,
15912
+ );
15913
+ if (stale.length > 0) {
15914
+ console.debug(
15915
+ "[design] skipping stale geometry undo; frames changed since capture:",
15916
+ stale,
15917
+ );
15918
+ toast.info(t("designEditor.toasts.undoSkippedConcurrentEdit"));
15919
+ // Try the next undo entry rather than swallowing the whole gesture.
15920
+ return undoGeometry();
15921
+ }
14826
15922
  geometryRedoStackRef.current = [
14827
15923
  ...geometryRedoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
14828
15924
  entry,
@@ -14929,6 +16025,7 @@ export default function DesignEditor() {
14929
16025
  syncUndoRedoState,
14930
16026
  updateLiveScreenSnapshotContent,
14931
16027
  writeFrameGeometrySnapshot,
16028
+ t,
14932
16029
  ]);
14933
16030
 
14934
16031
  const handleRedo = useCallback(() => {
@@ -15111,6 +16208,22 @@ export default function DesignEditor() {
15111
16208
  if (!canUseOverviewHistory) return false;
15112
16209
  const entry = geometryRedoStackRef.current.pop();
15113
16210
  if (!entry) return false;
16211
+ // Freshness guard: when this entry was undone it wrote `entry.before`. If
16212
+ // a peer/agent has since moved any touched frame, replaying `entry.after`
16213
+ // would clobber that change — drop this entry and try the next redo.
16214
+ const stale = staleGeometryFrameIds(
16215
+ entry,
16216
+ liveFrameGeometryRef.current,
16217
+ entry.before,
16218
+ );
16219
+ if (stale.length > 0) {
16220
+ console.debug(
16221
+ "[design] skipping stale geometry redo; frames changed since capture:",
16222
+ stale,
16223
+ );
16224
+ toast.info(t("designEditor.toasts.redoSkippedConcurrentEdit"));
16225
+ return redoGeometry();
16226
+ }
15114
16227
  geometryUndoStackRef.current = [
15115
16228
  ...geometryUndoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
15116
16229
  entry,
@@ -15240,6 +16353,7 @@ export default function DesignEditor() {
15240
16353
  t,
15241
16354
  updateLiveScreenSnapshotContent,
15242
16355
  writeFrameGeometrySnapshot,
16356
+ t,
15243
16357
  ]);
15244
16358
 
15245
16359
  const handleZoomIn = useCallback(() => {
@@ -15355,6 +16469,12 @@ export default function DesignEditor() {
15355
16469
  pendingOverviewLayerSelectionRef.current = null;
15356
16470
  clearPendingOverviewLayerSelectionTimer();
15357
16471
  setCreatedOverviewLayerSelection(null);
16472
+ // P5/vector-edit: MultiScreenCanvas (the only place the vectorEdit
16473
+ // overlay renders) unmounts on leaving overview, so an active
16474
+ // vector-edit session has nothing left to render into — clear it
16475
+ // rather than leaving a stale/orphaned session in memory that would
16476
+ // resurface if the user returns to overview later.
16477
+ setVectorEditingState(null);
15358
16478
  runEditorViewTransition(() => {
15359
16479
  if (fileId) setActiveFileId(fileId);
15360
16480
  setDrawMode(false);
@@ -15597,6 +16717,36 @@ export default function DesignEditor() {
15597
16717
  ]),
15598
16718
  [],
15599
16719
  );
16720
+ /**
16721
+ * P5/vector-edit entry point. Only reachable while `viewMode === "overview"`
16722
+ * — MultiScreenCanvas's `vectorEdit` overlay (the only place this state is
16723
+ * rendered) doesn't exist in single-screen mode, so entering vector edit
16724
+ * from a single-screen selection is deliberately deferred rather than
16725
+ * silently doing something broken; see FINAL REPORT for the exact
16726
+ * deferral. Returns true when vector edit mode was entered (caller should
16727
+ * not also drill into the screen), false otherwise (not a pen path, or its
16728
+ * origin couldn't be resolved).
16729
+ */
16730
+ const enterVectorEditForSelection = useCallback(
16731
+ (owner: { fileId: string; node: CodeLayerNode }): boolean => {
16732
+ const penNodesAttr = owner.node.dataAttributes["data-an-pen-nodes"];
16733
+ if (!penNodesAttr) return false;
16734
+ const path = parsePenNodes(penNodesAttr);
16735
+ if (!path) return false;
16736
+ const originCanvas = getScreenFrameOriginCanvas({
16737
+ screenId: owner.fileId,
16738
+ overviewScreens,
16739
+ canvasFrameGeometryById,
16740
+ boardFileId,
16741
+ });
16742
+ if (!originCanvas) return false;
16743
+ const nodeId =
16744
+ owner.node.dataAttributes["data-agent-native-node-id"] ?? owner.node.id;
16745
+ setVectorEditingState({ screenId: owner.fileId, nodeId, path });
16746
+ return true;
16747
+ },
16748
+ [boardFileId, canvasFrameGeometryById, overviewScreens],
16749
+ );
15600
16750
  const handleEnterHotkey = useCallback(() => {
15601
16751
  if (viewMode !== "overview") {
15602
16752
  if (selectedLayerIdsState.length === 1) {
@@ -15616,6 +16766,20 @@ export default function DesignEditor() {
15616
16766
  }
15617
16767
  return;
15618
16768
  }
16769
+ // P5/vector-edit: Enter on a single selected committed pen path (has
16770
+ // data-an-pen-nodes — see setPenNodesAttributeOnElement) enters vector
16771
+ // edit mode instead of drilling into the owning screen. Vector edit's
16772
+ // overlay only exists on MultiScreenCanvas (overview), so this
16773
+ // deliberately only fires here — see enterVectorEditForSelection's doc
16774
+ // comment for the single-screen deferral.
16775
+ if (selectedLayerIdsState.length === 1) {
16776
+ const layerId = selectedLayerIdsState[0]!;
16777
+ const owner = codeLayerOwnerByNodeIdRef.current.get(layerId);
16778
+ const penNodesAttr = owner?.node.dataAttributes["data-an-pen-nodes"];
16779
+ if (owner && penNodesAttr && enterVectorEditForSelection(owner)) {
16780
+ return;
16781
+ }
16782
+ }
15619
16783
  const target = getOverviewEnterTarget({
15620
16784
  activeFileId: activeFile?.id ?? activeFileId,
15621
16785
  overviewSelectedScreenIds,
@@ -15627,6 +16791,7 @@ export default function DesignEditor() {
15627
16791
  activeFile?.id,
15628
16792
  activeFileId,
15629
16793
  enterSingleScreen,
16794
+ enterVectorEditForSelection,
15630
16795
  overviewSelectedScreenIds,
15631
16796
  selectedLayerIdsState,
15632
16797
  viewMode,
@@ -15772,6 +16937,22 @@ export default function DesignEditor() {
15772
16937
  setOverviewSelectAllRequest((request) => request + 1);
15773
16938
  }, [activeFile, files, getFreshActiveContent]);
15774
16939
 
16940
+ // L12: shared by Cmd+R and the canvas context-menu Rename item — the single
16941
+ // currently-selected layer id eligible for the layers-panel inline rename,
16942
+ // or null when zero or more-than-one layers are selected (screen/file rows
16943
+ // are excluded; renaming those is a separate flow). `__`-prefixed and file
16944
+ // ids are filtered the same way handleSelectAllFrames/other selection
16945
+ // helpers already do.
16946
+ const getSingleSelectedRenamableLayerId = useCallback((): string | null => {
16947
+ const fileIds = new Set(files.map((file) => file.id));
16948
+ const selectedRenamableLayerIds = selectedLayerIdsState.filter(
16949
+ (layerId) => !layerId.startsWith("__") && !fileIds.has(layerId),
16950
+ );
16951
+ return selectedRenamableLayerIds.length === 1
16952
+ ? selectedRenamableLayerIds[0]!
16953
+ : null;
16954
+ }, [files, selectedLayerIdsState]);
16955
+
15775
16956
  const shouldHandleEditorHotkey = useCallback((event: KeyboardEvent) => {
15776
16957
  const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
15777
16958
  const primary = event.metaKey || event.ctrlKey;
@@ -15815,21 +16996,16 @@ export default function DesignEditor() {
15815
16996
  // L12: Cmd+R (and the context-menu Rename item, both routed through
15816
16997
  // useDesignHotkeys' onRename) previously always renamed the DESIGN
15817
16998
  // TITLE, even while a layer was selected — surprising when the user's
15818
- // focus is clearly on a specific layer. Route to the layer instead when
15819
- // exactly one selectable (non-file-row) layer is selected; only fall
15820
- // back to renaming the design title when nothing is selected. The panel
15821
- // itself only exposes rename as an internal double-click/context-menu
15822
- // interaction (no imperative "start rename" entry point yet), so this
15823
- // selects the layer and nudges the user to double-click it in the panel
15824
- // rather than silently doing nothing or renaming the wrong thing.
16999
+ // focus is clearly on a specific layer. Route to the layer's real inline
17000
+ // rename editor (LayersPanel ref's beginRename) when exactly one
17001
+ // selectable (non-file-row) layer is selected; only fall back to
17002
+ // renaming the design title when nothing (or more than one layer) is
17003
+ // selected.
15825
17004
  onRename: () => {
15826
17005
  if (!canEditDesign) return;
15827
- const fileIds = new Set(files.map((file) => file.id));
15828
- const selectedRenamableLayerIds = selectedLayerIdsState.filter(
15829
- (layerId) => !layerId.startsWith("__") && !fileIds.has(layerId),
15830
- );
15831
- if (selectedRenamableLayerIds.length === 1) {
15832
- toast.info(t("designEditor.toasts.layerRenameSelectLayer"));
17006
+ const layerId = getSingleSelectedRenamableLayerId();
17007
+ if (layerId) {
17008
+ layersPanelRef.current?.beginRename(layerId);
15833
17009
  return;
15834
17010
  }
15835
17011
  setTitleDraft(design?.title ?? "");
@@ -17722,6 +18898,50 @@ ${serializedHtml}
17722
18898
  // connected inspector components; not all render paths call it directly.
17723
18899
  void requestLocalhostWrite;
17724
18900
 
18901
+ // Localhost workspace roots for the code workbench: one per distinct
18902
+ // connection referenced by this design's localhost-backed screens.
18903
+ const workbenchLocalhostConnections = useMemo(() => {
18904
+ const seen = new Map<string, { connectionId: string; label: string }>();
18905
+ for (const screen of overviewScreens) {
18906
+ if (screen.sourceType !== "localhost" || !screen.connectionId) continue;
18907
+ if (seen.has(screen.connectionId)) continue;
18908
+ let label = "Local app" /* i18n-ignore */;
18909
+ const screenUrl = screen.url ?? screen.previewUrl;
18910
+ if (screenUrl) {
18911
+ try {
18912
+ label = new URL(screenUrl).host || label;
18913
+ } catch {
18914
+ // Keep the fallback label for malformed screen URLs.
18915
+ }
18916
+ }
18917
+ seen.set(screen.connectionId, {
18918
+ connectionId: screen.connectionId,
18919
+ label,
18920
+ });
18921
+ }
18922
+ return [...seen.values()];
18923
+ }, [overviewScreens]);
18924
+
18925
+ // Consent round trip for code-workbench saves to local files: opens the
18926
+ // shared write-consent dialog and retries the save once granted.
18927
+ const handleWorkbenchLocalWriteConsent = useCallback(
18928
+ (connectionId: string, retry: () => void, filePath?: string) => {
18929
+ if (!id || !canEditDesign) return;
18930
+ setLocalhostConsentConnectionId(connectionId);
18931
+ setLocalhostWriteConsentPayload({
18932
+ rootPath:
18933
+ workbenchLocalhostConnections.find(
18934
+ (connection) => connection.connectionId === connectionId,
18935
+ )?.label ?? connectionId,
18936
+ files: filePath ? [filePath] : [],
18937
+ onGranted: () => retry(),
18938
+ onCancel: () => {},
18939
+ });
18940
+ setLocalhostWriteConsentOpen(true);
18941
+ },
18942
+ [canEditDesign, id, workbenchLocalhostConnections],
18943
+ );
18944
+
17725
18945
  /**
17726
18946
  * Derive a relative file path from the active localhost screen.
17727
18947
  * Prefers `sourceFile` (the build-output relative path recorded at connect
@@ -20437,6 +21657,7 @@ ${serializedHtml}
20437
21657
  </div>
20438
21658
  <div className="min-h-0 flex-1">
20439
21659
  <LayersPanel
21660
+ ref={layersPanelRef}
20440
21661
  screens={layerPanelFiles}
20441
21662
  activeScreenId={activeFileId ?? undefined}
20442
21663
  screenOverviewActive={viewMode === "overview"}
@@ -20601,7 +21822,7 @@ ${serializedHtml}
20601
21822
  </div>
20602
21823
  }
20603
21824
  >
20604
- <CodeWorkbenchHost
21825
+ <CodeWorkbench
20605
21826
  designId={id}
20606
21827
  activeFileId={routeCodeFileId}
20607
21828
  activeFilename={routeCodeFilename}
@@ -20609,6 +21830,10 @@ ${serializedHtml}
20609
21830
  selectedSelector={selectedCanvasSelector}
20610
21831
  canEdit={canEditDesign}
20611
21832
  onActiveFileChange={setActiveCodeFile}
21833
+ localhostConnections={workbenchLocalhostConnections}
21834
+ onRequestLocalWriteConsent={
21835
+ handleWorkbenchLocalWriteConsent
21836
+ }
20612
21837
  />
20613
21838
  </Suspense>
20614
21839
  ) : null}
@@ -20688,7 +21913,12 @@ ${serializedHtml}
20688
21913
  (selectedScreenIds.length > 0 && files.length > 1)),
20689
21914
  )}
20690
21915
  canReorder={canEditDesign && Boolean(selectedElement)}
20691
- canRename={false}
21916
+ // L12: rename is only offered for a single selectable layer
21917
+ // target (matching Cmd+R's routing) — the design-title rename
21918
+ // flow lives elsewhere (the title control), not this menu.
21919
+ canRename={
21920
+ canEditDesign && Boolean(getSingleSelectedRenamableLayerId())
21921
+ }
20692
21922
  canToggleLocked={canEditDesign && Boolean(activeLayerId)}
20693
21923
  canToggleHidden={canEditDesign && Boolean(activeLayerId)}
20694
21924
  canCopyProps={Boolean(selectedElement)}
@@ -20698,7 +21928,6 @@ ${serializedHtml}
20698
21928
  canCopyAsCode={Boolean(selectedElement?.selector)}
20699
21929
  canGroup={canGroup}
20700
21930
  canUngroup={canUngroup}
20701
- hiddenActions={["rename"]}
20702
21931
  getCanvasPoint={getContextCanvasPoint}
20703
21932
  onPasteHere={(details) =>
20704
21933
  void handlePasteSelection(
@@ -20720,6 +21949,14 @@ ${serializedHtml}
20720
21949
  onBringToFront={() => changeSelectedZIndex("front")}
20721
21950
  onSendBackward={() => changeSelectedZIndex("backward")}
20722
21951
  onSendToBack={() => changeSelectedZIndex("back")}
21952
+ // L12: start the layers panel's real inline rename editor on the
21953
+ // single selected layer — canRename above already gates this item
21954
+ // to exactly that case, so getSingleSelectedRenamableLayerId()
21955
+ // should always resolve here.
21956
+ onRename={() => {
21957
+ const layerId = getSingleSelectedRenamableLayerId();
21958
+ if (layerId) layersPanelRef.current?.beginRename(layerId);
21959
+ }}
20723
21960
  onToggleLocked={() => {
20724
21961
  if (activeLayerId) {
20725
21962
  handleToggleLayerLocked(activeLayerId, !activeLayerLocked);
@@ -20783,6 +22020,21 @@ ${serializedHtml}
20783
22020
  }}
20784
22021
  />
20785
22022
  )}
22023
+ {/* Full-app building status/controls. Renders only for
22024
+ designs backed by a fusion app (see readFusionApp) and
22025
+ only while the flag is on — the fusion actions the
22026
+ banner calls are gated on the same flag, so rendering it
22027
+ with the flag off would show controls that all error. */}
22028
+ {FULL_APP_BUILDING_ENABLED && id && fusionApp && (
22029
+ <FusionAppBanner
22030
+ designId={id}
22031
+ status={fusionApp.status}
22032
+ statusMessage={fusionApp.statusMessage}
22033
+ previewUrl={fusionApp.previewUrl}
22034
+ editorUrl={fusionApp.editorUrl}
22035
+ deployedUrl={fusionApp.deployedUrl}
22036
+ />
22037
+ )}
20786
22038
  {showPendingVisualStyleApply ? (
20787
22039
  <div className="pointer-events-none absolute bottom-5 right-5 z-[70] flex items-end">
20788
22040
  <DropdownMenu>
@@ -20827,139 +22079,164 @@ ${serializedHtml}
20827
22079
  </div>
20828
22080
  ) : null}
20829
22081
  {viewMode === "overview" ? (
20830
- <MultiScreenCanvas
20831
- screens={overviewScreens}
20832
- zoom={overviewCanvasZoom}
20833
- onZoomChange={setExplicitOverviewCanvasZoom}
20834
- activeId={activeFileId}
20835
- selectedScreenIds={overviewSelectedScreenIds}
20836
- fullViewScreenIds={fullViewScreenIds}
20837
- activeScreenHasHoveredChild={
20838
- Boolean(hoveredElement) &&
20839
- !hoveredElementIsScreenRoot &&
20840
- hoveredElementScreenId === activeFileId
20841
- }
20842
- hoveredChildScreenId={hoveredChildScreenId}
20843
- directlyHoveredScreenId={hoveredScreenRootId}
20844
- previewDeviceFrame={deviceFrame}
20845
- activeTool={activeTool}
20846
- onActiveToolChange={handleOverviewActiveToolChange}
20847
- selectAllRequest={overviewSelectAllRequest}
20848
- clearSelectionRequest={overviewClearSelectionRequest}
20849
- onScreenSelectionChange={
20850
- handleOverviewScreenSelectionChange
20851
- }
20852
- geometryById={canvasFrameGeometryById}
20853
- onGeometryChange={queueFrameGeometrySave}
20854
- onGeometryCommit={handleGeometryCommit}
20855
- onCreatePrimitive={handleCreatePrimitive}
20856
- onPrimitiveCreated={handlePrimitiveCreated}
20857
- onPrimitiveReparent={handleOverviewPrimitiveReparent}
20858
- onCrossScreenElementDrop={handleCrossScreenElementDrop}
20859
- boardFileId={boardFileId}
20860
- boardIsActive={activeFileId === boardFileId}
20861
- boardFileContent={boardFileContent}
20862
- boardFrameGeometry={boardFrameGeometry}
20863
- boardClearSelectionRequest={overviewClearSelectionRequest}
20864
- boardSelectedSelector={
20865
- activeFileId === boardFileId
20866
- ? selectedCanvasSelector
20867
- : null
20868
- }
20869
- boardSelectedSelectorCandidates={
20870
- activeFileId === boardFileId
20871
- ? selectedCanvasSelectorCandidates
20872
- : undefined
20873
- }
20874
- boardHoveredSelector={
20875
- hoveredElementScreenId === boardFileId
20876
- ? hoveredCanvasSelector
20877
- : null
20878
- }
20879
- boardHoveredSelectorCandidates={
20880
- hoveredElementScreenId === boardFileId
20881
- ? hoveredCanvasSelectorCandidates
20882
- : undefined
20883
- }
20884
- boardLockedSelectors={
20885
- boardFileId
20886
- ? getLayerSelectorsForFile(
20887
- boardFileId,
20888
- lockedLayerIds,
20889
- )
20890
- : undefined
20891
- }
20892
- boardHiddenSelectors={
20893
- boardFileId
20894
- ? getLayerSelectorsForFile(
20895
- boardFileId,
20896
- hiddenLayerIds,
20897
- )
20898
- : undefined
20899
- }
20900
- onBoardDrawPrimitive={
20901
- canEditDesign ? handleBoardDrawPrimitive : undefined
20902
- }
20903
- boardEditMode={canEditDesign}
20904
- onBoardElementSelect={
20905
- boardFileId ? handleBoardElementSelect : undefined
20906
- }
20907
- onBoardElementMarqueeSelect={
20908
- boardFileId
20909
- ? handleBoardElementMarqueeSelect
20910
- : undefined
20911
- }
20912
- onBoardElementHover={
20913
- boardFileId ? handleBoardElementHover : undefined
20914
- }
20915
- onBoardElementClear={
20916
- boardFileId ? handleBoardElementClear : undefined
20917
- }
20918
- onBoardIframeHotkey={handleIframeHotkey}
20919
- onBoardFigmaClipboardPaste={
20920
- handleCanvasFigmaClipboardPaste
20921
- }
20922
- onBoardIframeContextMenu={handleIframeContextMenu}
20923
- onBoardTextEditingStateChange={
20924
- handleBoardTextEditingStateChange
20925
- }
20926
- onBoardElementDblClickText={
20927
- boardFileId ? handleBoardElementDblClickText : undefined
20928
- }
20929
- onBoardVisualStyleChange={
20930
- boardFileId ? handleBoardVisualStyleChange : undefined
20931
- }
20932
- onBoardVisualStructureChange={
20933
- boardFileId
20934
- ? handleBoardVisualStructureChange
20935
- : undefined
20936
- }
20937
- onBoardVisualDuplicateChange={
20938
- boardFileId
20939
- ? handleBoardVisualDuplicateChange
20940
- : undefined
20941
- }
20942
- onBoardTextContentChange={
20943
- boardFileId ? handleBoardTextContentChange : undefined
20944
- }
20945
- onCreateScreenFrame={handleCreateScreenFrame}
20946
- onDeleteSelection={handleDeleteOverviewSelection}
20947
- onSelectionChange={handleOverviewScreenSelectionChange}
20948
- onLayerMarqueeSelectionChange={
20949
- handleLayerMarqueeSelectionChange
20950
- }
20951
- selectedLayerSelectorGroupsByScreen={
20952
- selectedLayerSelectorGroupsByScreen
20953
- }
20954
- onPick={handleOverviewScreenPick}
20955
- onEdit={enterSingleScreen}
20956
- onDuplicate={handleDuplicateScreen}
20957
- onAddBreakpoint={handleOverviewAddBreakpoint}
20958
- onActiveBreakpointChange={
20959
- handleOverviewActiveBreakpointChange
20960
- }
20961
- renderScreenContent={renderScreenContent}
20962
- />
22082
+ <>
22083
+ <MultiScreenCanvas
22084
+ screens={overviewScreens}
22085
+ zoom={overviewCanvasZoom}
22086
+ onZoomChange={setExplicitOverviewCanvasZoom}
22087
+ activeId={activeFileId}
22088
+ selectedScreenIds={overviewSelectedScreenIds}
22089
+ fullViewScreenIds={fullViewScreenIds}
22090
+ activeScreenHasHoveredChild={
22091
+ Boolean(hoveredElement) &&
22092
+ !hoveredElementIsScreenRoot &&
22093
+ hoveredElementScreenId === activeFileId
22094
+ }
22095
+ hoveredChildScreenId={hoveredChildScreenId}
22096
+ directlyHoveredScreenId={hoveredScreenRootId}
22097
+ previewDeviceFrame={deviceFrame}
22098
+ activeTool={activeTool}
22099
+ onActiveToolChange={handleOverviewActiveToolChange}
22100
+ selectAllRequest={overviewSelectAllRequest}
22101
+ clearSelectionRequest={overviewClearSelectionRequest}
22102
+ onScreenSelectionChange={
22103
+ handleOverviewScreenSelectionChange
22104
+ }
22105
+ geometryById={canvasFrameGeometryById}
22106
+ onGeometryChange={queueFrameGeometrySave}
22107
+ onGeometryCommit={handleGeometryCommit}
22108
+ vectorEdit={vectorEditOverlayState}
22109
+ onCreatePrimitive={handleCreatePrimitive}
22110
+ onPrimitiveCreated={handlePrimitiveCreated}
22111
+ onPrimitiveReparent={handleOverviewPrimitiveReparent}
22112
+ onCrossScreenElementDrop={handleCrossScreenElementDrop}
22113
+ boardFileId={boardFileId}
22114
+ boardIsActive={activeFileId === boardFileId}
22115
+ boardFileContent={boardFileContent}
22116
+ boardFrameGeometry={boardFrameGeometry}
22117
+ boardClearSelectionRequest={
22118
+ overviewClearSelectionRequest
22119
+ }
22120
+ boardSelectedSelector={
22121
+ activeFileId === boardFileId
22122
+ ? selectedCanvasSelector
22123
+ : null
22124
+ }
22125
+ boardSelectedSelectorCandidates={
22126
+ activeFileId === boardFileId
22127
+ ? selectedCanvasSelectorCandidates
22128
+ : undefined
22129
+ }
22130
+ boardHoveredSelector={
22131
+ hoveredElementScreenId === boardFileId
22132
+ ? hoveredCanvasSelector
22133
+ : null
22134
+ }
22135
+ boardHoveredSelectorCandidates={
22136
+ hoveredElementScreenId === boardFileId
22137
+ ? hoveredCanvasSelectorCandidates
22138
+ : undefined
22139
+ }
22140
+ boardLockedSelectors={
22141
+ boardFileId
22142
+ ? getLayerSelectorsForFile(
22143
+ boardFileId,
22144
+ lockedLayerIds,
22145
+ )
22146
+ : undefined
22147
+ }
22148
+ boardHiddenSelectors={
22149
+ boardFileId
22150
+ ? getLayerSelectorsForFile(
22151
+ boardFileId,
22152
+ hiddenLayerIds,
22153
+ )
22154
+ : undefined
22155
+ }
22156
+ onBoardDrawPrimitive={
22157
+ canEditDesign ? handleBoardDrawPrimitive : undefined
22158
+ }
22159
+ boardEditMode={canEditDesign}
22160
+ onBoardElementSelect={
22161
+ boardFileId ? handleBoardElementSelect : undefined
22162
+ }
22163
+ onBoardElementMarqueeSelect={
22164
+ boardFileId
22165
+ ? handleBoardElementMarqueeSelect
22166
+ : undefined
22167
+ }
22168
+ onBoardElementHover={
22169
+ boardFileId ? handleBoardElementHover : undefined
22170
+ }
22171
+ onBoardElementClear={
22172
+ boardFileId ? handleBoardElementClear : undefined
22173
+ }
22174
+ onBoardIframeHotkey={handleIframeHotkey}
22175
+ onBoardFigmaClipboardPaste={
22176
+ handleCanvasFigmaClipboardPaste
22177
+ }
22178
+ onBoardIframeContextMenu={handleIframeContextMenu}
22179
+ onBoardTextEditingStateChange={
22180
+ handleBoardTextEditingStateChange
22181
+ }
22182
+ onBoardElementDblClickText={
22183
+ boardFileId
22184
+ ? handleBoardElementDblClickText
22185
+ : undefined
22186
+ }
22187
+ onBoardVisualStyleChange={
22188
+ boardFileId ? handleBoardVisualStyleChange : undefined
22189
+ }
22190
+ onBoardVisualStructureChange={
22191
+ boardFileId
22192
+ ? handleBoardVisualStructureChange
22193
+ : undefined
22194
+ }
22195
+ onBoardVisualDuplicateChange={
22196
+ boardFileId
22197
+ ? handleBoardVisualDuplicateChange
22198
+ : undefined
22199
+ }
22200
+ onBoardTextContentChange={
22201
+ boardFileId ? handleBoardTextContentChange : undefined
22202
+ }
22203
+ onCreateScreenFrame={handleCreateScreenFrame}
22204
+ onDeleteSelection={handleDeleteOverviewSelection}
22205
+ onSelectionChange={handleOverviewScreenSelectionChange}
22206
+ onLayerMarqueeSelectionChange={
22207
+ handleLayerMarqueeSelectionChange
22208
+ }
22209
+ selectedLayerSelectorGroupsByScreen={
22210
+ selectedLayerSelectorGroupsByScreen
22211
+ }
22212
+ onPick={handleOverviewScreenPick}
22213
+ onEdit={enterSingleScreen}
22214
+ onDuplicate={handleDuplicateScreen}
22215
+ onAddBreakpoint={handleOverviewAddBreakpoint}
22216
+ onActiveBreakpointChange={
22217
+ handleOverviewActiveBreakpointChange
22218
+ }
22219
+ renderScreenContent={renderScreenContent}
22220
+ />
22221
+ {/* Presence (overview): the agent's selection ring +
22222
+ fading recent-edit highlights, resolved element-level
22223
+ inside the frame it is editing and positioned over the
22224
+ board. See overview presence pipeline above. */}
22225
+ {overviewAgentOthers.length > 0 && (
22226
+ <RemoteSelectionRings
22227
+ others={overviewAgentOthers}
22228
+ resolveRect={resolveOverviewSelectionRect}
22229
+ containerRef={canvasContainerRef}
22230
+ />
22231
+ )}
22232
+ {overviewRecentEditsForOverlays.length > 0 && (
22233
+ <RecentEditHighlights
22234
+ edits={overviewRecentEditsForOverlays}
22235
+ resolveRect={resolveOverviewRecentEditRect}
22236
+ containerRef={canvasContainerRef}
22237
+ />
22238
+ )}
22239
+ </>
20963
22240
  ) : (
20964
22241
  <>
20965
22242
  <DesignCanvas
@@ -20988,6 +22265,8 @@ ${serializedHtml}
20988
22265
  interactMode={mode === "interact"}
20989
22266
  readOnly={!canEditDesign}
20990
22267
  scaleMode={activeTool === "scale"}
22268
+ activeCreationTool={activeSingleScreenCreationTool}
22269
+ onCreatePrimitive={handleSingleScreenCreatePrimitive}
20991
22270
  clearSelectionRequest={overviewClearSelectionRequest}
20992
22271
  selectedSelector={selectedCanvasSelector}
20993
22272
  selectedSelectorCandidates={
@@ -21071,10 +22350,30 @@ ${serializedHtml}
21071
22350
  }
21072
22351
  }}
21073
22352
  />
21074
- {/* Presence: live cursor overlay for remote participants */}
22353
+ {/* Presence: remote selection rings (human peers + AI),
22354
+ resolved into the active screen's iframe. */}
21075
22355
  {others.length > 0 && (
22356
+ <RemoteSelectionRings
22357
+ others={othersForOverlays}
22358
+ resolveRect={resolveSelectionRect}
22359
+ containerRef={canvasContainerRef}
22360
+ />
22361
+ )}
22362
+ {/* Presence: lingering fading highlights over regions a
22363
+ peer or the AI just edited. */}
22364
+ {recentEditsForOverlays.length > 0 && (
22365
+ <RecentEditHighlights
22366
+ edits={recentEditsForOverlays}
22367
+ resolveRect={resolveRecentEditRect}
22368
+ containerRef={canvasContainerRef}
22369
+ />
22370
+ )}
22371
+ {/* Presence: live cursor overlay for remote participants.
22372
+ The AI gets a synthesized cursor derived from its
22373
+ current edit target (see othersWithAgentCursor). */}
22374
+ {othersWithAgentCursor.length > 0 && (
21076
22375
  <LiveCursorOverlay
21077
- others={others}
22376
+ others={othersWithAgentCursor}
21078
22377
  containerRef={canvasContainerRef}
21079
22378
  />
21080
22379
  )}
@@ -21605,7 +22904,7 @@ ${serializedHtml}
21605
22904
  {/* Localhost write-consent dialog: shown when the agent or editor wants to
21606
22905
  persist an edit to a local HTML/CSS source file and no valid grant
21607
22906
  exists for the active connection yet. */}
21608
- {id && activeLocalhostConnectionId && (
22907
+ {id && (activeLocalhostConnectionId || localhostConsentConnectionId) && (
21609
22908
  <LocalhostWriteConsentDialog
21610
22909
  open={localhostWriteConsentOpen}
21611
22910
  onOpenChange={(next) => {