@agent-native/core 0.85.6 → 0.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1069) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +130 -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 +6 -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/AssistantChat.tsx +126 -17
  93. package/corpus/core/src/client/agent-chat-adapter.ts +6 -5
  94. package/corpus/core/src/client/app-providers.tsx +2 -2
  95. package/corpus/core/src/client/collab/index.ts +31 -0
  96. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +16 -1
  97. package/corpus/core/src/client/components/PresenceBar.tsx +14 -1
  98. package/corpus/core/src/client/components/RecentEditHighlights.tsx +219 -0
  99. package/corpus/core/src/client/components/RemoteSelectionRings.tsx +40 -6
  100. package/corpus/core/src/client/index.ts +48 -0
  101. package/corpus/core/src/client/org/OrgSwitcher.tsx +0 -4
  102. package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +49 -32
  103. package/corpus/core/src/client/rich-markdown-editor/index.ts +6 -0
  104. package/corpus/core/src/client/rich-markdown-editor/surgical-apply.ts +192 -0
  105. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +59 -8
  106. package/corpus/core/src/client/session-replay.ts +181 -15
  107. package/corpus/core/src/client/sse-event-processor.ts +90 -1
  108. package/corpus/core/src/client/use-action.ts +123 -25
  109. package/corpus/core/src/client/use-agent-engine-configured.ts +7 -1
  110. package/corpus/core/src/client/use-chat-threads.ts +18 -7
  111. package/corpus/core/src/client/use-db-sync.ts +104 -19
  112. package/corpus/core/src/client/use-pinch-zoom.ts +76 -11
  113. package/corpus/core/src/client/visual-style-controls.tsx +475 -0
  114. package/corpus/core/src/collab/agent-presence.ts +191 -62
  115. package/corpus/core/src/collab/awareness.ts +83 -6
  116. package/corpus/core/src/collab/client.ts +89 -146
  117. package/corpus/core/src/collab/index.ts +34 -0
  118. package/corpus/core/src/collab/presence.ts +63 -3
  119. package/corpus/core/src/collab/recent-edits.ts +171 -0
  120. package/corpus/core/src/collab/undo.ts +432 -0
  121. package/corpus/core/src/collab/ydoc-manager.ts +68 -0
  122. package/corpus/core/src/connections/catalog.ts +2 -3
  123. package/corpus/core/src/db/create-get-db.ts +86 -1
  124. package/corpus/core/src/db/ensure-additive-columns.ts +430 -0
  125. package/corpus/core/src/db/index.ts +6 -0
  126. package/corpus/core/src/db/migrations.ts +236 -35
  127. package/corpus/core/src/jobs/scheduler.ts +17 -14
  128. package/corpus/core/src/observability/traces.ts +182 -1
  129. package/corpus/core/src/org/context.ts +79 -2
  130. package/corpus/core/src/server/agent-chat-plugin.ts +10 -6
  131. package/corpus/core/src/server/auth-marketing.ts +0 -11
  132. package/corpus/core/src/server/auth.ts +11 -4
  133. package/corpus/core/src/server/collab-plugin.ts +56 -13
  134. package/corpus/core/src/server/design-token-utils.ts +1 -1
  135. package/corpus/core/src/server/fusion-app.ts +447 -0
  136. package/corpus/core/src/server/index.ts +13 -0
  137. package/corpus/core/src/server/onboarding-html.ts +3 -4
  138. package/corpus/core/src/server/poll-events.ts +14 -2
  139. package/corpus/core/src/server/poll.ts +219 -12
  140. package/corpus/core/src/server/schema-prompt.ts +28 -9
  141. package/corpus/core/src/settings/store.ts +33 -2
  142. package/corpus/core/src/sharing/actions/set-resource-visibility.ts +2 -0
  143. package/corpus/core/src/sharing/actions/share-resource.ts +3 -0
  144. package/corpus/core/src/sharing/actions/unshare-resource.ts +2 -0
  145. package/corpus/core/src/styles/agent-native.css +0 -5
  146. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  147. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  148. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  149. package/corpus/core/src/templates/default/package.json +3 -3
  150. package/corpus/core/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  151. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  152. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  153. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  154. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  155. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  156. package/corpus/core/src/tracking/providers.ts +22 -1
  157. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +15 -0
  158. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +20 -5
  159. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +8 -0
  160. package/corpus/templates/analytics/AGENTS.md +33 -6
  161. package/corpus/templates/analytics/actions/navigate.ts +14 -4
  162. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  163. package/corpus/templates/analytics/actions/view-screen.ts +57 -0
  164. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +23 -1
  165. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  166. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -0
  167. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -0
  168. package/corpus/templates/analytics/app/i18n/zh-TW.ts +15 -1
  169. package/corpus/templates/analytics/app/i18n-data.ts +151 -10
  170. package/corpus/templates/analytics/app/pages/Agents.tsx +118 -0
  171. package/corpus/templates/analytics/app/pages/DataSources.tsx +11 -1
  172. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +76 -40
  173. package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +5 -0
  174. package/corpus/templates/analytics/app/routes/agents.tsx +10 -0
  175. package/corpus/templates/analytics/changelog/2026-07-03-agent-admin-tools-page.md +6 -0
  176. package/corpus/templates/analytics/changelog/2026-07-03-agent-llm-observability-dashboard.md +6 -0
  177. package/corpus/templates/analytics/changelog/2026-07-03-session-recordings-now-include-response-bodies-for-5xx-error.md +6 -0
  178. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +34 -0
  179. package/corpus/templates/analytics/docs/schemas/tracked-events.md +6 -0
  180. package/corpus/templates/analytics/package.json +3 -3
  181. package/corpus/templates/analytics/seeds/dashboards/agent-observability-llm.json +173 -0
  182. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +23 -5
  183. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +58 -13
  184. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +23 -0
  185. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +5 -0
  186. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +120 -8
  187. package/corpus/templates/analytics/server/lib/session-replay.ts +3 -2
  188. package/corpus/templates/analytics/server/plugins/db.ts +93 -2
  189. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +45 -1
  190. package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +10 -0
  191. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +82 -19
  192. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +15 -0
  193. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +8 -0
  194. package/corpus/templates/assets/package.json +3 -3
  195. package/corpus/templates/assets/server/plugins/db.ts +63 -2
  196. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +15 -0
  197. package/corpus/templates/brain/package.json +3 -3
  198. package/corpus/templates/brain/server/plugins/db.ts +75 -2
  199. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +15 -0
  200. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +8 -0
  201. package/corpus/templates/calendar/package.json +3 -3
  202. package/corpus/templates/calendar/server/plugins/db.ts +64 -2
  203. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +15 -0
  204. package/corpus/templates/chat/package.json +3 -3
  205. package/corpus/templates/clips/actions/finalize-recording.ts +50 -40
  206. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
  207. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -3
  208. package/corpus/templates/clips/changelog/2026-07-03-mobile-recording-pages-keep-videos-in-a-16-9-player-with-act.md +6 -0
  209. package/corpus/templates/clips/desktop/src/lib/recorder.ts +245 -77
  210. package/corpus/templates/clips/package.json +3 -3
  211. package/corpus/templates/clips/server/lib/recording-upload-state.ts +56 -0
  212. package/corpus/templates/clips/server/plugins/db.ts +68 -1
  213. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +27 -3
  214. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +15 -0
  215. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +8 -0
  216. package/corpus/templates/content/actions/edit-document.ts +48 -1
  217. package/corpus/templates/content/actions/list-documents.ts +101 -94
  218. package/corpus/templates/content/actions/search-documents.ts +19 -3
  219. package/corpus/templates/content/actions/update-document.ts +54 -1
  220. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -7
  221. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +97 -2
  222. package/corpus/templates/content/changelog/2026-07-03-agent-document-edits-now-appear-as-a-live-collaborator-prese.md +6 -0
  223. package/corpus/templates/content/changelog/2026-07-03-document-search-and-list-views-load-faster-on-large-workspac.md +6 -0
  224. package/corpus/templates/content/package.json +3 -3
  225. package/corpus/templates/content/server/plugins/db.ts +57 -1
  226. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +190 -13
  227. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +16 -4
  228. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +46 -3
  229. package/corpus/templates/design/.agents/skills/full-app-build/SKILL.md +140 -0
  230. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +20 -0
  231. package/corpus/templates/design/AGENTS.md +83 -13
  232. package/corpus/templates/design/actions/add-fusion-screens.ts +91 -0
  233. package/corpus/templates/design/actions/apply-a11y-fix.ts +5 -1
  234. package/corpus/templates/design/actions/apply-component-prop-edit.ts +24 -69
  235. package/corpus/templates/design/actions/apply-fusion-edits.ts +161 -0
  236. package/corpus/templates/design/actions/apply-motion-edit.ts +29 -2
  237. package/corpus/templates/design/actions/apply-visual-edit.ts +38 -3
  238. package/corpus/templates/design/actions/connect-localhost.ts +25 -18
  239. package/corpus/templates/design/actions/create-component.ts +5 -1
  240. package/corpus/templates/design/actions/create-fusion-app.ts +202 -0
  241. package/corpus/templates/design/actions/deploy-fusion-app.ts +123 -0
  242. package/corpus/templates/design/actions/edit-design.ts +13 -16
  243. package/corpus/templates/design/actions/export-pdf.ts +8 -1
  244. package/corpus/templates/design/actions/export-zip.ts +10 -1
  245. package/corpus/templates/design/actions/get-fusion-deploy-status.ts +86 -0
  246. package/corpus/templates/design/actions/get-motion-timeline.ts +26 -20
  247. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +10 -0
  248. package/corpus/templates/design/actions/insert-asset.ts +190 -7
  249. package/corpus/templates/design/actions/list-fusion-edits.ts +96 -0
  250. package/corpus/templates/design/actions/list-local-files.ts +133 -0
  251. package/corpus/templates/design/actions/open-visual-edit.ts +27 -5
  252. package/corpus/templates/design/actions/present-design-variants.ts +2 -2
  253. package/corpus/templates/design/actions/push-fusion-app.ts +62 -0
  254. package/corpus/templates/design/actions/queue-fusion-edit.ts +112 -0
  255. package/corpus/templates/design/actions/read-local-file.ts +139 -0
  256. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +18 -14
  257. package/corpus/templates/design/actions/run-design-audit.ts +7 -4
  258. package/corpus/templates/design/actions/send-fusion-message.ts +69 -0
  259. package/corpus/templates/design/actions/sync-fusion-app.ts +196 -0
  260. package/corpus/templates/design/actions/write-local-file.ts +162 -36
  261. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +12 -0
  262. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +600 -66
  263. package/corpus/templates/design/app/components/design/EditPanel.tsx +793 -304
  264. package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +328 -0
  265. package/corpus/templates/design/app/components/design/LayersPanel.tsx +671 -154
  266. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +1 -1
  267. package/corpus/templates/design/app/components/design/MotionDock.tsx +234 -46
  268. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2070 -420
  269. package/corpus/templates/design/app/components/design/StatesPanel.tsx +25 -2
  270. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -98
  271. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +640 -72
  272. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +605 -0
  273. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +50 -26
  274. package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +85 -0
  275. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +466 -0
  276. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +53 -0
  277. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +339 -0
  278. package/corpus/templates/design/app/components/design/code-workbench/editor/Breadcrumbs.tsx +42 -0
  279. package/corpus/templates/design/app/components/design/code-workbench/editor/BufferSync.tsx +128 -0
  280. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +363 -0
  281. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +268 -0
  282. package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +208 -0
  283. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +61 -0
  284. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme-palette.ts +191 -0
  285. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme.ts +29 -0
  286. package/corpus/templates/design/app/components/design/code-workbench/editor/status-bar-lang.ts +21 -0
  287. package/corpus/templates/design/app/components/design/code-workbench/editor/tab-drag.ts +27 -0
  288. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +155 -0
  289. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +551 -0
  290. package/corpus/templates/design/app/components/design/code-workbench/explorer/file-icons.tsx +115 -0
  291. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +65 -0
  292. package/corpus/templates/design/app/components/design/code-workbench/format/prettier-format.ts +182 -0
  293. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +133 -0
  294. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +563 -0
  295. package/corpus/templates/design/app/components/design/code-workbench/quickinput/fuzzy.ts +170 -0
  296. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +508 -0
  297. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +280 -0
  298. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +734 -0
  299. package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +39 -0
  300. package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +196 -0
  301. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +177 -0
  302. package/corpus/templates/design/app/components/design/code-workbench/workspace/tree.ts +110 -0
  303. package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +103 -0
  304. package/corpus/templates/design/app/components/design/code-workbench-theme.ts +68 -0
  305. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +16 -4
  306. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +217 -113
  307. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +111 -5
  308. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -2
  309. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +79 -16
  310. package/corpus/templates/design/app/components/design/inspector/index.ts +9 -0
  311. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +67 -2
  312. package/corpus/templates/design/app/components/design/types.ts +16 -0
  313. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +80 -1
  314. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +24 -6
  315. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +15 -1
  316. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +6 -16
  317. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +77 -0
  318. package/corpus/templates/design/app/i18n/zh-TW.ts +2 -0
  319. package/corpus/templates/design/app/i18n-data.ts +31 -0
  320. package/corpus/templates/design/app/lib/design-import.ts +95 -0
  321. package/corpus/templates/design/app/pages/DesignEditor.tsx +5586 -1112
  322. package/corpus/templates/design/app/pages/Index.tsx +90 -17
  323. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +6 -0
  324. package/corpus/templates/design/changelog/2026-07-03-inspector-fixes-mixed-selections-show-mixed-instead-of-wrong.md +6 -0
  325. package/corpus/templates/design/changelog/2026-07-03-keyframe-timeline-works-reliably-drag-keyframes-smoothly-pic.md +6 -0
  326. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +6 -0
  327. package/corpus/templates/design/changelog/2026-07-03-much-faster-editor-smooth-dragging-zooming-and-panel-updates.md +6 -0
  328. package/corpus/templates/design/changelog/2026-07-03-pen-tool-refinements-click-the-first-point-to-close-with-a-d.md +6 -0
  329. package/corpus/templates/design/changelog/2026-07-03-text-editing-enter-adds-a-line-break-international-ime-typin.md +6 -0
  330. package/corpus/templates/design/changelog/2026-07-03-the-ai-now-edits-like-a-collaborator-on-canvas-its-cursor-gl.md +6 -0
  331. 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
  332. package/corpus/templates/design/changelog/2026-07-03-undo-and-redo-are-dependable-across-agent-edits-screen-switc.md +6 -0
  333. package/corpus/templates/design/package.json +4 -3
  334. package/corpus/templates/design/server/db/schema.ts +31 -0
  335. package/corpus/templates/design/server/lib/design-export.ts +43 -1
  336. package/corpus/templates/design/server/lib/fusion-screens.ts +246 -0
  337. package/corpus/templates/design/server/plugins/db.ts +89 -2
  338. package/corpus/templates/design/shared/board-file.ts +25 -5
  339. package/corpus/templates/design/shared/canvas-math.ts +754 -9
  340. package/corpus/templates/design/shared/code-layer.ts +304 -26
  341. package/corpus/templates/design/shared/collab-selection.ts +52 -0
  342. package/corpus/templates/design/shared/color-utils.ts +84 -0
  343. package/corpus/templates/design/shared/design-source-capabilities.ts +16 -7
  344. package/corpus/templates/design/shared/full-app.ts +126 -0
  345. package/corpus/templates/design/shared/motion-compiler.ts +75 -31
  346. package/corpus/templates/design/shared/motion-timeline.ts +335 -0
  347. package/corpus/templates/design/shared/pen-path.ts +496 -23
  348. package/corpus/templates/dispatch/package.json +3 -3
  349. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +15 -0
  350. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +8 -0
  351. package/corpus/templates/forms/package.json +3 -3
  352. package/corpus/templates/forms/server/plugins/db.ts +62 -2
  353. package/corpus/templates/macros/package.json +3 -3
  354. package/corpus/templates/macros/server/plugins/db.ts +63 -2
  355. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +15 -0
  356. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +8 -0
  357. package/corpus/templates/mail/package.json +3 -3
  358. package/corpus/templates/mail/server/lib/contact-frequency.ts +42 -15
  359. package/corpus/templates/mail/server/plugins/db.ts +63 -2
  360. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +15 -0
  361. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +9 -3
  362. package/corpus/templates/plan/.agents/skills/visualize-repo/SKILL.md +92 -0
  363. package/corpus/templates/plan/actions/export-visual-plan.ts +5 -0
  364. package/corpus/templates/plan/actions/get-local-plan-folder.ts +4 -22
  365. package/corpus/templates/plan/actions/get-plan-version.ts +6 -16
  366. package/corpus/templates/plan/actions/list-plan-versions.ts +48 -20
  367. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +41 -1
  368. package/corpus/templates/plan/actions/promote-local-plan-folder.ts +4 -22
  369. package/corpus/templates/plan/actions/update-local-plan-comments.ts +4 -2
  370. package/corpus/templates/plan/actions/update-local-plan-folder.ts +4 -22
  371. package/corpus/templates/plan/actions/update-visual-plan.ts +60 -1
  372. package/corpus/templates/plan/agent-native.app-skill.json +10 -0
  373. package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +138 -39
  374. package/corpus/templates/plan/app/components/editor/usePlanUndoStack.ts +165 -23
  375. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +80 -4
  376. package/corpus/templates/plan/app/global.css +11 -9
  377. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +10 -1
  378. package/corpus/templates/plan/app/hooks/use-plan-presence.ts +66 -0
  379. package/corpus/templates/plan/app/pages/PlansPage.tsx +215 -63
  380. package/corpus/templates/plan/changelog/2026-07-03-local-visual-plans-and-recaps-can-save-comments-without-sign.md +6 -0
  381. package/corpus/templates/plan/changelog/2026-07-03-plan-tabs-render-nested-text-instead-of-raw-json.md +6 -0
  382. package/corpus/templates/plan/changelog/2026-07-03-plans-are-now-fully-real-time-collaborative-multiple-people-.md +6 -0
  383. package/corpus/templates/plan/changelog/2026-07-03-plans-open-and-refresh-faster-especially-long-lived-plans-wi.md +6 -0
  384. package/corpus/templates/plan/changelog/2026-07-03-undo-now-survives-agent-edits-cmd-z-keeps-working-on-your-ow.md +6 -0
  385. package/corpus/templates/plan/package.json +5 -3
  386. package/corpus/templates/plan/server/db/schema.ts +12 -0
  387. package/corpus/templates/plan/server/lib/local-plan-kind.ts +25 -0
  388. package/corpus/templates/plan/server/lib/plan-versions.ts +83 -1
  389. package/corpus/templates/plan/server/lib/public-action-paths.ts +0 -2
  390. package/corpus/templates/plan/server/plans.ts +26 -3
  391. package/corpus/templates/plan/server/plugins/db.ts +105 -2
  392. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +8 -4
  393. package/corpus/templates/slides/AGENTS.md +4 -0
  394. package/corpus/templates/slides/actions/_agent-presence.ts +48 -0
  395. package/corpus/templates/slides/actions/add-slide.ts +16 -18
  396. package/corpus/templates/slides/actions/patch-deck.ts +4 -2
  397. package/corpus/templates/slides/actions/update-slide.ts +114 -77
  398. package/corpus/templates/slides/actions/view-screen.ts +39 -0
  399. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +47 -3
  400. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +32 -35
  401. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +625 -199
  402. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +342 -0
  403. package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +46 -96
  404. package/corpus/templates/slides/app/components/visual-editor/SaveStatusIndicator.tsx +8 -1
  405. package/corpus/templates/slides/app/context/DeckContext.tsx +589 -176
  406. package/corpus/templates/slides/app/hooks/use-deck-presence.ts +84 -42
  407. package/corpus/templates/slides/app/i18n/ar-SA.ts +46 -0
  408. package/corpus/templates/slides/app/i18n/de-DE.ts +46 -0
  409. package/corpus/templates/slides/app/i18n/en-US.ts +46 -0
  410. package/corpus/templates/slides/app/i18n/es-ES.ts +46 -0
  411. package/corpus/templates/slides/app/i18n/fr-FR.ts +46 -0
  412. package/corpus/templates/slides/app/i18n/hi-IN.ts +46 -0
  413. package/corpus/templates/slides/app/i18n/ja-JP.ts +46 -0
  414. package/corpus/templates/slides/app/i18n/ko-KR.ts +46 -0
  415. package/corpus/templates/slides/app/i18n/pt-BR.ts +46 -0
  416. package/corpus/templates/slides/app/i18n/zh-CN.ts +46 -0
  417. package/corpus/templates/slides/app/i18n/zh-TW.ts +46 -0
  418. package/corpus/templates/slides/app/pages/DeckEditor.tsx +25 -13
  419. package/corpus/templates/slides/changelog/2026-07-03-figma-style-slide-editing.md +6 -0
  420. package/corpus/templates/slides/changelog/2026-07-03-see-the-ai-working-with-you-when-the-agent-edits-a-slide-it-.md +6 -0
  421. package/corpus/templates/slides/changelog/2026-07-03-undo-redo-is-now-precise-and-safe-with-collaborators-it-only.md +6 -0
  422. package/corpus/templates/slides/package.json +3 -3
  423. package/corpus/templates/slides/server/handlers/decks.ts +31 -3
  424. package/corpus/templates/slides/server/plugins/db.ts +65 -2
  425. package/corpus/templates/videos/netlify.toml +1 -12
  426. package/dist/agent/run-store.js +1 -1
  427. package/dist/agent/run-store.js.map +1 -1
  428. package/dist/chat-threads/store.d.ts +9 -1
  429. package/dist/chat-threads/store.d.ts.map +1 -1
  430. package/dist/chat-threads/store.js +63 -11
  431. package/dist/chat-threads/store.js.map +1 -1
  432. package/dist/checkpoints/store.d.ts.map +1 -1
  433. package/dist/checkpoints/store.js +10 -2
  434. package/dist/checkpoints/store.js.map +1 -1
  435. package/dist/cli/create.d.ts.map +1 -1
  436. package/dist/cli/create.js +3 -5
  437. package/dist/cli/create.js.map +1 -1
  438. package/dist/cli/design-connect.d.ts +49 -1
  439. package/dist/cli/design-connect.d.ts.map +1 -1
  440. package/dist/cli/design-connect.js +414 -9
  441. package/dist/cli/design-connect.js.map +1 -1
  442. package/dist/cli/index.js +12 -0
  443. package/dist/cli/index.js.map +1 -1
  444. package/dist/cli/plan-local.d.ts +24 -0
  445. package/dist/cli/plan-local.d.ts.map +1 -1
  446. package/dist/cli/plan-local.js +188 -20
  447. package/dist/cli/plan-local.js.map +1 -1
  448. package/dist/cli/skills.d.ts +3 -1
  449. package/dist/cli/skills.d.ts.map +1 -1
  450. package/dist/cli/skills.js +173 -21
  451. package/dist/cli/skills.js.map +1 -1
  452. package/dist/cli/templates-meta.d.ts.map +1 -1
  453. package/dist/cli/templates-meta.js +0 -14
  454. package/dist/cli/templates-meta.js.map +1 -1
  455. package/dist/cli/visualize-repo.d.ts +49 -0
  456. package/dist/cli/visualize-repo.d.ts.map +1 -0
  457. package/dist/cli/visualize-repo.js +561 -0
  458. package/dist/cli/visualize-repo.js.map +1 -0
  459. package/dist/client/AssistantChat.d.ts.map +1 -1
  460. package/dist/client/AssistantChat.js +120 -15
  461. package/dist/client/AssistantChat.js.map +1 -1
  462. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  463. package/dist/client/agent-chat-adapter.js +6 -5
  464. package/dist/client/agent-chat-adapter.js.map +1 -1
  465. package/dist/client/app-providers.d.ts +2 -2
  466. package/dist/client/app-providers.js.map +1 -1
  467. package/dist/client/collab/index.d.ts +4 -1
  468. package/dist/client/collab/index.d.ts.map +1 -1
  469. package/dist/client/collab/index.js +3 -0
  470. package/dist/client/collab/index.js.map +1 -1
  471. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  472. package/dist/client/components/LiveCursorOverlay.js +11 -3
  473. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  474. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  475. package/dist/client/components/PresenceBar.js +6 -1
  476. package/dist/client/components/PresenceBar.js.map +1 -1
  477. package/dist/client/components/RecentEditHighlights.d.ts +43 -0
  478. package/dist/client/components/RecentEditHighlights.d.ts.map +1 -0
  479. package/dist/client/components/RecentEditHighlights.js +146 -0
  480. package/dist/client/components/RecentEditHighlights.js.map +1 -0
  481. package/dist/client/components/RemoteSelectionRings.d.ts +9 -0
  482. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
  483. package/dist/client/components/RemoteSelectionRings.js +22 -8
  484. package/dist/client/components/RemoteSelectionRings.js.map +1 -1
  485. package/dist/client/index.d.ts +6 -1
  486. package/dist/client/index.d.ts.map +1 -1
  487. package/dist/client/index.js +7 -0
  488. package/dist/client/index.js.map +1 -1
  489. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  490. package/dist/client/org/OrgSwitcher.js +1 -3
  491. package/dist/client/org/OrgSwitcher.js.map +1 -1
  492. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
  493. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  494. package/dist/client/rich-markdown-editor/SharedRichEditor.js +8 -2
  495. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  496. package/dist/client/rich-markdown-editor/index.d.ts +1 -0
  497. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  498. package/dist/client/rich-markdown-editor/index.js +1 -0
  499. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  500. package/dist/client/rich-markdown-editor/surgical-apply.d.ts +66 -0
  501. package/dist/client/rich-markdown-editor/surgical-apply.d.ts.map +1 -0
  502. package/dist/client/rich-markdown-editor/surgical-apply.js +147 -0
  503. package/dist/client/rich-markdown-editor/surgical-apply.js.map +1 -0
  504. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +18 -2
  505. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  506. package/dist/client/rich-markdown-editor/useCollabReconcile.js +39 -8
  507. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  508. package/dist/client/session-replay.d.ts +10 -1
  509. package/dist/client/session-replay.d.ts.map +1 -1
  510. package/dist/client/session-replay.js +134 -7
  511. package/dist/client/session-replay.js.map +1 -1
  512. package/dist/client/sse-event-processor.d.ts.map +1 -1
  513. package/dist/client/sse-event-processor.js +81 -1
  514. package/dist/client/sse-event-processor.js.map +1 -1
  515. package/dist/client/use-action.d.ts +24 -0
  516. package/dist/client/use-action.d.ts.map +1 -1
  517. package/dist/client/use-action.js +109 -27
  518. package/dist/client/use-action.js.map +1 -1
  519. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  520. package/dist/client/use-agent-engine-configured.js +7 -1
  521. package/dist/client/use-agent-engine-configured.js.map +1 -1
  522. package/dist/client/use-chat-threads.d.ts.map +1 -1
  523. package/dist/client/use-chat-threads.js +11 -3
  524. package/dist/client/use-chat-threads.js.map +1 -1
  525. package/dist/client/use-db-sync.d.ts +36 -0
  526. package/dist/client/use-db-sync.d.ts.map +1 -1
  527. package/dist/client/use-db-sync.js +68 -12
  528. package/dist/client/use-db-sync.js.map +1 -1
  529. package/dist/client/use-pinch-zoom.d.ts.map +1 -1
  530. package/dist/client/use-pinch-zoom.js +76 -11
  531. package/dist/client/use-pinch-zoom.js.map +1 -1
  532. package/dist/client/visual-style-controls.d.ts +79 -0
  533. package/dist/client/visual-style-controls.d.ts.map +1 -0
  534. package/dist/client/visual-style-controls.js +150 -0
  535. package/dist/client/visual-style-controls.js.map +1 -0
  536. package/dist/collab/agent-presence.d.ts +51 -5
  537. package/dist/collab/agent-presence.d.ts.map +1 -1
  538. package/dist/collab/agent-presence.js +144 -58
  539. package/dist/collab/agent-presence.js.map +1 -1
  540. package/dist/collab/awareness-store.d.ts +32 -0
  541. package/dist/collab/awareness-store.d.ts.map +1 -0
  542. package/dist/collab/awareness-store.js +0 -0
  543. package/dist/collab/awareness-store.js.map +1 -0
  544. package/dist/collab/awareness.d.ts +14 -3
  545. package/dist/collab/awareness.d.ts.map +1 -1
  546. package/dist/collab/awareness.js +53 -5
  547. package/dist/collab/awareness.js.map +1 -1
  548. package/dist/collab/client.d.ts +2 -0
  549. package/dist/collab/client.d.ts.map +1 -1
  550. package/dist/collab/client.js +79 -119
  551. package/dist/collab/client.js.map +1 -1
  552. package/dist/collab/index.d.ts +3 -1
  553. package/dist/collab/index.d.ts.map +1 -1
  554. package/dist/collab/index.js +5 -1
  555. package/dist/collab/index.js.map +1 -1
  556. package/dist/collab/presence.d.ts.map +1 -1
  557. package/dist/collab/presence.js +54 -3
  558. package/dist/collab/presence.js.map +1 -1
  559. package/dist/collab/recent-edits.d.ts +87 -0
  560. package/dist/collab/recent-edits.d.ts.map +1 -0
  561. package/dist/collab/recent-edits.js +116 -0
  562. package/dist/collab/recent-edits.js.map +1 -0
  563. package/dist/collab/undo.d.ts +121 -0
  564. package/dist/collab/undo.d.ts.map +1 -0
  565. package/dist/collab/undo.js +244 -0
  566. package/dist/collab/undo.js.map +1 -0
  567. package/dist/collab/ydoc-manager.d.ts +10 -0
  568. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  569. package/dist/collab/ydoc-manager.js +57 -0
  570. package/dist/collab/ydoc-manager.js.map +1 -1
  571. package/dist/connections/catalog.d.ts +2 -2
  572. package/dist/connections/catalog.d.ts.map +1 -1
  573. package/dist/connections/catalog.js +1 -1
  574. package/dist/connections/catalog.js.map +1 -1
  575. package/dist/db/create-get-db.d.ts +22 -0
  576. package/dist/db/create-get-db.d.ts.map +1 -1
  577. package/dist/db/create-get-db.js +73 -1
  578. package/dist/db/create-get-db.js.map +1 -1
  579. package/dist/db/ensure-additive-columns.d.ts +93 -0
  580. package/dist/db/ensure-additive-columns.d.ts.map +1 -0
  581. package/dist/db/ensure-additive-columns.js +355 -0
  582. package/dist/db/ensure-additive-columns.js.map +1 -0
  583. package/dist/db/index.d.ts +1 -0
  584. package/dist/db/index.d.ts.map +1 -1
  585. package/dist/db/index.js +1 -0
  586. package/dist/db/index.js.map +1 -1
  587. package/dist/db/migrations.d.ts +70 -0
  588. package/dist/db/migrations.d.ts.map +1 -1
  589. package/dist/db/migrations.js +196 -33
  590. package/dist/db/migrations.js.map +1 -1
  591. package/dist/jobs/scheduler.js +17 -10
  592. package/dist/jobs/scheduler.js.map +1 -1
  593. package/dist/notifications/routes.d.ts +3 -3
  594. package/dist/observability/routes.d.ts +5 -5
  595. package/dist/observability/traces.d.ts.map +1 -1
  596. package/dist/observability/traces.js +155 -1
  597. package/dist/observability/traces.js.map +1 -1
  598. package/dist/org/context.d.ts +7 -0
  599. package/dist/org/context.d.ts.map +1 -1
  600. package/dist/org/context.js +54 -2
  601. package/dist/org/context.js.map +1 -1
  602. package/dist/progress/routes.d.ts +1 -1
  603. package/dist/resources/handlers.d.ts +3 -3
  604. package/dist/secrets/routes.d.ts +6 -6
  605. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  606. package/dist/server/agent-chat-plugin.js +10 -6
  607. package/dist/server/agent-chat-plugin.js.map +1 -1
  608. package/dist/server/auth-marketing.d.ts.map +1 -1
  609. package/dist/server/auth-marketing.js +0 -10
  610. package/dist/server/auth-marketing.js.map +1 -1
  611. package/dist/server/auth.d.ts.map +1 -1
  612. package/dist/server/auth.js +6 -4
  613. package/dist/server/auth.js.map +1 -1
  614. package/dist/server/collab-plugin.d.ts.map +1 -1
  615. package/dist/server/collab-plugin.js +45 -13
  616. package/dist/server/collab-plugin.js.map +1 -1
  617. package/dist/server/design-token-utils.d.ts +1 -1
  618. package/dist/server/design-token-utils.js +1 -1
  619. package/dist/server/design-token-utils.js.map +1 -1
  620. package/dist/server/fusion-app.d.ts +107 -0
  621. package/dist/server/fusion-app.d.ts.map +1 -0
  622. package/dist/server/fusion-app.js +371 -0
  623. package/dist/server/fusion-app.js.map +1 -0
  624. package/dist/server/index.d.ts +1 -0
  625. package/dist/server/index.d.ts.map +1 -1
  626. package/dist/server/index.js +1 -0
  627. package/dist/server/index.js.map +1 -1
  628. package/dist/server/onboarding-html.d.ts.map +1 -1
  629. package/dist/server/onboarding-html.js +3 -4
  630. package/dist/server/onboarding-html.js.map +1 -1
  631. package/dist/server/poll-events.d.ts +2 -0
  632. package/dist/server/poll-events.d.ts.map +1 -1
  633. package/dist/server/poll-events.js +7 -2
  634. package/dist/server/poll-events.js.map +1 -1
  635. package/dist/server/poll.d.ts +47 -3
  636. package/dist/server/poll.d.ts.map +1 -1
  637. package/dist/server/poll.js +176 -10
  638. package/dist/server/poll.js.map +1 -1
  639. package/dist/server/schema-prompt.d.ts.map +1 -1
  640. package/dist/server/schema-prompt.js +24 -7
  641. package/dist/server/schema-prompt.js.map +1 -1
  642. package/dist/settings/store.d.ts.map +1 -1
  643. package/dist/settings/store.js +32 -3
  644. package/dist/settings/store.js.map +1 -1
  645. package/dist/sharing/actions/set-resource-visibility.js +2 -0
  646. package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
  647. package/dist/sharing/actions/share-resource.d.ts.map +1 -1
  648. package/dist/sharing/actions/share-resource.js +3 -0
  649. package/dist/sharing/actions/share-resource.js.map +1 -1
  650. package/dist/sharing/actions/unshare-resource.js +2 -0
  651. package/dist/sharing/actions/unshare-resource.js.map +1 -1
  652. package/dist/styles/agent-native.css +0 -5
  653. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  654. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  655. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  656. package/dist/templates/default/package.json +3 -3
  657. package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  658. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  659. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  660. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  661. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  662. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  663. package/dist/tracking/providers.d.ts.map +1 -1
  664. package/dist/tracking/providers.js +17 -1
  665. package/dist/tracking/providers.js.map +1 -1
  666. package/docs/content/cloneable-saas.mdx +0 -1
  667. package/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
  668. package/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
  669. package/docs/content/locales/ar-SA/sharing.mdx +1 -2
  670. package/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
  671. package/docs/content/locales/ar-SA/template-plan.mdx +23 -7
  672. package/docs/content/locales/ar-SA/tracking.mdx +1 -1
  673. package/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
  674. package/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
  675. package/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
  676. package/docs/content/locales/de-DE/sharing.mdx +1 -2
  677. package/docs/content/locales/de-DE/skills-guide.mdx +2 -3
  678. package/docs/content/locales/de-DE/template-plan.mdx +23 -7
  679. package/docs/content/locales/de-DE/tracking.mdx +1 -1
  680. package/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
  681. package/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
  682. package/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
  683. package/docs/content/locales/es-ES/sharing.mdx +1 -2
  684. package/docs/content/locales/es-ES/skills-guide.mdx +2 -3
  685. package/docs/content/locales/es-ES/template-plan.mdx +23 -7
  686. package/docs/content/locales/es-ES/tracking.mdx +1 -1
  687. package/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
  688. package/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
  689. package/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
  690. package/docs/content/locales/fr-FR/sharing.mdx +1 -2
  691. package/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
  692. package/docs/content/locales/fr-FR/template-plan.mdx +23 -7
  693. package/docs/content/locales/fr-FR/tracking.mdx +1 -1
  694. package/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
  695. package/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
  696. package/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
  697. package/docs/content/locales/hi-IN/sharing.mdx +1 -2
  698. package/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
  699. package/docs/content/locales/hi-IN/template-plan.mdx +23 -7
  700. package/docs/content/locales/hi-IN/tracking.mdx +1 -1
  701. package/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
  702. package/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
  703. package/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
  704. package/docs/content/locales/ja-JP/sharing.mdx +1 -2
  705. package/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
  706. package/docs/content/locales/ja-JP/template-plan.mdx +23 -7
  707. package/docs/content/locales/ja-JP/tracking.mdx +1 -1
  708. package/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
  709. package/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
  710. package/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
  711. package/docs/content/locales/ko-KR/sharing.mdx +1 -2
  712. package/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
  713. package/docs/content/locales/ko-KR/template-plan.mdx +23 -7
  714. package/docs/content/locales/ko-KR/tracking.mdx +1 -1
  715. package/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
  716. package/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
  717. package/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
  718. package/docs/content/locales/pt-BR/sharing.mdx +1 -2
  719. package/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
  720. package/docs/content/locales/pt-BR/template-plan.mdx +23 -7
  721. package/docs/content/locales/pt-BR/tracking.mdx +1 -1
  722. package/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
  723. package/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
  724. package/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
  725. package/docs/content/locales/zh-CN/sharing.mdx +1 -2
  726. package/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
  727. package/docs/content/locales/zh-CN/template-plan.mdx +23 -7
  728. package/docs/content/locales/zh-CN/tracking.mdx +1 -1
  729. package/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
  730. package/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
  731. package/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
  732. package/docs/content/locales/zh-TW/sharing.mdx +1 -2
  733. package/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
  734. package/docs/content/locales/zh-TW/template-plan.mdx +23 -7
  735. package/docs/content/locales/zh-TW/tracking.mdx +1 -1
  736. package/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
  737. package/docs/content/plan-plugin.mdx +12 -11
  738. package/docs/content/sharing.mdx +1 -2
  739. package/docs/content/skills-guide.mdx +4 -3
  740. package/docs/content/template-plan.mdx +37 -6
  741. package/docs/content/tracking.mdx +1 -1
  742. package/docs/content/workspace-connections.mdx +1 -1
  743. package/package.json +6 -6
  744. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  745. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  746. package/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  747. package/src/templates/default/package.json +3 -3
  748. package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  749. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  750. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  751. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  752. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  753. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  754. package/corpus/core/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  755. package/corpus/core/docs/content/locales/de-DE/template-videos.mdx +0 -336
  756. package/corpus/core/docs/content/locales/es-ES/template-videos.mdx +0 -336
  757. package/corpus/core/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  758. package/corpus/core/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  759. package/corpus/core/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  760. package/corpus/core/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  761. package/corpus/core/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  762. package/corpus/core/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  763. package/corpus/core/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  764. package/corpus/core/docs/content/template-videos.mdx +0 -332
  765. package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +0 -697
  766. package/corpus/templates/slides/app/components/editor/CodeEditor.tsx +0 -49
  767. package/corpus/templates/videos/.agents/skills/actions/SKILL.md +0 -432
  768. package/corpus/templates/videos/.agents/skills/animation-tracks/SKILL.md +0 -99
  769. package/corpus/templates/videos/.agents/skills/capture-learnings/SKILL.md +0 -76
  770. package/corpus/templates/videos/.agents/skills/composition-management/SKILL.md +0 -106
  771. package/corpus/templates/videos/.agents/skills/create-skill/SKILL.md +0 -221
  772. package/corpus/templates/videos/.agents/skills/delegate-to-agent/SKILL.md +0 -263
  773. package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +0 -112
  774. package/corpus/templates/videos/.agents/skills/security/SKILL.md +0 -263
  775. package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +0 -88
  776. package/corpus/templates/videos/.agents/skills/shadcn-ui/SKILL.md +0 -95
  777. package/corpus/templates/videos/.builder/ANIMATED_PROPERTIES.md +0 -335
  778. package/corpus/templates/videos/.builder/KEYFRAME_SYNC.md +0 -166
  779. package/corpus/templates/videos/.builder/SEAMLESS_SYNC.md +0 -211
  780. package/corpus/templates/videos/.builder/rules/deploy-app.mdc +0 -22
  781. package/corpus/templates/videos/.builder/rules/organize-ui.mdc +0 -9
  782. package/corpus/templates/videos/.builder/skills/remotion/SKILL.md +0 -29
  783. package/corpus/templates/videos/.builder/skills/remotion/rules/animations.md +0 -27
  784. package/corpus/templates/videos/.builder/skills/remotion/rules/assets.md +0 -43
  785. package/corpus/templates/videos/.builder/skills/remotion/rules/audio.md +0 -58
  786. package/corpus/templates/videos/.builder/skills/remotion/rules/compositions.md +0 -129
  787. package/corpus/templates/videos/.builder/skills/remotion/rules/fonts.md +0 -47
  788. package/corpus/templates/videos/.builder/skills/remotion/rules/images.md +0 -41
  789. package/corpus/templates/videos/.builder/skills/remotion/rules/parameters.md +0 -59
  790. package/corpus/templates/videos/.builder/skills/remotion/rules/sequencing.md +0 -54
  791. package/corpus/templates/videos/.builder/skills/remotion/rules/tailwind.md +0 -11
  792. package/corpus/templates/videos/.builder/skills/remotion/rules/text-animations.md +0 -18
  793. package/corpus/templates/videos/.builder/skills/remotion/rules/timing.md +0 -103
  794. package/corpus/templates/videos/.builder/skills/remotion/rules/transparent-videos.md +0 -35
  795. package/corpus/templates/videos/.builder/skills/remotion/rules/trimming.md +0 -38
  796. package/corpus/templates/videos/.builder/skills/remotion/rules/videos.md +0 -54
  797. package/corpus/templates/videos/.dockerignore +0 -62
  798. package/corpus/templates/videos/.env.example +0 -4
  799. package/corpus/templates/videos/.oxfmtrc.json +0 -8
  800. package/corpus/templates/videos/AGENTS.md +0 -46
  801. package/corpus/templates/videos/CHANGELOG.md +0 -10
  802. package/corpus/templates/videos/DEVELOPING.md +0 -143
  803. package/corpus/templates/videos/README.md +0 -33
  804. package/corpus/templates/videos/actions/analyze-brand-assets.ts +0 -151
  805. package/corpus/templates/videos/actions/apply-design-system.ts +0 -35
  806. package/corpus/templates/videos/actions/create-design-system.ts +0 -84
  807. package/corpus/templates/videos/actions/create-folder.ts +0 -48
  808. package/corpus/templates/videos/actions/db-connect.ts +0 -45
  809. package/corpus/templates/videos/actions/db-health.ts +0 -24
  810. package/corpus/templates/videos/actions/db-status.ts +0 -28
  811. package/corpus/templates/videos/actions/delete-composition.ts +0 -272
  812. package/corpus/templates/videos/actions/delete-folder.ts +0 -27
  813. package/corpus/templates/videos/actions/generate-animated-component.ts +0 -470
  814. package/corpus/templates/videos/actions/get-composition.ts +0 -36
  815. package/corpus/templates/videos/actions/get-design-system.ts +0 -35
  816. package/corpus/templates/videos/actions/import-code.ts +0 -67
  817. package/corpus/templates/videos/actions/import-design-project.ts +0 -42
  818. package/corpus/templates/videos/actions/import-document.ts +0 -125
  819. package/corpus/templates/videos/actions/import-from-url.ts +0 -18
  820. package/corpus/templates/videos/actions/import-github.ts +0 -182
  821. package/corpus/templates/videos/actions/list-compositions.ts +0 -32
  822. package/corpus/templates/videos/actions/list-design-systems.ts +0 -66
  823. package/corpus/templates/videos/actions/list-folders.ts +0 -61
  824. package/corpus/templates/videos/actions/move-composition-to-folder.ts +0 -59
  825. package/corpus/templates/videos/actions/navigate.ts +0 -40
  826. package/corpus/templates/videos/actions/rename-folder.ts +0 -41
  827. package/corpus/templates/videos/actions/run.ts +0 -2
  828. package/corpus/templates/videos/actions/save-composition.ts +0 -114
  829. package/corpus/templates/videos/actions/set-default-design-system.ts +0 -40
  830. package/corpus/templates/videos/actions/update-composition.ts +0 -81
  831. package/corpus/templates/videos/actions/update-design-system.ts +0 -68
  832. package/corpus/templates/videos/actions/validate-compositions.ts +0 -502
  833. package/corpus/templates/videos/actions/view-screen.ts +0 -48
  834. package/corpus/templates/videos/app/components/CameraControls.tsx +0 -442
  835. package/corpus/templates/videos/app/components/CameraToolbar.tsx +0 -437
  836. package/corpus/templates/videos/app/components/CloudUpgrade.tsx +0 -287
  837. package/corpus/templates/videos/app/components/CollabPresenceBar.tsx +0 -81
  838. package/corpus/templates/videos/app/components/CompSettingsEditor.tsx +0 -107
  839. package/corpus/templates/videos/app/components/ComponentLibrarySidebar.tsx +0 -457
  840. package/corpus/templates/videos/app/components/ComponentPropsPanel.tsx +0 -227
  841. package/corpus/templates/videos/app/components/ComponentsSidebar.tsx +0 -70
  842. package/corpus/templates/videos/app/components/CompositionCard.tsx +0 -147
  843. package/corpus/templates/videos/app/components/CurrentElementPanel.tsx +0 -835
  844. package/corpus/templates/videos/app/components/CursorControls.tsx +0 -664
  845. package/corpus/templates/videos/app/components/CursorPositioningOverlay.tsx +0 -238
  846. package/corpus/templates/videos/app/components/LibraryFolderRow.tsx +0 -235
  847. package/corpus/templates/videos/app/components/MotionCurveSelect.tsx +0 -61
  848. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +0 -192
  849. package/corpus/templates/videos/app/components/PropsEditor.tsx +0 -210
  850. package/corpus/templates/videos/app/components/QuestionFlow.tsx +0 -40
  851. package/corpus/templates/videos/app/components/Sidebar.tsx +0 -609
  852. package/corpus/templates/videos/app/components/StudioHeader.tsx +0 -109
  853. package/corpus/templates/videos/app/components/Timeline.tsx +0 -2067
  854. package/corpus/templates/videos/app/components/TrackPropertiesPanel.tsx +0 -922
  855. package/corpus/templates/videos/app/components/TweaksPanel.tsx +0 -277
  856. package/corpus/templates/videos/app/components/VideoPlayer.tsx +0 -487
  857. package/corpus/templates/videos/app/components/design-system/DesignSystemCard.tsx +0 -134
  858. package/corpus/templates/videos/app/components/design-system/DesignSystemPreview.tsx +0 -176
  859. package/corpus/templates/videos/app/components/design-system/DesignSystemSetup.tsx +0 -707
  860. package/corpus/templates/videos/app/components/keyframes/KeyframeActionButtons.tsx +0 -78
  861. package/corpus/templates/videos/app/components/keyframes/KeyframeNavigation.tsx +0 -59
  862. package/corpus/templates/videos/app/components/keyframes/PropertyNumberInput.tsx +0 -56
  863. package/corpus/templates/videos/app/components/layout/Header.tsx +0 -64
  864. package/corpus/templates/videos/app/components/layout/HeaderActions.tsx +0 -84
  865. package/corpus/templates/videos/app/components/layout/Layout.tsx +0 -132
  866. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -180
  867. package/corpus/templates/videos/app/components/ui/accordion.tsx +0 -56
  868. package/corpus/templates/videos/app/components/ui/alert-dialog.tsx +0 -139
  869. package/corpus/templates/videos/app/components/ui/alert.tsx +0 -59
  870. package/corpus/templates/videos/app/components/ui/aspect-ratio.tsx +0 -5
  871. package/corpus/templates/videos/app/components/ui/avatar.tsx +0 -48
  872. package/corpus/templates/videos/app/components/ui/badge.tsx +0 -37
  873. package/corpus/templates/videos/app/components/ui/breadcrumb.tsx +0 -115
  874. package/corpus/templates/videos/app/components/ui/button.tsx +0 -57
  875. package/corpus/templates/videos/app/components/ui/calendar.tsx +0 -68
  876. package/corpus/templates/videos/app/components/ui/card.tsx +0 -86
  877. package/corpus/templates/videos/app/components/ui/carousel.tsx +0 -260
  878. package/corpus/templates/videos/app/components/ui/chart.tsx +0 -375
  879. package/corpus/templates/videos/app/components/ui/checkbox.tsx +0 -28
  880. package/corpus/templates/videos/app/components/ui/collapsible.tsx +0 -9
  881. package/corpus/templates/videos/app/components/ui/command.tsx +0 -154
  882. package/corpus/templates/videos/app/components/ui/context-menu.tsx +0 -202
  883. package/corpus/templates/videos/app/components/ui/dialog.tsx +0 -130
  884. package/corpus/templates/videos/app/components/ui/drawer.tsx +0 -116
  885. package/corpus/templates/videos/app/components/ui/dropdown-menu.tsx +0 -212
  886. package/corpus/templates/videos/app/components/ui/form.tsx +0 -177
  887. package/corpus/templates/videos/app/components/ui/hover-card.tsx +0 -27
  888. package/corpus/templates/videos/app/components/ui/input-otp.tsx +0 -69
  889. package/corpus/templates/videos/app/components/ui/input.tsx +0 -22
  890. package/corpus/templates/videos/app/components/ui/label.tsx +0 -24
  891. package/corpus/templates/videos/app/components/ui/menubar.tsx +0 -234
  892. package/corpus/templates/videos/app/components/ui/navigation-menu.tsx +0 -128
  893. package/corpus/templates/videos/app/components/ui/pagination.tsx +0 -121
  894. package/corpus/templates/videos/app/components/ui/popover.tsx +0 -44
  895. package/corpus/templates/videos/app/components/ui/progress.tsx +0 -26
  896. package/corpus/templates/videos/app/components/ui/radio-group.tsx +0 -42
  897. package/corpus/templates/videos/app/components/ui/resizable.tsx +0 -43
  898. package/corpus/templates/videos/app/components/ui/scroll-area.tsx +0 -46
  899. package/corpus/templates/videos/app/components/ui/select.tsx +0 -158
  900. package/corpus/templates/videos/app/components/ui/separator.tsx +0 -29
  901. package/corpus/templates/videos/app/components/ui/sheet.tsx +0 -160
  902. package/corpus/templates/videos/app/components/ui/sidebar.tsx +0 -782
  903. package/corpus/templates/videos/app/components/ui/skeleton.tsx +0 -15
  904. package/corpus/templates/videos/app/components/ui/slider.tsx +0 -26
  905. package/corpus/templates/videos/app/components/ui/sonner.tsx +0 -58
  906. package/corpus/templates/videos/app/components/ui/spinner.tsx +0 -17
  907. package/corpus/templates/videos/app/components/ui/switch.tsx +0 -27
  908. package/corpus/templates/videos/app/components/ui/table.tsx +0 -117
  909. package/corpus/templates/videos/app/components/ui/tabs.tsx +0 -53
  910. package/corpus/templates/videos/app/components/ui/textarea.tsx +0 -23
  911. package/corpus/templates/videos/app/components/ui/toast.tsx +0 -127
  912. package/corpus/templates/videos/app/components/ui/toaster.tsx +0 -33
  913. package/corpus/templates/videos/app/components/ui/toggle-group.tsx +0 -59
  914. package/corpus/templates/videos/app/components/ui/toggle.tsx +0 -43
  915. package/corpus/templates/videos/app/components/ui/tooltip.tsx +0 -30
  916. package/corpus/templates/videos/app/components/ui/use-toast.ts +0 -3
  917. package/corpus/templates/videos/app/config/constants.ts +0 -204
  918. package/corpus/templates/videos/app/contexts/CompositionContext.tsx +0 -437
  919. package/corpus/templates/videos/app/contexts/CurrentElementContext.tsx +0 -247
  920. package/corpus/templates/videos/app/contexts/PlaybackContext.tsx +0 -65
  921. package/corpus/templates/videos/app/contexts/TimelineContext.tsx +0 -433
  922. package/corpus/templates/videos/app/entry.client.tsx +0 -19
  923. package/corpus/templates/videos/app/entry.server.tsx +0 -10
  924. package/corpus/templates/videos/app/global.css +0 -171
  925. package/corpus/templates/videos/app/hooks/use-composition-collab.ts +0 -269
  926. package/corpus/templates/videos/app/hooks/use-composition-design-system.ts +0 -47
  927. package/corpus/templates/videos/app/hooks/use-database-compositions.ts +0 -144
  928. package/corpus/templates/videos/app/hooks/use-db-status.ts +0 -30
  929. package/corpus/templates/videos/app/hooks/use-design-systems.ts +0 -21
  930. package/corpus/templates/videos/app/hooks/use-folders.ts +0 -199
  931. package/corpus/templates/videos/app/hooks/use-mobile.tsx +0 -21
  932. package/corpus/templates/videos/app/hooks/use-navigation-state.ts +0 -52
  933. package/corpus/templates/videos/app/hooks/use-question-flow.ts +0 -14
  934. package/corpus/templates/videos/app/hooks/use-toast.ts +0 -188
  935. package/corpus/templates/videos/app/hooks/useUnsavedChanges.ts +0 -48
  936. package/corpus/templates/videos/app/i18n/ar-SA.ts +0 -446
  937. package/corpus/templates/videos/app/i18n/de-DE.ts +0 -470
  938. package/corpus/templates/videos/app/i18n/en-US.ts +0 -452
  939. package/corpus/templates/videos/app/i18n/es-ES.ts +0 -468
  940. package/corpus/templates/videos/app/i18n/fr-FR.ts +0 -469
  941. package/corpus/templates/videos/app/i18n/hi-IN.ts +0 -446
  942. package/corpus/templates/videos/app/i18n/index.ts +0 -34
  943. package/corpus/templates/videos/app/i18n/ja-JP.ts +0 -460
  944. package/corpus/templates/videos/app/i18n/ko-KR.ts +0 -453
  945. package/corpus/templates/videos/app/i18n/pt-BR.ts +0 -461
  946. package/corpus/templates/videos/app/i18n/zh-CN.ts +0 -437
  947. package/corpus/templates/videos/app/i18n/zh-TW.ts +0 -437
  948. package/corpus/templates/videos/app/lib/database-compositions.ts +0 -67
  949. package/corpus/templates/videos/app/lib/design-systems.ts +0 -234
  950. package/corpus/templates/videos/app/lib/tab-id.ts +0 -1
  951. package/corpus/templates/videos/app/lib/utils.ts +0 -1
  952. package/corpus/templates/videos/app/pages/ComponentLibrary.tsx +0 -134
  953. package/corpus/templates/videos/app/pages/ComponentLibraryView.tsx +0 -362
  954. package/corpus/templates/videos/app/pages/CompositionView.tsx +0 -715
  955. package/corpus/templates/videos/app/pages/DesignSystems.tsx +0 -180
  956. package/corpus/templates/videos/app/pages/Index.tsx +0 -258
  957. package/corpus/templates/videos/app/pages/NewComposition.tsx +0 -95
  958. package/corpus/templates/videos/app/pages/NotFound.tsx +0 -36
  959. package/corpus/templates/videos/app/remotion/CameraHost.tsx +0 -223
  960. package/corpus/templates/videos/app/remotion/animations/useElementAnimations.ts +0 -231
  961. package/corpus/templates/videos/app/remotion/componentRegistry.ts +0 -473
  962. package/corpus/templates/videos/app/remotion/components/AnimatedElement.tsx +0 -436
  963. package/corpus/templates/videos/app/remotion/components/InteractiveElement.tsx +0 -159
  964. package/corpus/templates/videos/app/remotion/components/InteractiveZoneEditor.tsx +0 -273
  965. package/corpus/templates/videos/app/remotion/compositions/BlankComposition.tsx +0 -204
  966. package/corpus/templates/videos/app/remotion/compositions/index.ts +0 -2
  967. package/corpus/templates/videos/app/remotion/createComponentCursorTrack.ts +0 -132
  968. package/corpus/templates/videos/app/remotion/docs/CURSOR_TRACKS.md +0 -285
  969. package/corpus/templates/videos/app/remotion/easingFunctions.ts +0 -255
  970. package/corpus/templates/videos/app/remotion/hooks/createInteractiveComposition.tsx +0 -283
  971. package/corpus/templates/videos/app/remotion/hooks/useCursorHistory.ts +0 -72
  972. package/corpus/templates/videos/app/remotion/hooks/useCursorTypeFromHover.ts +0 -35
  973. package/corpus/templates/videos/app/remotion/hooks/useEditableZones.tsx +0 -267
  974. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimation.ts +0 -90
  975. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimationSmooth.ts +0 -146
  976. package/corpus/templates/videos/app/remotion/hooks/useInteractiveComponent.ts +0 -711
  977. package/corpus/templates/videos/app/remotion/hooks/useRegisterInteractiveElement.ts +0 -56
  978. package/corpus/templates/videos/app/remotion/library-components/Button.tsx +0 -252
  979. package/corpus/templates/videos/app/remotion/library-components/Card.tsx +0 -241
  980. package/corpus/templates/videos/app/remotion/library-components/CodePanel.tsx +0 -1005
  981. package/corpus/templates/videos/app/remotion/library-components/FileItem.tsx +0 -196
  982. package/corpus/templates/videos/app/remotion/library-components/FolderItem.tsx +0 -199
  983. package/corpus/templates/videos/app/remotion/library-components/PrimaryButton.tsx +0 -177
  984. package/corpus/templates/videos/app/remotion/library-components/SecondaryButton.tsx +0 -167
  985. package/corpus/templates/videos/app/remotion/library-components/SectionHeader.tsx +0 -108
  986. package/corpus/templates/videos/app/remotion/library-components/TEMPLATE.tsx +0 -211
  987. package/corpus/templates/videos/app/remotion/library-components/index.ts +0 -10
  988. package/corpus/templates/videos/app/remotion/registry.ts +0 -91
  989. package/corpus/templates/videos/app/remotion/testing/animationTestUtils.ts +0 -366
  990. package/corpus/templates/videos/app/remotion/trackAnimation.ts +0 -179
  991. package/corpus/templates/videos/app/remotion/trackHelpers.ts +0 -110
  992. package/corpus/templates/videos/app/remotion/ui-components/Cursor.tsx +0 -182
  993. package/corpus/templates/videos/app/remotion/ui-components/InteractiveButton.tsx +0 -116
  994. package/corpus/templates/videos/app/remotion/ui-components/InteractiveCard.tsx +0 -126
  995. package/corpus/templates/videos/app/remotion/ui-components/index.ts +0 -16
  996. package/corpus/templates/videos/app/remotion/utils/animationHelpers.ts +0 -444
  997. package/corpus/templates/videos/app/remotion/utils/interactiveElements.ts +0 -235
  998. package/corpus/templates/videos/app/remotion/utils/springAnimation.ts +0 -76
  999. package/corpus/templates/videos/app/root.tsx +0 -165
  1000. package/corpus/templates/videos/app/routes/$.tsx +0 -10
  1001. package/corpus/templates/videos/app/routes/_index.tsx +0 -25
  1002. package/corpus/templates/videos/app/routes/c.$compositionId.tsx +0 -10
  1003. package/corpus/templates/videos/app/routes/components.tsx +0 -10
  1004. package/corpus/templates/videos/app/routes/design-systems.tsx +0 -10
  1005. package/corpus/templates/videos/app/routes/extensions.$id.$slug.tsx +0 -2
  1006. package/corpus/templates/videos/app/routes/extensions.$id.tsx +0 -5
  1007. package/corpus/templates/videos/app/routes/extensions._index.tsx +0 -5
  1008. package/corpus/templates/videos/app/routes/extensions.tsx +0 -5
  1009. package/corpus/templates/videos/app/routes/settings.tsx +0 -88
  1010. package/corpus/templates/videos/app/routes/team.tsx +0 -11
  1011. package/corpus/templates/videos/app/routes.ts +0 -4
  1012. package/corpus/templates/videos/app/state.ts +0 -77
  1013. package/corpus/templates/videos/app/types/elementAnimations.ts +0 -121
  1014. package/corpus/templates/videos/app/types.ts +0 -200
  1015. package/corpus/templates/videos/app/utils/compositionHelpers.ts +0 -445
  1016. package/corpus/templates/videos/app/utils/debug.ts +0 -63
  1017. package/corpus/templates/videos/app/utils/keyframeUtils.ts +0 -331
  1018. package/corpus/templates/videos/app/utils/timelineCoordinates.ts +0 -79
  1019. package/corpus/templates/videos/app/vite-env.d.ts +0 -6
  1020. package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
  1021. package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
  1022. package/corpus/templates/videos/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
  1023. package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +0 -6
  1024. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
  1025. package/corpus/templates/videos/components.json +0 -20
  1026. package/corpus/templates/videos/data/sync-config.json +0 -3
  1027. package/corpus/templates/videos/docs/examples/create-composition.example.ts +0 -128
  1028. package/corpus/templates/videos/drizzle.config.ts +0 -3
  1029. package/corpus/templates/videos/learnings.defaults.md +0 -5
  1030. package/corpus/templates/videos/package.json +0 -114
  1031. package/corpus/templates/videos/public/agent-native-icon-dark.svg +0 -10
  1032. package/corpus/templates/videos/public/agent-native-icon-light.svg +0 -10
  1033. package/corpus/templates/videos/public/agent-native-logo-dark.svg +0 -21
  1034. package/corpus/templates/videos/public/agent-native-logo-light.svg +0 -21
  1035. package/corpus/templates/videos/public/favicon.svg +0 -1
  1036. package/corpus/templates/videos/public/icon-180.svg +0 -1
  1037. package/corpus/templates/videos/public/icon-192.svg +0 -1
  1038. package/corpus/templates/videos/public/icon-512.svg +0 -1
  1039. package/corpus/templates/videos/public/manifest.json +0 -21
  1040. package/corpus/templates/videos/public/placeholder.svg +0 -1
  1041. package/corpus/templates/videos/public/robots.txt +0 -14
  1042. package/corpus/templates/videos/react-router.config.ts +0 -7
  1043. package/corpus/templates/videos/server/db/index.ts +0 -37
  1044. package/corpus/templates/videos/server/db/schema.ts +0 -55
  1045. package/corpus/templates/videos/server/middleware/auth.ts +0 -15
  1046. package/corpus/templates/videos/server/plugins/agent-chat.ts +0 -29
  1047. package/corpus/templates/videos/server/plugins/auth.ts +0 -14
  1048. package/corpus/templates/videos/server/plugins/collab.ts +0 -11
  1049. package/corpus/templates/videos/server/plugins/core-routes.ts +0 -22
  1050. package/corpus/templates/videos/server/plugins/db.ts +0 -160
  1051. package/corpus/templates/videos/server/routes/[...page].get.ts +0 -5
  1052. package/corpus/templates/videos/server/routes/[...page].head.ts +0 -1
  1053. package/corpus/templates/videos/server/routes/api/save-composition-defaults.post.ts +0 -274
  1054. package/corpus/templates/videos/shared/api.ts +0 -75
  1055. package/corpus/templates/videos/ssr-entry.ts +0 -15
  1056. package/corpus/templates/videos/tsconfig.json +0 -21
  1057. package/corpus/templates/videos/vite.config.ts +0 -19
  1058. package/corpus/templates/videos/vitest.config.ts +0 -16
  1059. package/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  1060. package/docs/content/locales/de-DE/template-videos.mdx +0 -336
  1061. package/docs/content/locales/es-ES/template-videos.mdx +0 -336
  1062. package/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  1063. package/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  1064. package/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  1065. package/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  1066. package/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  1067. package/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  1068. package/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  1069. package/docs/content/template-videos.mdx +0 -332
@@ -12,8 +12,11 @@ import {
12
12
  IconSearch,
13
13
  } from "@tabler/icons-react";
14
14
  import {
15
+ forwardRef,
16
+ memo,
15
17
  useCallback,
16
18
  useEffect,
19
+ useImperativeHandle,
17
20
  useLayoutEffect,
18
21
  useMemo,
19
22
  useRef,
@@ -22,6 +25,8 @@ import {
22
25
  type DragEvent,
23
26
  type MouseEvent,
24
27
  type ReactNode,
28
+ type Ref,
29
+ type RefObject,
25
30
  } from "react";
26
31
 
27
32
  import { Button } from "@/components/ui/button";
@@ -40,9 +45,6 @@ import {
40
45
  } from "@/components/ui/tooltip";
41
46
  import { cn } from "@/lib/utils";
42
47
 
43
- const LAYER_ROW_BASE_INDENT = 4;
44
- const LAYER_ROW_DEPTH_INDENT = 18;
45
-
46
48
  export type LayersPanelNodeType =
47
49
  | "file"
48
50
  | "screen"
@@ -181,6 +183,20 @@ export interface LayersPanelProps {
181
183
  boardElements?: LayersPanelNode[];
182
184
  }
183
185
 
186
+ // L12: imperative handle so an external trigger (Cmd+R hotkey, canvas
187
+ // context-menu Rename item) can start the panel's inline rename editor on a
188
+ // specific layer, matching Figma. See beginRename below for what it does.
189
+ export interface LayersPanelHandle {
190
+ /**
191
+ * Starts inline rename for the given layer id. Returns false (and does
192
+ * nothing) when the id doesn't resolve to a renamable row — i.e. it isn't
193
+ * in the current tree, or the node has `renamable === false`. On success,
194
+ * expands the layer's collapsed ancestors so the row is visible, scrolls
195
+ * it into view, and focuses+selects the rename input once it mounts.
196
+ */
197
+ beginRename: (layerId: string) => boolean;
198
+ }
199
+
184
200
  export interface FlatLayerRow {
185
201
  node: LayersPanelNode;
186
202
  rowKey: string;
@@ -215,10 +231,13 @@ let activeDropIntent: LayersPanelMoveIntent | null = null;
215
231
 
216
232
  const ROW_BASE_INDENT = 4;
217
233
  const ROW_INDENT_STEP = 28;
218
- const ROW_MAX_INDENT = 96;
219
234
 
235
+ // No indent cap: deeply nested trees (Figma-style component instances easily
236
+ // exceed 4 levels) must stay visually distinguishable by depth. A previous
237
+ // 96px cap made every row at depth >= 4 render at the same indent, making
238
+ // nested structure ambiguous in the panel.
220
239
  function rowIndent(depth: number): number {
221
- return Math.min(ROW_BASE_INDENT + depth * ROW_INDENT_STEP, ROW_MAX_INDENT);
240
+ return ROW_BASE_INDENT + depth * ROW_INDENT_STEP;
222
241
  }
223
242
 
224
243
  function defaultLabels(t: ReturnType<typeof useT>): LayersPanelLabels {
@@ -348,7 +367,24 @@ function filterNode(
348
367
  return null;
349
368
  }
350
369
 
351
- function flattenRows(
370
+ // ORDER CONVENTION (L5): the panel's top row within a sibling group is the
371
+ // topmost-RENDERED layer, matching Figma. LayersPanelNode.children arrives in
372
+ // DOM order (first array element = first DOM child = bottom of the paint
373
+ // stack for overlapping siblings; last DOM child = topmost paint). So the
374
+ // panel must display each sibling group in REVERSE DOM order. This is the
375
+ // single place that convention is applied — everything else (drop-placement
376
+ // mapping in dropPlacementForEvent/handleDrop callers, and the CL:2769
377
+ // moveNode "inside" insertion point) is written to agree with it:
378
+ // - drop "above row X" (before X in the reversed panel list) => DOM order
379
+ // "after" X (closer to the paint-top), i.e. inserted after X in the DOM.
380
+ // - drop "below row X" (after X in the reversed panel list) => DOM order
381
+ // "before" X, i.e. inserted before X in the DOM.
382
+ // - "inside" a container drops at the END of the panel's child list, i.e.
383
+ // the FIRST DOM child position (contentStart), so the dropped node
384
+ // becomes the bottom-most-painted / top-of-panel-list child. See
385
+ // mapPanelPlacementToDomPlacement below and its use at the LP/DE drop
386
+ // boundary.
387
+ export function flattenRows(
352
388
  nodes: LayersPanelNode[],
353
389
  expandedIds: ReadonlySet<string>,
354
390
  forceExpanded: boolean,
@@ -357,7 +393,8 @@ function flattenRows(
357
393
  ancestorIds: string[] = [],
358
394
  rows: FlatLayerRow[] = [],
359
395
  ) {
360
- nodes.forEach((node, index) => {
396
+ const displayOrder = [...nodes].reverse();
397
+ displayOrder.forEach((node, index) => {
361
398
  const children = node.children ?? [];
362
399
  const hasChildren = children.length > 0;
363
400
  const canAcceptChildren = CONTAINER_TYPES.has(node.type);
@@ -385,6 +422,33 @@ function flattenRows(
385
422
  return rows;
386
423
  }
387
424
 
425
+ /**
426
+ * Maps a panel-order drop placement (computed from where the user dropped
427
+ * relative to a row's position in the reversed, top-row-is-topmost panel
428
+ * list) to the DOM-order placement the underlying moveNode/applyMoveNodeEdit
429
+ * primitive expects (see CL applyMoveNodeEdit: "before" = anchor.start,
430
+ * "after" = anchor.end, "inside" = anchor.contentEnd i.e. last DOM child).
431
+ *
432
+ * Because the panel displays each sibling group in reverse DOM order:
433
+ * - "before" in the panel (drop above row X, i.e. towards the top/topmost)
434
+ * means the moved node should render ABOVE X, i.e. paint AFTER X in the
435
+ * DOM => DOM placement "after".
436
+ * - "after" in the panel (drop below row X, towards the bottom/backmost)
437
+ * means the moved node should render BELOW X, i.e. paint BEFORE X in the
438
+ * DOM => DOM placement "before".
439
+ * - "inside" is unchanged in kind, but the DOM primitive already inserts at
440
+ * contentEnd (last DOM child), which is exactly the panel's "top of this
441
+ * group's list" — i.e. inside-drops naturally land at the top of the
442
+ * panel's child list with no further mapping needed.
443
+ */
444
+ export function mapPanelPlacementToDomPlacement(
445
+ placement: LayersPanelMoveIntent["placement"],
446
+ ): LayersPanelMoveIntent["placement"] {
447
+ if (placement === "before") return "after";
448
+ if (placement === "after") return "before";
449
+ return "inside";
450
+ }
451
+
388
452
  function nextExpandedIds(
389
453
  ids: readonly string[],
390
454
  nodeId: string,
@@ -399,6 +463,34 @@ function nextExpandedIds(
399
463
  return Array.from(next);
400
464
  }
401
465
 
466
+ /**
467
+ * L1: pure computation for the auto-expand-ancestors-of-selection effect.
468
+ * Given the current selection's ancestor ids and the CURRENT expanded set,
469
+ * returns the next expanded id list with any missing ancestors added, or
470
+ * null if nothing needs to change. Extracted as a pure function (mirroring
471
+ * shouldResyncLayerSelectionAnchor) so the auto-expand decision is testable
472
+ * without mounting the component. The caller is responsible for only
473
+ * invoking this once per NEW selection signature — see the
474
+ * lastAutoExpandedSelectionRef gate in the effect below, which is what
475
+ * actually fixes the collapse-bounces-back-instantly bug (this function
476
+ * itself is a straightforward set-union and isn't where that bug lived).
477
+ */
478
+ export function nextAutoExpandedIds(args: {
479
+ selectedAncestorIds: readonly string[];
480
+ expandedIds: readonly string[];
481
+ }): string[] | null {
482
+ if (args.selectedAncestorIds.length === 0) return null;
483
+ const next = new Set(args.expandedIds);
484
+ let changed = false;
485
+ args.selectedAncestorIds.forEach((id) => {
486
+ if (!next.has(id)) {
487
+ next.add(id);
488
+ changed = true;
489
+ }
490
+ });
491
+ return changed ? Array.from(next) : null;
492
+ }
493
+
402
494
  function collectAncestorIds(
403
495
  nodes: LayersPanelNode[],
404
496
  targetIds: ReadonlySet<string>,
@@ -424,6 +516,54 @@ function collectAncestorIds(
424
516
  return Array.from(ancestors);
425
517
  }
426
518
 
519
+ // Full-tree ancestor map (id -> ancestor id chain from root), independent of
520
+ // expand/collapse or search-filter state. Used at drag start to correctly
521
+ // identify selected descendants even when their row is currently not
522
+ // rendered in visibleRows (e.g. inside a collapsed ancestor) — see
523
+ // getDraggedLayerIdsForRows below.
524
+ export function buildAncestorIdMap(
525
+ nodes: LayersPanelNode[],
526
+ ): Map<string, string[]> {
527
+ const map = new Map<string, string[]>();
528
+
529
+ function visit(node: LayersPanelNode, ancestorIds: string[]) {
530
+ map.set(node.id, ancestorIds);
531
+ const children = node.children ?? [];
532
+ children.forEach((child) => visit(child, [...ancestorIds, node.id]));
533
+ }
534
+
535
+ nodes.forEach((node) => visit(node, []));
536
+ return map;
537
+ }
538
+
539
+ // L12: find a node anywhere in the full (unfiltered) tree by id, alongside
540
+ // its ancestor id chain. Used by beginRename to validate the target and to
541
+ // know which ancestors must be expanded for the row to become visible,
542
+ // independent of the current search/expand state.
543
+ export function findNodeWithAncestors(
544
+ nodes: LayersPanelNode[],
545
+ targetId: string,
546
+ ): { node: LayersPanelNode; ancestorIds: string[] } | null {
547
+ function visit(
548
+ node: LayersPanelNode,
549
+ ancestorIds: string[],
550
+ ): { node: LayersPanelNode; ancestorIds: string[] } | null {
551
+ if (node.id === targetId) return { node, ancestorIds };
552
+ const children = node.children ?? [];
553
+ for (const child of children) {
554
+ const found = visit(child, [...ancestorIds, node.id]);
555
+ if (found) return found;
556
+ }
557
+ return null;
558
+ }
559
+
560
+ for (const node of nodes) {
561
+ const found = visit(node, []);
562
+ if (found) return found;
563
+ }
564
+ return null;
565
+ }
566
+
427
567
  export function getLayerSelectionAnchorFromExternalSelection(args: {
428
568
  selectedIds: readonly string[];
429
569
  selectableVisibleIds: readonly string[];
@@ -451,20 +591,48 @@ export function getTreeOrderedLayerIds(
451
591
  ];
452
592
  }
453
593
 
594
+ // Shift-range selection is a straight slice through the flattened visible
595
+ // rows, so when the range spans an expanded parent AND some of its children,
596
+ // both end up selected. Figma normalizes this away: selecting an ancestor
597
+ // already implies its descendants for move/visual purposes, so a descendant
598
+ // whose ancestor is also in the resulting set should be dropped from the
599
+ // selection (the ancestor "wins"). Order-preserving.
600
+ export function dropDescendantsOfSelectedAncestors(
601
+ ids: readonly string[],
602
+ visibleRows: readonly FlatLayerRow[],
603
+ ): string[] {
604
+ const idSet = new Set(ids);
605
+ const ancestorIdsById = new Map(
606
+ visibleRows.map((row) => [row.node.id, row.ancestorIds] as const),
607
+ );
608
+ return ids.filter((id) => {
609
+ const ancestorIds = ancestorIdsById.get(id);
610
+ return !ancestorIds?.some((ancestorId) => idSet.has(ancestorId));
611
+ });
612
+ }
613
+
454
614
  export function getDraggedLayerIdsForRows(args: {
455
615
  selectedIds: readonly string[];
456
616
  nodeId: string;
457
617
  visibleRows: readonly FlatLayerRow[];
618
+ // Full-tree ancestor map (see buildAncestorIdMap), independent of
619
+ // expand/collapse or search-filter state. Required to correctly drop
620
+ // selected descendants whose row is currently not in visibleRows (e.g.
621
+ // inside a collapsed dragged parent) — falling back to visibleRows-only
622
+ // ancestor lookup would treat those as separate top-level drags and
623
+ // extract them from the parent being dragged. Optional only for
624
+ // call-site/back-compat convenience; always pass it in the real panel.
625
+ ancestorIdMap?: ReadonlyMap<string, string[]>;
458
626
  }): string[] {
459
627
  const rawDraggedIds = args.selectedIds.includes(args.nodeId)
460
628
  ? getTreeOrderedLayerIds(args.selectedIds, args.visibleRows)
461
629
  : [args.nodeId];
462
630
  const draggedIdSet = new Set(rawDraggedIds);
463
631
  return rawDraggedIds.filter((id) => {
464
- const rowForId = args.visibleRows.find((row) => row.node.id === id);
465
- return !rowForId?.ancestorIds.some((ancestorId) =>
466
- draggedIdSet.has(ancestorId),
467
- );
632
+ const ancestorIds =
633
+ args.ancestorIdMap?.get(id) ??
634
+ args.visibleRows.find((row) => row.node.id === id)?.ancestorIds;
635
+ return !ancestorIds?.some((ancestorId) => draggedIdSet.has(ancestorId));
468
636
  });
469
637
  }
470
638
 
@@ -491,44 +659,83 @@ function layerCanShowBadge(node: LayersPanelNode) {
491
659
  );
492
660
  }
493
661
 
494
- export function LayersPanel({
495
- screens,
496
- activeScreenId,
497
- screenOverviewActive = false,
498
- files,
499
- layers,
500
- codeLayers,
501
- elementLayers,
502
- selectedIds,
503
- expandedIds,
504
- searchQuery,
505
- className,
506
- footer,
507
- labels: labelsProp,
508
- onSearchQueryChange,
509
- onScreenSelect,
510
- onScreenOverview,
511
- onAddScreen,
512
- onExpandedIdsChange,
513
- onSelectionChange,
514
- onRename,
515
- onToggleLocked,
516
- onToggleHidden,
517
- onHoverLayer,
518
- onLeaveLayer,
519
- onMoveLayer,
520
- canMoveLayer,
521
- boardElements,
522
- }: LayersPanelProps) {
662
+ // PF8: DesignEditor re-renders on many state changes unrelated to the layers
663
+ // tree (drag gestures, zoom, canvas hover, etc). All of LayersPanel's call-site
664
+ // props are already stabilized (useMemo/useCallback/plain state — see
665
+ // DesignEditor.tsx's layerPanelFiles/overviewLayerPanelFiles/
666
+ // activeLayerPanelNodes/boardElements and the onXxx handlers passed to
667
+ // <LayersPanel>), so a default shallow-prop comparator is sufficient here;
668
+ // no custom comparator is needed or wanted since it would risk silently
669
+ // ignoring a genuinely-changed prop.
670
+ // L12: forwardRef is composed OUTSIDE memo (memo(forwardRef(...))) — this is
671
+ // the standard ordering and keeps the default shallow-prop memo comparator
672
+ // applying to the same props as before; the ref itself is never part of that
673
+ // comparison (React handles ref identity separately from memo's prop diff).
674
+ function LayersPanelImpl(
675
+ {
676
+ screens,
677
+ activeScreenId,
678
+ screenOverviewActive = false,
679
+ files,
680
+ layers,
681
+ codeLayers,
682
+ elementLayers,
683
+ selectedIds,
684
+ expandedIds,
685
+ searchQuery,
686
+ className,
687
+ footer,
688
+ labels: labelsProp,
689
+ onSearchQueryChange,
690
+ onScreenSelect,
691
+ onScreenOverview,
692
+ onAddScreen,
693
+ onExpandedIdsChange,
694
+ onSelectionChange,
695
+ onRename,
696
+ onToggleLocked,
697
+ onToggleHidden,
698
+ onHoverLayer,
699
+ onLeaveLayer,
700
+ onMoveLayer,
701
+ canMoveLayer,
702
+ boardElements,
703
+ }: LayersPanelProps,
704
+ ref: Ref<LayersPanelHandle>,
705
+ ) {
523
706
  const t = useT();
524
707
  const labels = useMemo(() => mergeLabels(labelsProp, t), [labelsProp, t]);
525
708
  const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
526
709
  const selectedIdsRef = useRef<readonly string[]>(selectedIds);
710
+ // Rows need the full visible-row order for keyboard navigation and
711
+ // multi-drag payload ordering, but that's whole-tree state, not a per-row
712
+ // primitive. Route it through a stable ref instead of a prop so passing it
713
+ // to LayerRow doesn't defeat React.memo (the ref object identity never
714
+ // changes; only .current does).
715
+ const visibleRowsRef = useRef<FlatLayerRow[]>([]);
716
+ // Full (unfiltered-by-expand/collapse) root nodes, threaded the same way so
717
+ // drag start can build a full-tree ancestor map (see buildAncestorIdMap)
718
+ // without adding a per-row array prop that would defeat React.memo.
719
+ const rootsRef = useRef<LayersPanelNode[]>([]);
720
+ // Same idea for expandedIds: onToggleExpanded needs the current expanded
721
+ // set to compute the next one, but reading it from a ref lets the
722
+ // per-row callback stay referentially stable across renders.
723
+ const expandedIdsRef = useRef<readonly string[]>(expandedIds);
724
+ expandedIdsRef.current = expandedIds;
527
725
  const lastPanelSelectionSignatureRef = useRef(selectedIds.join("\0"));
528
726
  const expandedIdSet = useMemo(() => new Set(expandedIds), [expandedIds]);
529
727
  const lastSelectionAnchorRef = useRef<string | null>(selectedIds[0] ?? null);
530
728
  const searchInputRef = useRef<HTMLInputElement>(null);
531
729
  const rowElementRefs = useRef(new Map<string, HTMLDivElement>());
730
+ // L20: edge auto-scroll during a row drag. scrollContainerRef is the
731
+ // scrollable rows list; autoScrollFrameRef holds the active rAF handle (or
732
+ // null when idle); autoScrollDirectionRef holds the current scroll
733
+ // direction/speed so the rAF loop keeps scrolling smoothly across frames
734
+ // without needing dragover to fire every frame (dragover cadence is
735
+ // browser-throttled and not reliable enough on its own for smooth scroll).
736
+ const scrollContainerRef = useRef<HTMLDivElement>(null);
737
+ const autoScrollFrameRef = useRef<number | null>(null);
738
+ const autoScrollSpeedRef = useRef(0);
532
739
  const [renamingId, setRenamingId] = useState<string | null>(null);
533
740
  const [renameDraft, setRenameDraft] = useState("");
534
741
  const renameOriginalNameRef = useRef<string>("");
@@ -570,6 +777,13 @@ export function LayersPanel({
570
777
  [visibleRows],
571
778
  );
572
779
 
780
+ // Keep the row-facing refs current every render. This runs during render
781
+ // (not an effect) so event handlers created during this same commit already
782
+ // see the latest arrays; it never triggers a re-render itself since only
783
+ // `.current` is written.
784
+ visibleRowsRef.current = visibleRows;
785
+ rootsRef.current = roots;
786
+
573
787
  useLayoutEffect(() => {
574
788
  selectedIdsRef.current = selectedIds;
575
789
  const signature = selectedIds.join("\0");
@@ -591,18 +805,28 @@ export function LayersPanel({
591
805
  });
592
806
  }, [selectableVisibleIds, selectedIds]);
593
807
 
808
+ // Auto-expand ancestors of the current selection. This must run only when
809
+ // the SELECTION changes (a new selection signature), not whenever
810
+ // expandedIds changes — otherwise collapsing an ancestor of the selected
811
+ // layer (which changes expandedIds but not the selection) would
812
+ // immediately re-expand it, since selectedAncestorIds still contains it.
813
+ // Track the selection signature we last auto-expanded for in a ref so the
814
+ // effect can bail out on every render triggered purely by a collapse.
815
+ const lastAutoExpandedSelectionRef = useRef<string | null>(null);
594
816
  useEffect(() => {
595
- if (selectedAncestorIds.length === 0) return;
596
- const next = new Set(expandedIds);
597
- let changed = false;
598
- selectedAncestorIds.forEach((id) => {
599
- if (!next.has(id)) {
600
- next.add(id);
601
- changed = true;
602
- }
817
+ const signature = selectedIds.join("\0");
818
+ if (lastAutoExpandedSelectionRef.current === signature) return;
819
+ lastAutoExpandedSelectionRef.current = signature;
820
+ const nextExpanded = nextAutoExpandedIds({
821
+ selectedAncestorIds,
822
+ expandedIds: expandedIdsRef.current,
603
823
  });
604
- if (changed) onExpandedIdsChange(Array.from(next));
605
- }, [expandedIds, onExpandedIdsChange, selectedAncestorIds]);
824
+ if (nextExpanded) onExpandedIdsChange(nextExpanded);
825
+ // Intentionally NOT depending on expandedIds: this effect must only react
826
+ // to selection changes (selectedIds / selectedAncestorIds), and reads the
827
+ // current expanded set from expandedIdsRef so a collapse doesn't retrigger it.
828
+ // eslint-disable-next-line react-hooks/exhaustive-deps
829
+ }, [onExpandedIdsChange, selectedAncestorIds, selectedIds]);
606
830
 
607
831
  const selectedScrollId = selectedIds[selectedIds.length - 1] ?? null;
608
832
  const selectedScrollRowKey = useMemo(() => {
@@ -656,9 +880,17 @@ export function LayersPanel({
656
880
  if (from >= 0 && to >= 0) {
657
881
  const [start, end] = from < to ? [from, to] : [to, from];
658
882
  const rangeIds = selectableVisibleIds.slice(start, end + 1);
659
- nextIds = options.additive
883
+ const merged = options.additive
660
884
  ? Array.from(new Set([...currentSelectedIds, ...rangeIds]))
661
885
  : rangeIds;
886
+ // A range that spans an expanded parent and some of its children
887
+ // would otherwise co-select both. Normalize so a selected
888
+ // descendant whose ancestor is also selected gets dropped — the
889
+ // ancestor selection already implies it.
890
+ nextIds = dropDescendantsOfSelectedAncestors(
891
+ merged,
892
+ visibleRowsRef.current,
893
+ );
662
894
  } else {
663
895
  nextIds = [id];
664
896
  }
@@ -718,6 +950,81 @@ export function LayersPanel({
718
950
  [],
719
951
  );
720
952
 
953
+ // L12: id of a layer whose rename was started externally (beginRename) and
954
+ // is waiting for its row to become visible/mounted so the input can be
955
+ // focused. Ancestor expansion is asynchronous (it flows out through
956
+ // onExpandedIdsChange and back in via the expandedIds prop), so we can't
957
+ // synchronously focus the input the same tick beginRename runs — the row
958
+ // may not exist in the DOM yet. The effect below watches for the row to
959
+ // appear in rowElementRefs and finishes the job once it does.
960
+ const pendingRenameFocusIdRef = useRef<string | null>(null);
961
+
962
+ const beginRename = useCallback(
963
+ (layerId: string): boolean => {
964
+ if (!onRename) return false;
965
+ const found = findNodeWithAncestors(rootsRef.current, layerId);
966
+ if (!found || found.node.renamable === false) return false;
967
+ const { node, ancestorIds } = found;
968
+
969
+ renameOriginalNameRef.current = node.name;
970
+ setRenamingId(node.id);
971
+ setRenameDraft(node.name);
972
+
973
+ const nextExpanded = nextAutoExpandedIds({
974
+ selectedAncestorIds: ancestorIds,
975
+ expandedIds: expandedIdsRef.current,
976
+ });
977
+ if (nextExpanded) onExpandedIdsChange(nextExpanded);
978
+
979
+ pendingRenameFocusIdRef.current = node.id;
980
+ return true;
981
+ },
982
+ [onExpandedIdsChange, onRename],
983
+ );
984
+
985
+ useImperativeHandle(ref, () => ({ beginRename }), [beginRename]);
986
+
987
+ // Finishes an in-flight beginRename once its row is mounted: scrolls it
988
+ // into view and focuses+selects the rename input (the input already
989
+ // select-on-focuses via its own onFocus handler below). Depends on
990
+ // renamingId and visibleRows so it re-checks whenever either the rename
991
+ // target or ancestor-expansion state changes — the row can become visible
992
+ // either on this same render (already expanded) or a later one (ancestors
993
+ // needed expanding first, which round-trips through onExpandedIdsChange).
994
+ useEffect(() => {
995
+ const pendingId = pendingRenameFocusIdRef.current;
996
+ if (!pendingId || renamingId !== pendingId) return;
997
+ const rowKey = visibleRows.find((row) => row.node.id === pendingId)?.rowKey;
998
+ if (!rowKey) return;
999
+ const frame = window.requestAnimationFrame(() => {
1000
+ const rowElement = rowElementRefs.current.get(rowKey);
1001
+ rowElement?.scrollIntoView({ block: "nearest" });
1002
+ rowElement
1003
+ ?.querySelector<HTMLInputElement>("input")
1004
+ ?.focus({ preventScroll: true });
1005
+ });
1006
+ pendingRenameFocusIdRef.current = null;
1007
+ return () => window.cancelAnimationFrame(frame);
1008
+ }, [renamingId, visibleRows]);
1009
+
1010
+ // Id-first, stable callbacks for LayerRow. Each reads current
1011
+ // expandedIds/onExpandedIdsChange/onRename from refs/closure-captured
1012
+ // props at call time rather than recreating a fresh per-row closure every
1013
+ // render — this keeps LayerRow's props referentially stable so
1014
+ // React.memo(LayerRow) actually skips re-renders.
1015
+ const handleToggleExpanded = useCallback(
1016
+ (id: string, expanded: boolean) => {
1017
+ onExpandedIdsChange(
1018
+ nextExpandedIds(expandedIdsRef.current, id, expanded),
1019
+ );
1020
+ },
1021
+ [onExpandedIdsChange],
1022
+ );
1023
+
1024
+ const handleCancelRename = useCallback(() => {
1025
+ setRenamingId(null);
1026
+ }, []);
1027
+
721
1028
  const hasAnyRows = roots.length > 0;
722
1029
  const screenRows = screens ?? files ?? [];
723
1030
  const openSearch = useCallback(() => {
@@ -745,6 +1052,65 @@ export function LayersPanel({
745
1052
  );
746
1053
  }, [collapseTargetId, expandedIds, onExpandedIdsChange]);
747
1054
 
1055
+ // L20: auto-scroll the rows list while dragging near the top/bottom edge.
1056
+ // Runs a rAF loop so the scroll speed stays smooth and independent of the
1057
+ // browser's dragover event cadence.
1058
+ const AUTO_SCROLL_EDGE_PX = 40;
1059
+ const AUTO_SCROLL_MAX_SPEED_PX = 14;
1060
+
1061
+ const stopAutoScroll = useCallback(() => {
1062
+ if (autoScrollFrameRef.current !== null) {
1063
+ window.cancelAnimationFrame(autoScrollFrameRef.current);
1064
+ autoScrollFrameRef.current = null;
1065
+ }
1066
+ autoScrollSpeedRef.current = 0;
1067
+ }, []);
1068
+
1069
+ const runAutoScrollFrame = useCallback(() => {
1070
+ const container = scrollContainerRef.current;
1071
+ const speed = autoScrollSpeedRef.current;
1072
+ if (!container || speed === 0) {
1073
+ autoScrollFrameRef.current = null;
1074
+ return;
1075
+ }
1076
+ container.scrollTop += speed;
1077
+ autoScrollFrameRef.current =
1078
+ window.requestAnimationFrame(runAutoScrollFrame);
1079
+ }, []);
1080
+
1081
+ const handleRowsDragOver = useCallback(
1082
+ (event: DragEvent<HTMLDivElement>) => {
1083
+ if (!activeDragState) {
1084
+ stopAutoScroll();
1085
+ return;
1086
+ }
1087
+ const container = scrollContainerRef.current;
1088
+ if (!container) return;
1089
+ const rect = container.getBoundingClientRect();
1090
+ const offsetFromTop = event.clientY - rect.top;
1091
+ const offsetFromBottom = rect.bottom - event.clientY;
1092
+ let speed = 0;
1093
+ if (offsetFromTop < AUTO_SCROLL_EDGE_PX) {
1094
+ const intensity = 1 - Math.max(0, offsetFromTop) / AUTO_SCROLL_EDGE_PX;
1095
+ speed = -Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_PX);
1096
+ } else if (offsetFromBottom < AUTO_SCROLL_EDGE_PX) {
1097
+ const intensity =
1098
+ 1 - Math.max(0, offsetFromBottom) / AUTO_SCROLL_EDGE_PX;
1099
+ speed = Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_PX);
1100
+ }
1101
+ autoScrollSpeedRef.current = speed;
1102
+ if (speed !== 0 && autoScrollFrameRef.current === null) {
1103
+ autoScrollFrameRef.current =
1104
+ window.requestAnimationFrame(runAutoScrollFrame);
1105
+ } else if (speed === 0) {
1106
+ stopAutoScroll();
1107
+ }
1108
+ },
1109
+ [runAutoScrollFrame, stopAutoScroll],
1110
+ );
1111
+
1112
+ useEffect(() => stopAutoScroll, [stopAutoScroll]);
1113
+
748
1114
  return (
749
1115
  <aside
750
1116
  className={cn(
@@ -866,55 +1232,71 @@ export function LayersPanel({
866
1232
  </div>
867
1233
  ) : null}
868
1234
 
869
- <div className="min-h-0 flex-1 overflow-auto overscroll-contain py-2">
1235
+ <div
1236
+ ref={scrollContainerRef}
1237
+ className="min-h-0 flex-1 overflow-auto overscroll-contain py-2"
1238
+ onDragOver={handleRowsDragOver}
1239
+ onDrop={stopAutoScroll}
1240
+ onDragEnd={stopAutoScroll}
1241
+ >
870
1242
  {visibleRows.length ? (
871
1243
  <div
872
1244
  className="w-max min-w-full px-2"
873
1245
  role="tree"
874
1246
  aria-label={labels.title}
875
1247
  >
876
- {visibleRows.map((row) => (
877
- <LayerRow
878
- key={row.rowKey}
879
- row={row}
880
- labels={labels}
881
- isExpanded={expandedIdSet.has(row.node.id)}
882
- isSelected={selectedIdSet.has(row.node.id)}
883
- isInSelectedSubtree={row.ancestorIds.some((id) =>
884
- selectedIdSet.has(id),
885
- )}
886
- isActiveScreen={
887
- row.node.id === activeScreenId &&
888
- (row.node.type === "file" ||
889
- row.node.type === "screen" ||
890
- row.node.type === "frame")
891
- }
892
- isRenaming={renamingId === row.node.id}
893
- rowRef={(element) => registerRowElement(row.rowKey, element)}
894
- renameDraft={renameDraft}
895
- onRenameDraftChange={setRenameDraft}
896
- onCommitRename={commitRename}
897
- onCancelRename={() => setRenamingId(null)}
898
- onStartRename={startRename}
899
- onRename={onRename}
900
- onSelect={selectNode}
901
- onToggleExpanded={(expanded) =>
902
- onExpandedIdsChange(
903
- nextExpandedIds(expandedIds, row.node.id, expanded),
904
- )
905
- }
906
- onToggleLocked={onToggleLocked}
907
- onToggleHidden={onToggleHidden}
908
- onHoverLayer={onHoverLayer}
909
- onLeaveLayer={onLeaveLayer}
910
- onMoveLayer={onMoveLayer}
911
- canMoveLayer={canMoveLayer}
912
- dropIndicator={dropIndicator}
913
- onDropIndicatorChange={setDropIndicator}
914
- selectedIds={selectedIds}
915
- visibleRows={visibleRows}
916
- />
917
- ))}
1248
+ {visibleRows.map((row) => {
1249
+ // Per-row primitives computed here (not inside LayerRow) so the
1250
+ // row only receives booleans/strings it needs — no whole-tree
1251
+ // arrays that would force a re-render every time any other
1252
+ // row's selection state changes.
1253
+ const isSelected = selectedIdSet.has(row.node.id);
1254
+ const isInSelectedSubtree = row.ancestorIds.some((id) =>
1255
+ selectedIdSet.has(id),
1256
+ );
1257
+ const isActiveScreen =
1258
+ row.node.id === activeScreenId &&
1259
+ (row.node.type === "file" ||
1260
+ row.node.type === "screen" ||
1261
+ row.node.type === "frame");
1262
+ const isRenaming = renamingId === row.node.id;
1263
+ const activeDropPlacement =
1264
+ dropIndicator?.targetId === row.node.id
1265
+ ? dropIndicator.placement
1266
+ : null;
1267
+ return (
1268
+ <LayerRow
1269
+ key={row.rowKey}
1270
+ row={row}
1271
+ labels={labels}
1272
+ isExpanded={expandedIdSet.has(row.node.id)}
1273
+ isSelected={isSelected}
1274
+ isInSelectedSubtree={isInSelectedSubtree}
1275
+ isActiveScreen={isActiveScreen}
1276
+ isRenaming={isRenaming}
1277
+ renameDraft={isRenaming ? renameDraft : ""}
1278
+ registerRowElement={registerRowElement}
1279
+ onRenameDraftChange={setRenameDraft}
1280
+ onCommitRename={commitRename}
1281
+ onCancelRename={handleCancelRename}
1282
+ onStartRename={startRename}
1283
+ onRename={onRename}
1284
+ onSelect={selectNode}
1285
+ onToggleExpanded={handleToggleExpanded}
1286
+ onToggleLocked={onToggleLocked}
1287
+ onToggleHidden={onToggleHidden}
1288
+ onHoverLayer={onHoverLayer}
1289
+ onLeaveLayer={onLeaveLayer}
1290
+ onMoveLayer={onMoveLayer}
1291
+ canMoveLayer={canMoveLayer}
1292
+ activeDropPlacement={activeDropPlacement}
1293
+ onDropIndicatorChange={setDropIndicator}
1294
+ selectedIdsRef={selectedIdsRef}
1295
+ visibleRowsRef={visibleRowsRef}
1296
+ rootsRef={rootsRef}
1297
+ />
1298
+ );
1299
+ })}
918
1300
  </div>
919
1301
  ) : (
920
1302
  <div className="px-3 py-8 text-center !text-[11px] text-muted-foreground">
@@ -927,34 +1309,16 @@ export function LayersPanel({
927
1309
  );
928
1310
  }
929
1311
 
930
- function LayerRow({
931
- row,
932
- labels,
933
- isExpanded,
934
- isSelected,
935
- isInSelectedSubtree,
936
- isActiveScreen,
937
- isRenaming,
938
- rowRef,
939
- renameDraft,
940
- onRenameDraftChange,
941
- onCommitRename,
942
- onCancelRename,
943
- onStartRename,
944
- onRename,
945
- onSelect,
946
- onToggleExpanded,
947
- onToggleLocked,
948
- onToggleHidden,
949
- onHoverLayer,
950
- onLeaveLayer,
951
- onMoveLayer,
952
- canMoveLayer,
953
- dropIndicator,
954
- onDropIndicatorChange,
955
- selectedIds,
956
- visibleRows,
957
- }: {
1312
+ // L12: forwardRef wraps the implementation function, and memo wraps the
1313
+ // forwardRef result — memo(forwardRef(Impl)), the standard composition order.
1314
+ // displayName is set explicitly because forwardRef's returned object doesn't
1315
+ // inherit the inner function's name the way a plain function component would
1316
+ // (React devtools/debugging would otherwise show "ForwardRef").
1317
+ const LayersPanelWithRef = forwardRef(LayersPanelImpl);
1318
+ LayersPanelWithRef.displayName = "LayersPanel";
1319
+ export const LayersPanel = memo(LayersPanelWithRef);
1320
+
1321
+ interface LayerRowProps {
958
1322
  row: FlatLayerRow;
959
1323
  labels: LayersPanelLabels;
960
1324
  isExpanded: boolean;
@@ -962,11 +1326,11 @@ function LayerRow({
962
1326
  isInSelectedSubtree: boolean;
963
1327
  isActiveScreen: boolean;
964
1328
  isRenaming: boolean;
965
- rowRef: (element: HTMLDivElement | null) => void;
1329
+ registerRowElement: (rowKey: string, element: HTMLDivElement | null) => void;
966
1330
  renameDraft: string;
967
1331
  onRenameDraftChange: (value: string) => void;
968
1332
  onCommitRename: (id: string) => void;
969
- onCancelRename: () => void;
1333
+ onCancelRename: (id: string) => void;
970
1334
  onStartRename: (node: LayersPanelNode) => void;
971
1335
  onRename?: (id: string, name: string) => void;
972
1336
  onSelect: (
@@ -978,39 +1342,108 @@ function LayerRow({
978
1342
  source: "keyboard" | "pointer";
979
1343
  },
980
1344
  ) => void;
981
- onToggleExpanded: (expanded: boolean) => void;
1345
+ onToggleExpanded: (id: string, expanded: boolean) => void;
982
1346
  onToggleLocked?: (id: string, locked: boolean) => void;
983
1347
  onToggleHidden?: (id: string, hidden: boolean) => void;
984
1348
  onHoverLayer?: (id: string) => void;
985
1349
  onLeaveLayer?: (id: string) => void;
986
1350
  onMoveLayer?: (intent: LayersPanelMoveIntent) => void;
987
1351
  canMoveLayer?: (intent: LayersPanelMoveIntent) => boolean;
988
- dropIndicator: LayersPanelMoveIntent | null;
1352
+ // Only this row's own drop-indicator placement ("before" | "after" |
1353
+ // "inside" | null) — not the whole dropIndicator object, so a dragover on
1354
+ // one row doesn't force every other row to re-render.
1355
+ activeDropPlacement: LayersPanelMoveIntent["placement"] | null;
989
1356
  onDropIndicatorChange: (intent: LayersPanelMoveIntent | null) => void;
990
- selectedIds: readonly string[];
991
- visibleRows: FlatLayerRow[];
992
- }) {
1357
+ // Whole-tree state needed for keyboard nav / multi-drag ordering, threaded
1358
+ // through stable refs instead of arrays so it never defeats memo — see the
1359
+ // comment where these refs are created in LayersPanel.
1360
+ selectedIdsRef: RefObject<readonly string[]>;
1361
+ visibleRowsRef: RefObject<FlatLayerRow[]>;
1362
+ // Full (unfiltered) root nodes, used only at drag start to build a
1363
+ // full-tree ancestor map — see buildAncestorIdMap and L13 in the drag-start
1364
+ // handler below.
1365
+ rootsRef: RefObject<LayersPanelNode[]>;
1366
+ }
1367
+
1368
+ const LayerRow = memo(function LayerRow({
1369
+ row,
1370
+ labels,
1371
+ isExpanded,
1372
+ isSelected,
1373
+ isInSelectedSubtree,
1374
+ isActiveScreen,
1375
+ isRenaming,
1376
+ registerRowElement,
1377
+ renameDraft,
1378
+ onRenameDraftChange,
1379
+ onCommitRename,
1380
+ onCancelRename,
1381
+ onStartRename,
1382
+ onRename,
1383
+ onSelect,
1384
+ onToggleExpanded,
1385
+ onToggleLocked,
1386
+ onToggleHidden,
1387
+ onHoverLayer,
1388
+ onLeaveLayer,
1389
+ onMoveLayer,
1390
+ canMoveLayer,
1391
+ activeDropPlacement,
1392
+ onDropIndicatorChange,
1393
+ selectedIdsRef,
1394
+ visibleRowsRef,
1395
+ rootsRef,
1396
+ }: LayerRowProps) {
993
1397
  const { node, depth, hasChildren, canAcceptChildren } = row;
994
1398
  const isComponentLayer = layerNodeIsComponent(node);
995
1399
  const selectable = node.selectable !== false;
996
1400
  const lockable = node.lockable !== false && Boolean(onToggleLocked);
997
1401
  const hideable = node.hideable !== false && Boolean(onToggleHidden);
998
- const dragEligible = selectable && !node.locked && !node.hidden;
999
- const draggable = dragEligible && Boolean(onMoveLayer);
1402
+ // L8: being a valid DRAG SOURCE and being a valid DROP ANCHOR (before/
1403
+ // after/inside target) are different concerns and must not share one gate.
1404
+ // - dragSourceEligible: only "locked" should block picking this row up to
1405
+ // drag it — locked means "don't let me move", not "don't let me be
1406
+ // referenced". Hidden no longer blocks dragging a row: visibility is
1407
+ // orthogonal to whether the layer can be reordered.
1408
+ // - anchorEligible: locked/hidden rows must still be usable as before/
1409
+ // after/inside drop targets so the user can position new layers next to
1410
+ // or inside a locked/hidden one without having to unlock/show it first.
1411
+ // (The corresponding DE:15181 canMoveLayer gate is fixed separately by
1412
+ // the DesignEditor owner; this only removes LP's OWN early-return that
1413
+ // would otherwise block reaching canMoveLayer at all.)
1414
+ const dragSourceEligible = selectable && !node.locked;
1415
+ const anchorEligible = selectable;
1416
+ const draggable = dragSourceEligible && Boolean(onMoveLayer);
1000
1417
  const canDropInside = layerCanDropInside(
1001
1418
  node,
1002
1419
  hasChildren,
1003
1420
  canAcceptChildren,
1004
1421
  );
1005
- const activeDrop =
1006
- dropIndicator?.targetId === node.id ? dropIndicator.placement : null;
1422
+ // L10: whether this row's bottom hover zone should resolve to "inside"
1423
+ // instead of "after" see dropPlacementForEvent.
1424
+ const isExpandedWithChildren = hasChildren && isExpanded;
1425
+ const activeDrop = activeDropPlacement;
1007
1426
  // Tracks whether the user pressed Escape to cancel rename so that the
1008
1427
  // subsequent blur event does not commit the edit.
1009
1428
  const renameCancelledRef = useRef(false);
1429
+ // L20: spring-loaded expand. Tracks the pending timer id for "hovering
1430
+ // this collapsed container during a drag" so a sustained hover expands it
1431
+ // (Figma-style) without requiring the user to drop and re-drag. Cleared on
1432
+ // drag-leave/drop/dragend and whenever the hover target/placement changes.
1433
+ const springLoadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
1434
+ const SPRING_LOAD_DELAY_MS = 600;
1435
+ const clearSpringLoadTimer = () => {
1436
+ if (springLoadTimerRef.current !== null) {
1437
+ clearTimeout(springLoadTimerRef.current);
1438
+ springLoadTimerRef.current = null;
1439
+ }
1440
+ };
1441
+ useEffect(() => clearSpringLoadTimer, []);
1010
1442
 
1011
1443
  const readSelectedIdsFromTree = (target: HTMLElement): string[] => {
1012
1444
  const tree = target.closest('[role="tree"]');
1013
- if (!tree) return selectedIds.filter((id) => !id.startsWith("__"));
1445
+ if (!tree)
1446
+ return selectedIdsRef.current.filter((id) => !id.startsWith("__"));
1014
1447
  return Array.from(
1015
1448
  tree.querySelectorAll<HTMLElement>(
1016
1449
  '[role="treeitem"][aria-selected="true"] [data-layer-row-button][data-layer-node-id]',
@@ -1061,7 +1494,7 @@ function LayerRow({
1061
1494
  target.focus();
1062
1495
  return true;
1063
1496
  };
1064
- const currentIndex = visibleRows.findIndex(
1497
+ const currentIndex = visibleRowsRef.current.findIndex(
1065
1498
  (visibleRow) => visibleRow.rowKey === row.rowKey,
1066
1499
  );
1067
1500
  const focusableButtons = getFocusableButtons();
@@ -1105,7 +1538,7 @@ function LayerRow({
1105
1538
  }
1106
1539
  if (event.key === "ArrowRight" && hasChildren && !isExpanded) {
1107
1540
  event.preventDefault();
1108
- onToggleExpanded(true);
1541
+ onToggleExpanded(node.id, true);
1109
1542
  return;
1110
1543
  }
1111
1544
  if (event.key === "ArrowRight" && hasChildren && isExpanded) {
@@ -1113,7 +1546,9 @@ function LayerRow({
1113
1546
  const nextButton = focusableButtons[currentFocusableIndex + 1];
1114
1547
  const childId = nextButton?.dataset.layerNodeId;
1115
1548
  const child = childId
1116
- ? visibleRows.find((visibleRow) => visibleRow.node.id === childId)
1549
+ ? visibleRowsRef.current.find(
1550
+ (visibleRow) => visibleRow.node.id === childId,
1551
+ )
1117
1552
  : null;
1118
1553
  if (child?.ancestorIds.includes(node.id)) {
1119
1554
  focusVisibleButton(currentFocusableIndex + 1);
@@ -1122,7 +1557,7 @@ function LayerRow({
1122
1557
  }
1123
1558
  if (event.key === "ArrowLeft" && hasChildren && isExpanded) {
1124
1559
  event.preventDefault();
1125
- onToggleExpanded(false);
1560
+ onToggleExpanded(node.id, false);
1126
1561
  return;
1127
1562
  }
1128
1563
  if (event.key === "ArrowLeft" && row.ancestorIds.length > 0) {
@@ -1137,10 +1572,18 @@ function LayerRow({
1137
1572
  event.preventDefault();
1138
1573
  return;
1139
1574
  }
1575
+ // Build the full-tree ancestor map once, here at drag start, rather than
1576
+ // on every render — this is O(tree size) so it must not run per-row per
1577
+ // render. It uses the FULL root tree (not visibleRows) so a selected
1578
+ // descendant nested inside a currently-collapsed dragged ancestor is
1579
+ // still correctly recognized as a descendant and excluded from the drag
1580
+ // payload (see L13 / buildAncestorIdMap).
1581
+ const ancestorIdMap = buildAncestorIdMap(rootsRef.current);
1140
1582
  const draggedIds = getDraggedLayerIdsForRows({
1141
- selectedIds,
1583
+ selectedIds: selectedIdsRef.current,
1142
1584
  nodeId: node.id,
1143
- visibleRows,
1585
+ visibleRows: visibleRowsRef.current,
1586
+ ancestorIdMap,
1144
1587
  });
1145
1588
  event.dataTransfer.effectAllowed = "move";
1146
1589
  event.dataTransfer.setData("application/x-design-layer-id", node.id);
@@ -1153,31 +1596,81 @@ function LayerRow({
1153
1596
  activeDragState = { sourceId: node.id, draggedIds };
1154
1597
  };
1155
1598
 
1599
+ // A dragover that is rejected (any early-return path below) must clear a
1600
+ // stale indicator that a PRIOR dragover on this same row left behind — e.g.
1601
+ // hovering near the row edge changes the placement from "inside" to
1602
+ // "before", which canMoveLayer may now reject even though the previous
1603
+ // placement was accepted. Without this, the indicator line/ring lingers on
1604
+ // a row that no longer has a valid drop target.
1605
+ const clearStaleIndicatorForThisRow = () => {
1606
+ if (activeDropIntent?.targetId === node.id) activeDropIntent = null;
1607
+ if (activeDropPlacement !== null) onDropIndicatorChange(null);
1608
+ clearSpringLoadTimer();
1609
+ };
1610
+
1156
1611
  const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
1157
- if (!onMoveLayer || !dragEligible) return;
1612
+ if (!onMoveLayer || !anchorEligible) {
1613
+ clearStaleIndicatorForThisRow();
1614
+ return;
1615
+ }
1158
1616
  // dataTransfer.getData() always returns "" during dragover per spec.
1159
1617
  // Read from the module-level activeDragState set in handleDragStart instead.
1160
- if (!activeDragState) return;
1618
+ if (!activeDragState) {
1619
+ clearStaleIndicatorForThisRow();
1620
+ return;
1621
+ }
1161
1622
  const { sourceId, draggedIds } = activeDragState;
1162
- if (sourceId === node.id) return;
1623
+ if (sourceId === node.id) {
1624
+ clearStaleIndicatorForThisRow();
1625
+ return;
1626
+ }
1163
1627
  const cleanedIds = draggedIds.filter(
1164
1628
  (id) => id && id !== node.id && !id.startsWith("__"),
1165
1629
  );
1166
- if (cleanedIds.length === 0) return;
1630
+ if (cleanedIds.length === 0) {
1631
+ clearStaleIndicatorForThisRow();
1632
+ return;
1633
+ }
1167
1634
  const intent = {
1168
1635
  draggedIds: cleanedIds,
1169
1636
  targetId: node.id,
1170
- placement: dropPlacementForEvent(event, canDropInside),
1637
+ placement: dropPlacementForEvent(
1638
+ event,
1639
+ canDropInside,
1640
+ isExpandedWithChildren,
1641
+ ),
1171
1642
  } satisfies LayersPanelMoveIntent;
1172
- if (canMoveLayer && !canMoveLayer(intent)) return;
1643
+ if (canMoveLayer && !canMoveLayer(intent)) {
1644
+ clearStaleIndicatorForThisRow();
1645
+ return;
1646
+ }
1173
1647
  event.preventDefault();
1174
1648
  event.dataTransfer.dropEffect = "move";
1175
1649
  activeDropIntent = intent;
1176
1650
  onDropIndicatorChange(intent);
1651
+
1652
+ // L20 spring-loaded expand: sustained "inside" hover over a collapsed
1653
+ // container expands it after a delay so the user can drop into nested
1654
+ // children without a separate drop-then-redrag step. Only arm the timer
1655
+ // once per qualifying hover — if a timer is already pending for this row
1656
+ // we leave it running rather than resetting it on every dragover tick.
1657
+ if (
1658
+ intent.placement === "inside" &&
1659
+ hasChildren &&
1660
+ !isExpanded &&
1661
+ springLoadTimerRef.current === null
1662
+ ) {
1663
+ springLoadTimerRef.current = setTimeout(() => {
1664
+ springLoadTimerRef.current = null;
1665
+ onToggleExpanded(node.id, true);
1666
+ }, SPRING_LOAD_DELAY_MS);
1667
+ } else if (intent.placement !== "inside" || !hasChildren || isExpanded) {
1668
+ clearSpringLoadTimer();
1669
+ }
1177
1670
  };
1178
1671
 
1179
1672
  const handleDrop = (event: DragEvent<HTMLDivElement>) => {
1180
- if (!onMoveLayer || !dragEligible) {
1673
+ if (!onMoveLayer || !anchorEligible) {
1181
1674
  onDropIndicatorChange(null);
1182
1675
  return;
1183
1676
  }
@@ -1216,7 +1709,11 @@ function LayerRow({
1216
1709
  : ({
1217
1710
  draggedIds: cleanedIds,
1218
1711
  targetId: node.id,
1219
- placement: dropPlacementForEvent(event, canDropInside),
1712
+ placement: dropPlacementForEvent(
1713
+ event,
1714
+ canDropInside,
1715
+ isExpandedWithChildren,
1716
+ ),
1220
1717
  } satisfies LayersPanelMoveIntent);
1221
1718
  if (!canMoveLayer || canMoveLayer(intent)) {
1222
1719
  onMoveLayer(intent);
@@ -1224,6 +1721,7 @@ function LayerRow({
1224
1721
  }
1225
1722
  activeDropIntent = null;
1226
1723
  onDropIndicatorChange(null);
1724
+ clearSpringLoadTimer();
1227
1725
  };
1228
1726
 
1229
1727
  const showContextMenu =
@@ -1234,7 +1732,7 @@ function LayerRow({
1234
1732
  <ContextMenu>
1235
1733
  <ContextMenuTrigger asChild disabled={!showContextMenu}>
1236
1734
  <div
1237
- ref={rowRef}
1735
+ ref={(element) => registerRowElement(row.rowKey, element)}
1238
1736
  role="treeitem"
1239
1737
  aria-expanded={hasChildren ? isExpanded : undefined}
1240
1738
  aria-level={depth + 1}
@@ -1253,12 +1751,14 @@ function LayerRow({
1253
1751
  return;
1254
1752
  if (activeDropIntent?.targetId === node.id) activeDropIntent = null;
1255
1753
  onDropIndicatorChange(null);
1754
+ clearSpringLoadTimer();
1256
1755
  }}
1257
1756
  onDrop={handleDrop}
1258
1757
  onDragEnd={() => {
1259
1758
  activeDragState = null;
1260
1759
  activeDropIntent = null;
1261
1760
  onDropIndicatorChange(null);
1761
+ clearSpringLoadTimer();
1262
1762
  }}
1263
1763
  onMouseEnter={() => onHoverLayer?.(node.id)}
1264
1764
  onMouseLeave={() => onLeaveLayer?.(node.id)}
@@ -1303,7 +1803,7 @@ function LayerRow({
1303
1803
  aria-label={isExpanded ? labels.collapse : labels.expand}
1304
1804
  onClick={(event) => {
1305
1805
  event.stopPropagation();
1306
- onToggleExpanded(!isExpanded);
1806
+ onToggleExpanded(node.id, !isExpanded);
1307
1807
  }}
1308
1808
  >
1309
1809
  {isExpanded ? (
@@ -1345,6 +1845,7 @@ function LayerRow({
1345
1845
  value={renameDraft}
1346
1846
  onClick={(event) => event.stopPropagation()}
1347
1847
  onChange={(event) => onRenameDraftChange(event.target.value)}
1848
+ onFocus={(event) => event.currentTarget.select()}
1348
1849
  onBlur={() => {
1349
1850
  // Escape sets renameCancelledRef before blur fires; skip commit.
1350
1851
  if (renameCancelledRef.current) {
@@ -1373,7 +1874,7 @@ function LayerRow({
1373
1874
  event.preventDefault();
1374
1875
  event.stopPropagation();
1375
1876
  renameCancelledRef.current = true;
1376
- onCancelRename();
1877
+ onCancelRename(node.id);
1377
1878
  }
1378
1879
  }}
1379
1880
  className="h-6 min-w-0 flex-1 rounded-[4px] border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1.5 text-[12px] text-foreground outline-none"
@@ -1510,7 +2011,7 @@ function LayerRow({
1510
2011
  ) : null}
1511
2012
  </ContextMenu>
1512
2013
  );
1513
- }
2014
+ });
1514
2015
 
1515
2016
  function IconTooltipButton({
1516
2017
  label,
@@ -2019,13 +2520,29 @@ function layerCanDropInside(
2019
2520
  );
2020
2521
  }
2021
2522
 
2022
- function dropPlacementForEvent(
2523
+ /**
2524
+ * L10: for an EXPANDED container that already has children, the bottom hover
2525
+ * zone sits (visually, in the panel) directly above that container's
2526
+ * first-listed child row — not above its next sibling. A plain "after"
2527
+ * placement there would target the position following the container's
2528
+ * ENTIRE subtree (anchor.end in applyMoveNodeEdit), which visually
2529
+ * contradicts the indicator's position between the container and its first
2530
+ * child. Resolve that zone to "inside" instead (still targeting the
2531
+ * container), which inserts at contentEnd — under the L5 reversed-order
2532
+ * convention that is exactly the container's first-panel-row / topmost-paint
2533
+ * child slot, matching what the indicator visually promises.
2534
+ */
2535
+ export function dropPlacementForEvent(
2023
2536
  event: DragEvent<HTMLDivElement>,
2024
2537
  canDropInside: boolean,
2538
+ isExpandedWithChildren = false,
2025
2539
  ): LayersPanelMoveIntent["placement"] {
2026
2540
  const rect = event.currentTarget.getBoundingClientRect();
2027
2541
  const offset = event.clientY - rect.top;
2028
2542
  if (offset < rect.height * 0.3) return "before";
2029
- if (offset > rect.height * 0.7) return "after";
2543
+ if (offset > rect.height * 0.7) {
2544
+ if (canDropInside && isExpandedWithChildren) return "inside";
2545
+ return "after";
2546
+ }
2030
2547
  return canDropInside ? "inside" : "after";
2031
2548
  }