@agent-native/core 0.85.7 → 0.86.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1132) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +133 -1
  3. package/corpus/core/README.md +0 -11
  4. package/corpus/core/docs/content/cloneable-saas.mdx +0 -1
  5. package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
  6. package/corpus/core/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
  7. package/corpus/core/docs/content/locales/ar-SA/sharing.mdx +1 -2
  8. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
  9. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +23 -7
  10. package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +1 -1
  11. package/corpus/core/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
  12. package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
  13. package/corpus/core/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
  14. package/corpus/core/docs/content/locales/de-DE/sharing.mdx +1 -2
  15. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +2 -3
  16. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +23 -7
  17. package/corpus/core/docs/content/locales/de-DE/tracking.mdx +1 -1
  18. package/corpus/core/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
  19. package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
  20. package/corpus/core/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
  21. package/corpus/core/docs/content/locales/es-ES/sharing.mdx +1 -2
  22. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +2 -3
  23. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +23 -7
  24. package/corpus/core/docs/content/locales/es-ES/tracking.mdx +1 -1
  25. package/corpus/core/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
  26. package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
  27. package/corpus/core/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
  28. package/corpus/core/docs/content/locales/fr-FR/sharing.mdx +1 -2
  29. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
  30. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +23 -7
  31. package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +1 -1
  32. package/corpus/core/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
  33. package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
  34. package/corpus/core/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
  35. package/corpus/core/docs/content/locales/hi-IN/sharing.mdx +1 -2
  36. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
  37. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +23 -7
  38. package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +1 -1
  39. package/corpus/core/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
  40. package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
  41. package/corpus/core/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
  42. package/corpus/core/docs/content/locales/ja-JP/sharing.mdx +1 -2
  43. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
  44. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +23 -7
  45. package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +1 -1
  46. package/corpus/core/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
  47. package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
  48. package/corpus/core/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
  49. package/corpus/core/docs/content/locales/ko-KR/sharing.mdx +1 -2
  50. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
  51. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +23 -7
  52. package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +1 -1
  53. package/corpus/core/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
  54. package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
  55. package/corpus/core/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
  56. package/corpus/core/docs/content/locales/pt-BR/sharing.mdx +1 -2
  57. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
  58. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +23 -7
  59. package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +1 -1
  60. package/corpus/core/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
  61. package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
  62. package/corpus/core/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
  63. package/corpus/core/docs/content/locales/zh-CN/sharing.mdx +1 -2
  64. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
  65. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +23 -7
  66. package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +1 -1
  67. package/corpus/core/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
  68. package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
  69. package/corpus/core/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
  70. package/corpus/core/docs/content/locales/zh-TW/sharing.mdx +1 -2
  71. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
  72. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +23 -7
  73. package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +1 -1
  74. package/corpus/core/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
  75. package/corpus/core/docs/content/plan-plugin.mdx +12 -11
  76. package/corpus/core/docs/content/sharing.mdx +1 -2
  77. package/corpus/core/docs/content/skills-guide.mdx +4 -3
  78. package/corpus/core/docs/content/template-plan.mdx +37 -6
  79. package/corpus/core/docs/content/tracking.mdx +1 -1
  80. package/corpus/core/docs/content/workspace-connections.mdx +1 -1
  81. package/corpus/core/package.json +7 -6
  82. package/corpus/core/src/agent/run-store.ts +1 -1
  83. package/corpus/core/src/chat-threads/store.ts +73 -11
  84. package/corpus/core/src/checkpoints/store.ts +13 -2
  85. package/corpus/core/src/cli/create.ts +3 -4
  86. package/corpus/core/src/cli/design-connect.ts +469 -9
  87. package/corpus/core/src/cli/index.ts +13 -0
  88. package/corpus/core/src/cli/plan-local.ts +281 -24
  89. package/corpus/core/src/cli/skills.ts +177 -21
  90. package/corpus/core/src/cli/templates-meta.ts +0 -13
  91. package/corpus/core/src/cli/visualize-repo.ts +715 -0
  92. package/corpus/core/src/client/AgentPanel.tsx +10 -0
  93. package/corpus/core/src/client/AssistantChat.tsx +194 -21
  94. package/corpus/core/src/client/ErrorBoundary.tsx +67 -12
  95. package/corpus/core/src/client/ErrorReportActions.tsx +69 -0
  96. package/corpus/core/src/client/FeedbackButton.tsx +8 -4
  97. package/corpus/core/src/client/agent-chat-adapter.ts +6 -5
  98. package/corpus/core/src/client/app-providers.tsx +2 -2
  99. package/corpus/core/src/client/collab/index.ts +31 -0
  100. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +16 -1
  101. package/corpus/core/src/client/components/PresenceBar.tsx +14 -1
  102. package/corpus/core/src/client/components/RecentEditHighlights.tsx +219 -0
  103. package/corpus/core/src/client/components/RemoteSelectionRings.tsx +40 -6
  104. package/corpus/core/src/client/db-admin/DbAdminPage.tsx +41 -13
  105. package/corpus/core/src/client/db-admin/SqlEditor.tsx +10 -3
  106. package/corpus/core/src/client/db-admin/TableEditor.tsx +12 -5
  107. package/corpus/core/src/client/db-admin/useAgentSync.ts +10 -3
  108. package/corpus/core/src/client/db-admin/useDbAdmin.ts +65 -16
  109. package/corpus/core/src/client/error-reporting.ts +100 -0
  110. package/corpus/core/src/client/index.ts +59 -0
  111. package/corpus/core/src/client/org/OrgSwitcher.tsx +0 -4
  112. package/corpus/core/src/client/org/RequireActiveOrg.tsx +12 -0
  113. package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +49 -32
  114. package/corpus/core/src/client/rich-markdown-editor/index.ts +6 -0
  115. package/corpus/core/src/client/rich-markdown-editor/surgical-apply.ts +192 -0
  116. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +59 -8
  117. package/corpus/core/src/client/session-replay.ts +181 -15
  118. package/corpus/core/src/client/sse-event-processor.ts +90 -1
  119. package/corpus/core/src/client/use-action.ts +123 -25
  120. package/corpus/core/src/client/use-agent-engine-configured.ts +7 -1
  121. package/corpus/core/src/client/use-chat-threads.ts +18 -7
  122. package/corpus/core/src/client/use-db-sync.ts +104 -19
  123. package/corpus/core/src/client/visual-style-controls.tsx +1088 -0
  124. package/corpus/core/src/collab/agent-presence.ts +191 -62
  125. package/corpus/core/src/collab/awareness.ts +83 -6
  126. package/corpus/core/src/collab/client.ts +89 -146
  127. package/corpus/core/src/collab/index.ts +34 -0
  128. package/corpus/core/src/collab/recent-edits.ts +171 -0
  129. package/corpus/core/src/collab/undo.ts +432 -0
  130. package/corpus/core/src/collab/ydoc-manager.ts +68 -0
  131. package/corpus/core/src/connections/catalog.ts +2 -3
  132. package/corpus/core/src/db/create-get-db.ts +86 -1
  133. package/corpus/core/src/db/ensure-additive-columns.ts +430 -0
  134. package/corpus/core/src/db/index.ts +6 -0
  135. package/corpus/core/src/db/migrations.ts +236 -35
  136. package/corpus/core/src/db-admin/index.ts +26 -0
  137. package/corpus/core/src/db-admin/operations.ts +87 -49
  138. package/corpus/core/src/jobs/scheduler.ts +17 -14
  139. package/corpus/core/src/localization/default-messages.ts +4 -0
  140. package/corpus/core/src/observability/traces.ts +182 -1
  141. package/corpus/core/src/org/context.ts +79 -2
  142. package/corpus/core/src/server/agent-chat-plugin.ts +10 -6
  143. package/corpus/core/src/server/auth-marketing.ts +0 -11
  144. package/corpus/core/src/server/auth.ts +11 -4
  145. package/corpus/core/src/server/builder-design-systems.ts +328 -9
  146. package/corpus/core/src/server/collab-plugin.ts +56 -13
  147. package/corpus/core/src/server/design-token-utils.ts +1 -1
  148. package/corpus/core/src/server/fusion-app.ts +447 -0
  149. package/corpus/core/src/server/index.ts +28 -0
  150. package/corpus/core/src/server/onboarding-html.ts +3 -4
  151. package/corpus/core/src/server/poll-events.ts +14 -2
  152. package/corpus/core/src/server/poll.ts +219 -12
  153. package/corpus/core/src/server/schema-prompt.ts +28 -9
  154. package/corpus/core/src/settings/store.ts +33 -2
  155. package/corpus/core/src/sharing/actions/set-resource-visibility.ts +2 -0
  156. package/corpus/core/src/sharing/actions/share-resource.ts +3 -0
  157. package/corpus/core/src/sharing/actions/unshare-resource.ts +2 -0
  158. package/corpus/core/src/styles/agent-native.css +0 -5
  159. package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  160. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  161. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  162. package/corpus/core/src/templates/default/app/root.tsx +47 -0
  163. package/corpus/core/src/templates/default/package.json +3 -3
  164. package/corpus/core/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  165. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  166. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  167. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  168. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  169. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  170. package/corpus/core/src/tracking/providers.ts +22 -1
  171. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +15 -0
  172. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +20 -5
  173. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +8 -0
  174. package/corpus/templates/analytics/AGENTS.md +35 -6
  175. package/corpus/templates/analytics/actions/delete-db-admin-connection.ts +19 -0
  176. package/corpus/templates/analytics/actions/list-db-admin-connections.ts +18 -0
  177. package/corpus/templates/analytics/actions/navigate.ts +28 -4
  178. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
  179. package/corpus/templates/analytics/actions/save-db-admin-connection.ts +33 -0
  180. package/corpus/templates/analytics/actions/view-screen.ts +65 -0
  181. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +23 -1
  182. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  183. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -0
  184. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +16 -0
  185. package/corpus/templates/analytics/app/i18n/zh-TW.ts +36 -1
  186. package/corpus/templates/analytics/app/i18n-data.ts +361 -10
  187. package/corpus/templates/analytics/app/pages/Agents.tsx +512 -0
  188. package/corpus/templates/analytics/app/pages/DataSources.tsx +11 -1
  189. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +76 -40
  190. package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +5 -0
  191. package/corpus/templates/analytics/app/routes/agents.tsx +10 -0
  192. package/corpus/templates/analytics/changelog/2026-07-03-agent-admin-tools-page.md +6 -0
  193. package/corpus/templates/analytics/changelog/2026-07-03-agent-llm-observability-dashboard.md +6 -0
  194. package/corpus/templates/analytics/changelog/2026-07-03-connect-other-app-databases-from-analytics.md +6 -0
  195. package/corpus/templates/analytics/changelog/2026-07-03-session-recordings-now-include-response-bodies-for-5xx-error.md +6 -0
  196. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +34 -0
  197. package/corpus/templates/analytics/docs/schemas/tracked-events.md +6 -0
  198. package/corpus/templates/analytics/package.json +3 -3
  199. package/corpus/templates/analytics/seeds/dashboards/agent-observability-llm.json +173 -0
  200. package/corpus/templates/analytics/server/db/schema.ts +27 -0
  201. package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +23 -5
  202. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +58 -13
  203. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +23 -0
  204. package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +5 -0
  205. package/corpus/templates/analytics/server/lib/db-admin-connections.ts +458 -0
  206. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +120 -8
  207. package/corpus/templates/analytics/server/lib/session-replay.ts +3 -2
  208. package/corpus/templates/analytics/server/plugins/db.ts +128 -2
  209. package/corpus/templates/analytics/server/routes/[base]/_agent-native/analytics-db-admin/[...path].ts +1 -0
  210. package/corpus/templates/analytics/server/routes/_agent-native/analytics-db-admin/[...path].ts +160 -0
  211. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +45 -1
  212. package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +10 -0
  213. package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +82 -19
  214. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +15 -0
  215. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +8 -0
  216. package/corpus/templates/assets/package.json +3 -3
  217. package/corpus/templates/assets/server/plugins/db.ts +63 -2
  218. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +15 -0
  219. package/corpus/templates/brain/package.json +3 -3
  220. package/corpus/templates/brain/server/plugins/db.ts +75 -2
  221. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +15 -0
  222. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +8 -0
  223. package/corpus/templates/calendar/package.json +3 -3
  224. package/corpus/templates/calendar/server/plugins/db.ts +64 -2
  225. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +15 -0
  226. package/corpus/templates/chat/package.json +3 -3
  227. package/corpus/templates/clips/actions/finalize-recording.ts +50 -40
  228. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
  229. package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -3
  230. package/corpus/templates/clips/changelog/2026-07-03-mobile-recording-pages-keep-videos-in-a-16-9-player-with-act.md +6 -0
  231. package/corpus/templates/clips/package.json +3 -3
  232. package/corpus/templates/clips/server/lib/recording-upload-state.ts +56 -0
  233. package/corpus/templates/clips/server/plugins/db.ts +68 -1
  234. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +27 -3
  235. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +15 -0
  236. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +8 -0
  237. package/corpus/templates/content/actions/edit-document.ts +48 -1
  238. package/corpus/templates/content/actions/list-documents.ts +101 -94
  239. package/corpus/templates/content/actions/search-documents.ts +19 -3
  240. package/corpus/templates/content/actions/update-document.ts +54 -1
  241. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -7
  242. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +97 -2
  243. package/corpus/templates/content/app/root.tsx +9 -0
  244. package/corpus/templates/content/changelog/2026-07-03-agent-document-edits-now-appear-as-a-live-collaborator-prese.md +6 -0
  245. package/corpus/templates/content/changelog/2026-07-03-document-search-and-list-views-load-faster-on-large-workspac.md +6 -0
  246. package/corpus/templates/content/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  247. package/corpus/templates/content/package.json +3 -3
  248. package/corpus/templates/content/server/plugins/db.ts +57 -1
  249. package/corpus/templates/design/.agents/skills/full-app-build/SKILL.md +140 -0
  250. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +20 -0
  251. package/corpus/templates/design/AGENTS.md +63 -19
  252. package/corpus/templates/design/actions/add-fusion-screens.ts +91 -0
  253. package/corpus/templates/design/actions/apply-a11y-fix.ts +5 -1
  254. package/corpus/templates/design/actions/apply-component-prop-edit.ts +10 -2
  255. package/corpus/templates/design/actions/apply-fusion-edits.ts +161 -0
  256. package/corpus/templates/design/actions/apply-visual-edit.ts +34 -1
  257. package/corpus/templates/design/actions/create-component.ts +5 -1
  258. package/corpus/templates/design/actions/create-fusion-app.ts +202 -0
  259. package/corpus/templates/design/actions/deploy-fusion-app.ts +123 -0
  260. package/corpus/templates/design/actions/edit-design.ts +13 -16
  261. package/corpus/templates/design/actions/get-design-system.ts +21 -0
  262. package/corpus/templates/design/actions/get-fusion-deploy-status.ts +86 -0
  263. package/corpus/templates/design/actions/index-design-system-with-builder.ts +14 -37
  264. package/corpus/templates/design/actions/list-fusion-edits.ts +96 -0
  265. package/corpus/templates/design/actions/list-local-files.ts +133 -0
  266. package/corpus/templates/design/actions/push-fusion-app.ts +62 -0
  267. package/corpus/templates/design/actions/queue-fusion-edit.ts +112 -0
  268. package/corpus/templates/design/actions/read-local-file.ts +139 -0
  269. package/corpus/templates/design/actions/send-fusion-message.ts +69 -0
  270. package/corpus/templates/design/actions/sync-fusion-app.ts +196 -0
  271. package/corpus/templates/design/actions/write-local-file.ts +115 -14
  272. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +12 -0
  273. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +379 -0
  274. package/corpus/templates/design/app/components/design/EditPanel.tsx +149 -73
  275. package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +328 -0
  276. package/corpus/templates/design/app/components/design/LayersPanel.tsx +148 -30
  277. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +1 -1
  278. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +567 -6
  279. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -98
  280. package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +85 -0
  281. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +466 -0
  282. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +114 -0
  283. package/corpus/templates/design/app/components/design/code-workbench/commands.ts +339 -0
  284. package/corpus/templates/design/app/components/design/code-workbench/editor/Breadcrumbs.tsx +42 -0
  285. package/corpus/templates/design/app/components/design/code-workbench/editor/BufferSync.tsx +128 -0
  286. package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +372 -0
  287. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +268 -0
  288. package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +208 -0
  289. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +61 -0
  290. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme-palette.ts +191 -0
  291. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme.ts +29 -0
  292. package/corpus/templates/design/app/components/design/code-workbench/editor/status-bar-lang.ts +21 -0
  293. package/corpus/templates/design/app/components/design/code-workbench/editor/tab-drag.ts +27 -0
  294. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +161 -0
  295. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +555 -0
  296. package/corpus/templates/design/app/components/design/code-workbench/explorer/file-icons.tsx +115 -0
  297. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +65 -0
  298. package/corpus/templates/design/app/components/design/code-workbench/format/prettier-format.ts +182 -0
  299. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +133 -0
  300. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +563 -0
  301. package/corpus/templates/design/app/components/design/code-workbench/quickinput/fuzzy.ts +170 -0
  302. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +508 -0
  303. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +280 -0
  304. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +782 -0
  305. package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +39 -0
  306. package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +196 -0
  307. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +177 -0
  308. package/corpus/templates/design/app/components/design/code-workbench/workspace/tree.ts +110 -0
  309. package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +103 -0
  310. package/corpus/templates/design/app/components/design/code-workbench-theme.ts +68 -0
  311. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +127 -10
  312. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +52 -29
  313. package/corpus/templates/design/app/components/design/inspector/index.ts +9 -0
  314. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +56 -0
  315. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +80 -1
  316. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +6 -16
  317. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +6 -0
  318. package/corpus/templates/design/app/i18n/zh-TW.ts +17 -15
  319. package/corpus/templates/design/app/i18n-data.ts +185 -160
  320. package/corpus/templates/design/app/pages/DesignEditor.tsx +1474 -175
  321. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +32 -14
  322. package/corpus/templates/design/app/pages/Index.tsx +90 -17
  323. package/corpus/templates/design/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  324. package/corpus/templates/design/changelog/2026-07-03-the-ai-now-edits-like-a-collaborator-on-canvas-its-cursor-gl.md +6 -0
  325. package/corpus/templates/design/changelog/2026-07-03-the-code-editor-is-easier-to-use-with-top-sidebar-tabs-clear.md +6 -0
  326. package/corpus/templates/design/changelog/2026-07-03-the-code-panel-is-now-a-full-vs-code-style-editor-with-a-fil.md +6 -0
  327. package/corpus/templates/design/package.json +4 -3
  328. package/corpus/templates/design/server/db/schema.ts +31 -0
  329. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +16 -106
  330. package/corpus/templates/design/server/lib/fusion-screens.ts +246 -0
  331. package/corpus/templates/design/server/plugins/db.ts +89 -2
  332. package/corpus/templates/design/shared/collab-selection.ts +52 -0
  333. package/corpus/templates/design/shared/full-app.ts +126 -0
  334. package/corpus/templates/design/shared/pen-path.ts +296 -0
  335. package/corpus/templates/dispatch/package.json +3 -3
  336. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +15 -0
  337. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +8 -0
  338. package/corpus/templates/forms/package.json +3 -3
  339. package/corpus/templates/forms/server/plugins/db.ts +62 -2
  340. package/corpus/templates/macros/package.json +3 -3
  341. package/corpus/templates/macros/server/plugins/db.ts +63 -2
  342. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +15 -0
  343. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +8 -0
  344. package/corpus/templates/mail/app/root.tsx +60 -1
  345. package/corpus/templates/mail/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  346. package/corpus/templates/mail/package.json +3 -3
  347. package/corpus/templates/mail/server/lib/contact-frequency.ts +42 -15
  348. package/corpus/templates/mail/server/plugins/db.ts +63 -2
  349. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +15 -0
  350. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +9 -3
  351. package/corpus/templates/plan/.agents/skills/visualize-repo/SKILL.md +92 -0
  352. package/corpus/templates/plan/actions/export-visual-plan.ts +5 -0
  353. package/corpus/templates/plan/actions/get-local-plan-folder.ts +4 -22
  354. package/corpus/templates/plan/actions/get-plan-version.ts +6 -16
  355. package/corpus/templates/plan/actions/list-plan-versions.ts +48 -20
  356. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +41 -1
  357. package/corpus/templates/plan/actions/promote-local-plan-folder.ts +4 -22
  358. package/corpus/templates/plan/actions/update-local-plan-comments.ts +4 -2
  359. package/corpus/templates/plan/actions/update-local-plan-folder.ts +4 -22
  360. package/corpus/templates/plan/actions/update-visual-plan.ts +60 -1
  361. package/corpus/templates/plan/agent-native.app-skill.json +10 -0
  362. package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +138 -39
  363. package/corpus/templates/plan/app/components/editor/usePlanUndoStack.ts +165 -23
  364. package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +80 -4
  365. package/corpus/templates/plan/app/global.css +11 -9
  366. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +10 -1
  367. package/corpus/templates/plan/app/hooks/use-plan-presence.ts +66 -0
  368. package/corpus/templates/plan/app/i18n/ar-SA.ts +3 -0
  369. package/corpus/templates/plan/app/i18n/de-DE.ts +4 -0
  370. package/corpus/templates/plan/app/i18n/en-US.ts +4 -0
  371. package/corpus/templates/plan/app/i18n/es-ES.ts +4 -0
  372. package/corpus/templates/plan/app/i18n/fr-FR.ts +4 -0
  373. package/corpus/templates/plan/app/i18n/hi-IN.ts +3 -0
  374. package/corpus/templates/plan/app/i18n/ja-JP.ts +4 -0
  375. package/corpus/templates/plan/app/i18n/ko-KR.ts +4 -0
  376. package/corpus/templates/plan/app/i18n/pt-BR.ts +4 -0
  377. package/corpus/templates/plan/app/i18n/zh-CN.ts +3 -0
  378. package/corpus/templates/plan/app/i18n/zh-TW.ts +3 -0
  379. package/corpus/templates/plan/app/pages/PlansPage.tsx +289 -66
  380. package/corpus/templates/plan/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
  381. package/corpus/templates/plan/changelog/2026-07-03-local-visual-plans-and-recaps-can-save-comments-without-sign.md +6 -0
  382. package/corpus/templates/plan/changelog/2026-07-03-plan-tabs-render-nested-text-instead-of-raw-json.md +6 -0
  383. package/corpus/templates/plan/changelog/2026-07-03-plans-are-now-fully-real-time-collaborative-multiple-people-.md +6 -0
  384. package/corpus/templates/plan/changelog/2026-07-03-plans-open-and-refresh-faster-especially-long-lived-plans-wi.md +6 -0
  385. package/corpus/templates/plan/changelog/2026-07-03-undo-now-survives-agent-edits-cmd-z-keeps-working-on-your-ow.md +6 -0
  386. package/corpus/templates/plan/package.json +5 -3
  387. package/corpus/templates/plan/server/db/schema.ts +12 -0
  388. package/corpus/templates/plan/server/lib/local-plan-kind.ts +25 -0
  389. package/corpus/templates/plan/server/lib/plan-versions.ts +83 -1
  390. package/corpus/templates/plan/server/lib/public-action-paths.ts +0 -2
  391. package/corpus/templates/plan/server/plans.ts +26 -3
  392. package/corpus/templates/plan/server/plugins/db.ts +105 -2
  393. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +8 -4
  394. package/corpus/templates/slides/AGENTS.md +11 -3
  395. package/corpus/templates/slides/actions/_agent-presence.ts +48 -0
  396. package/corpus/templates/slides/actions/add-slide.ts +16 -18
  397. package/corpus/templates/slides/actions/get-design-system.ts +21 -0
  398. package/corpus/templates/slides/actions/import-file.ts +15 -7
  399. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +14 -37
  400. package/corpus/templates/slides/actions/patch-deck.ts +4 -2
  401. package/corpus/templates/slides/actions/update-slide.ts +114 -77
  402. package/corpus/templates/slides/actions/view-screen.ts +39 -0
  403. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +32 -8
  404. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +47 -3
  405. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -39
  406. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +507 -72
  407. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +346 -0
  408. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +56 -6
  409. package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +46 -96
  410. package/corpus/templates/slides/app/components/layout/Layout.tsx +6 -1
  411. package/corpus/templates/slides/app/components/visual-editor/SaveStatusIndicator.tsx +8 -1
  412. package/corpus/templates/slides/app/context/DeckContext.tsx +589 -176
  413. package/corpus/templates/slides/app/hooks/use-deck-presence.ts +84 -42
  414. package/corpus/templates/slides/app/i18n/ar-SA.ts +54 -7
  415. package/corpus/templates/slides/app/i18n/de-DE.ts +53 -8
  416. package/corpus/templates/slides/app/i18n/en-US.ts +54 -7
  417. package/corpus/templates/slides/app/i18n/es-ES.ts +53 -8
  418. package/corpus/templates/slides/app/i18n/fr-FR.ts +53 -8
  419. package/corpus/templates/slides/app/i18n/hi-IN.ts +54 -7
  420. package/corpus/templates/slides/app/i18n/ja-JP.ts +53 -8
  421. package/corpus/templates/slides/app/i18n/ko-KR.ts +53 -7
  422. package/corpus/templates/slides/app/i18n/pt-BR.ts +53 -8
  423. package/corpus/templates/slides/app/i18n/zh-CN.ts +54 -7
  424. package/corpus/templates/slides/app/i18n/zh-TW.ts +54 -7
  425. package/corpus/templates/slides/app/lib/canvas-zoom.ts +16 -1
  426. package/corpus/templates/slides/app/pages/DeckEditor.tsx +28 -16
  427. package/corpus/templates/slides/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
  428. package/corpus/templates/slides/changelog/2026-07-03-figma-style-slide-editing.md +6 -0
  429. package/corpus/templates/slides/changelog/2026-07-03-see-the-ai-working-with-you-when-the-agent-edits-a-slide-it-.md +6 -0
  430. package/corpus/templates/slides/changelog/2026-07-03-slide-editing-now-keeps-thumbnails-speaker-notes-styling-and.md +6 -0
  431. package/corpus/templates/slides/changelog/2026-07-03-undo-redo-is-now-precise-and-safe-with-collaborators-it-only.md +6 -0
  432. package/corpus/templates/slides/package.json +3 -3
  433. package/corpus/templates/slides/server/handlers/decks.ts +31 -3
  434. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +16 -104
  435. package/corpus/templates/slides/server/plugins/db.ts +65 -2
  436. package/corpus/templates/videos/netlify.toml +1 -12
  437. package/dist/agent/run-store.js +1 -1
  438. package/dist/agent/run-store.js.map +1 -1
  439. package/dist/chat-threads/store.d.ts +9 -1
  440. package/dist/chat-threads/store.d.ts.map +1 -1
  441. package/dist/chat-threads/store.js +63 -11
  442. package/dist/chat-threads/store.js.map +1 -1
  443. package/dist/checkpoints/store.d.ts.map +1 -1
  444. package/dist/checkpoints/store.js +10 -2
  445. package/dist/checkpoints/store.js.map +1 -1
  446. package/dist/cli/create.d.ts.map +1 -1
  447. package/dist/cli/create.js +3 -5
  448. package/dist/cli/create.js.map +1 -1
  449. package/dist/cli/design-connect.d.ts +49 -1
  450. package/dist/cli/design-connect.d.ts.map +1 -1
  451. package/dist/cli/design-connect.js +414 -9
  452. package/dist/cli/design-connect.js.map +1 -1
  453. package/dist/cli/index.js +12 -0
  454. package/dist/cli/index.js.map +1 -1
  455. package/dist/cli/plan-local.d.ts +24 -0
  456. package/dist/cli/plan-local.d.ts.map +1 -1
  457. package/dist/cli/plan-local.js +188 -20
  458. package/dist/cli/plan-local.js.map +1 -1
  459. package/dist/cli/skills.d.ts +3 -1
  460. package/dist/cli/skills.d.ts.map +1 -1
  461. package/dist/cli/skills.js +173 -21
  462. package/dist/cli/skills.js.map +1 -1
  463. package/dist/cli/templates-meta.d.ts.map +1 -1
  464. package/dist/cli/templates-meta.js +0 -14
  465. package/dist/cli/templates-meta.js.map +1 -1
  466. package/dist/cli/visualize-repo.d.ts +49 -0
  467. package/dist/cli/visualize-repo.d.ts.map +1 -0
  468. package/dist/cli/visualize-repo.js +561 -0
  469. package/dist/cli/visualize-repo.js.map +1 -0
  470. package/dist/client/AgentPanel.d.ts.map +1 -1
  471. package/dist/client/AgentPanel.js +2 -1
  472. package/dist/client/AgentPanel.js.map +1 -1
  473. package/dist/client/AssistantChat.d.ts.map +1 -1
  474. package/dist/client/AssistantChat.js +175 -18
  475. package/dist/client/AssistantChat.js.map +1 -1
  476. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  477. package/dist/client/ErrorBoundary.js +37 -2
  478. package/dist/client/ErrorBoundary.js.map +1 -1
  479. package/dist/client/ErrorReportActions.d.ts +13 -0
  480. package/dist/client/ErrorReportActions.d.ts.map +1 -0
  481. package/dist/client/ErrorReportActions.js +14 -0
  482. package/dist/client/ErrorReportActions.js.map +1 -0
  483. package/dist/client/FeedbackButton.d.ts +3 -1
  484. package/dist/client/FeedbackButton.d.ts.map +1 -1
  485. package/dist/client/FeedbackButton.js +6 -5
  486. package/dist/client/FeedbackButton.js.map +1 -1
  487. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  488. package/dist/client/agent-chat-adapter.js +6 -5
  489. package/dist/client/agent-chat-adapter.js.map +1 -1
  490. package/dist/client/app-providers.d.ts +2 -2
  491. package/dist/client/app-providers.js.map +1 -1
  492. package/dist/client/collab/index.d.ts +4 -1
  493. package/dist/client/collab/index.d.ts.map +1 -1
  494. package/dist/client/collab/index.js +3 -0
  495. package/dist/client/collab/index.js.map +1 -1
  496. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  497. package/dist/client/components/LiveCursorOverlay.js +11 -3
  498. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  499. package/dist/client/components/PresenceBar.d.ts.map +1 -1
  500. package/dist/client/components/PresenceBar.js +6 -1
  501. package/dist/client/components/PresenceBar.js.map +1 -1
  502. package/dist/client/components/RecentEditHighlights.d.ts +43 -0
  503. package/dist/client/components/RecentEditHighlights.d.ts.map +1 -0
  504. package/dist/client/components/RecentEditHighlights.js +146 -0
  505. package/dist/client/components/RecentEditHighlights.js.map +1 -0
  506. package/dist/client/components/RemoteSelectionRings.d.ts +9 -0
  507. package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
  508. package/dist/client/components/RemoteSelectionRings.js +22 -8
  509. package/dist/client/components/RemoteSelectionRings.js.map +1 -1
  510. package/dist/client/db-admin/DbAdminPage.d.ts +9 -1
  511. package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -1
  512. package/dist/client/db-admin/DbAdminPage.js +17 -13
  513. package/dist/client/db-admin/DbAdminPage.js.map +1 -1
  514. package/dist/client/db-admin/SqlEditor.d.ts +3 -1
  515. package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
  516. package/dist/client/db-admin/SqlEditor.js +3 -3
  517. package/dist/client/db-admin/SqlEditor.js.map +1 -1
  518. package/dist/client/db-admin/TableEditor.d.ts +3 -1
  519. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  520. package/dist/client/db-admin/TableEditor.js +6 -6
  521. package/dist/client/db-admin/TableEditor.js.map +1 -1
  522. package/dist/client/db-admin/useAgentSync.d.ts +3 -2
  523. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  524. package/dist/client/db-admin/useAgentSync.js +8 -4
  525. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  526. package/dist/client/db-admin/useDbAdmin.d.ts +11 -7
  527. package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -1
  528. package/dist/client/db-admin/useDbAdmin.js +45 -20
  529. package/dist/client/db-admin/useDbAdmin.js.map +1 -1
  530. package/dist/client/error-reporting.d.ts +19 -0
  531. package/dist/client/error-reporting.d.ts.map +1 -0
  532. package/dist/client/error-reporting.js +71 -0
  533. package/dist/client/error-reporting.js.map +1 -0
  534. package/dist/client/index.d.ts +8 -1
  535. package/dist/client/index.d.ts.map +1 -1
  536. package/dist/client/index.js +9 -0
  537. package/dist/client/index.js.map +1 -1
  538. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  539. package/dist/client/org/OrgSwitcher.js +1 -3
  540. package/dist/client/org/OrgSwitcher.js.map +1 -1
  541. package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
  542. package/dist/client/org/RequireActiveOrg.js +2 -1
  543. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  544. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
  545. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  546. package/dist/client/rich-markdown-editor/SharedRichEditor.js +8 -2
  547. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  548. package/dist/client/rich-markdown-editor/index.d.ts +1 -0
  549. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  550. package/dist/client/rich-markdown-editor/index.js +1 -0
  551. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  552. package/dist/client/rich-markdown-editor/surgical-apply.d.ts +66 -0
  553. package/dist/client/rich-markdown-editor/surgical-apply.d.ts.map +1 -0
  554. package/dist/client/rich-markdown-editor/surgical-apply.js +147 -0
  555. package/dist/client/rich-markdown-editor/surgical-apply.js.map +1 -0
  556. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +18 -2
  557. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  558. package/dist/client/rich-markdown-editor/useCollabReconcile.js +39 -8
  559. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  560. package/dist/client/session-replay.d.ts +10 -1
  561. package/dist/client/session-replay.d.ts.map +1 -1
  562. package/dist/client/session-replay.js +134 -7
  563. package/dist/client/session-replay.js.map +1 -1
  564. package/dist/client/sse-event-processor.d.ts.map +1 -1
  565. package/dist/client/sse-event-processor.js +81 -1
  566. package/dist/client/sse-event-processor.js.map +1 -1
  567. package/dist/client/use-action.d.ts +24 -0
  568. package/dist/client/use-action.d.ts.map +1 -1
  569. package/dist/client/use-action.js +109 -27
  570. package/dist/client/use-action.js.map +1 -1
  571. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  572. package/dist/client/use-agent-engine-configured.js +7 -1
  573. package/dist/client/use-agent-engine-configured.js.map +1 -1
  574. package/dist/client/use-chat-threads.d.ts.map +1 -1
  575. package/dist/client/use-chat-threads.js +11 -3
  576. package/dist/client/use-chat-threads.js.map +1 -1
  577. package/dist/client/use-db-sync.d.ts +36 -0
  578. package/dist/client/use-db-sync.d.ts.map +1 -1
  579. package/dist/client/use-db-sync.js +68 -12
  580. package/dist/client/use-db-sync.js.map +1 -1
  581. package/dist/client/visual-style-controls.d.ts +90 -0
  582. package/dist/client/visual-style-controls.d.ts.map +1 -0
  583. package/dist/client/visual-style-controls.js +499 -0
  584. package/dist/client/visual-style-controls.js.map +1 -0
  585. package/dist/collab/agent-presence.d.ts +51 -5
  586. package/dist/collab/agent-presence.d.ts.map +1 -1
  587. package/dist/collab/agent-presence.js +144 -58
  588. package/dist/collab/agent-presence.js.map +1 -1
  589. package/dist/collab/awareness-store.d.ts +32 -0
  590. package/dist/collab/awareness-store.d.ts.map +1 -0
  591. package/dist/collab/awareness-store.js +0 -0
  592. package/dist/collab/awareness-store.js.map +1 -0
  593. package/dist/collab/awareness.d.ts +12 -1
  594. package/dist/collab/awareness.d.ts.map +1 -1
  595. package/dist/collab/awareness.js +53 -5
  596. package/dist/collab/awareness.js.map +1 -1
  597. package/dist/collab/client.d.ts +2 -0
  598. package/dist/collab/client.d.ts.map +1 -1
  599. package/dist/collab/client.js +79 -119
  600. package/dist/collab/client.js.map +1 -1
  601. package/dist/collab/index.d.ts +3 -1
  602. package/dist/collab/index.d.ts.map +1 -1
  603. package/dist/collab/index.js +5 -1
  604. package/dist/collab/index.js.map +1 -1
  605. package/dist/collab/recent-edits.d.ts +87 -0
  606. package/dist/collab/recent-edits.d.ts.map +1 -0
  607. package/dist/collab/recent-edits.js +116 -0
  608. package/dist/collab/recent-edits.js.map +1 -0
  609. package/dist/collab/routes.d.ts +1 -1
  610. package/dist/collab/undo.d.ts +121 -0
  611. package/dist/collab/undo.d.ts.map +1 -0
  612. package/dist/collab/undo.js +244 -0
  613. package/dist/collab/undo.js.map +1 -0
  614. package/dist/collab/ydoc-manager.d.ts +10 -0
  615. package/dist/collab/ydoc-manager.d.ts.map +1 -1
  616. package/dist/collab/ydoc-manager.js +57 -0
  617. package/dist/collab/ydoc-manager.js.map +1 -1
  618. package/dist/connections/catalog.d.ts +2 -2
  619. package/dist/connections/catalog.d.ts.map +1 -1
  620. package/dist/connections/catalog.js +1 -1
  621. package/dist/connections/catalog.js.map +1 -1
  622. package/dist/db/create-get-db.d.ts +22 -0
  623. package/dist/db/create-get-db.d.ts.map +1 -1
  624. package/dist/db/create-get-db.js +73 -1
  625. package/dist/db/create-get-db.js.map +1 -1
  626. package/dist/db/ensure-additive-columns.d.ts +93 -0
  627. package/dist/db/ensure-additive-columns.d.ts.map +1 -0
  628. package/dist/db/ensure-additive-columns.js +355 -0
  629. package/dist/db/ensure-additive-columns.js.map +1 -0
  630. package/dist/db/index.d.ts +1 -0
  631. package/dist/db/index.d.ts.map +1 -1
  632. package/dist/db/index.js +1 -0
  633. package/dist/db/index.js.map +1 -1
  634. package/dist/db/migrations.d.ts +70 -0
  635. package/dist/db/migrations.d.ts.map +1 -1
  636. package/dist/db/migrations.js +196 -33
  637. package/dist/db/migrations.js.map +1 -1
  638. package/dist/db-admin/index.d.ts +3 -0
  639. package/dist/db-admin/index.d.ts.map +1 -0
  640. package/dist/db-admin/index.js +2 -0
  641. package/dist/db-admin/index.js.map +1 -0
  642. package/dist/db-admin/operations.d.ts +26 -5
  643. package/dist/db-admin/operations.d.ts.map +1 -1
  644. package/dist/db-admin/operations.js +66 -55
  645. package/dist/db-admin/operations.js.map +1 -1
  646. package/dist/jobs/scheduler.js +17 -10
  647. package/dist/jobs/scheduler.js.map +1 -1
  648. package/dist/localization/default-messages.d.ts +3 -0
  649. package/dist/localization/default-messages.d.ts.map +1 -1
  650. package/dist/localization/default-messages.js +3 -0
  651. package/dist/localization/default-messages.js.map +1 -1
  652. package/dist/notifications/routes.d.ts +2 -2
  653. package/dist/observability/routes.d.ts +2 -2
  654. package/dist/observability/traces.d.ts.map +1 -1
  655. package/dist/observability/traces.js +155 -1
  656. package/dist/observability/traces.js.map +1 -1
  657. package/dist/org/context.d.ts +7 -0
  658. package/dist/org/context.d.ts.map +1 -1
  659. package/dist/org/context.js +54 -2
  660. package/dist/org/context.js.map +1 -1
  661. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  662. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  663. package/dist/server/agent-chat-plugin.js +10 -6
  664. package/dist/server/agent-chat-plugin.js.map +1 -1
  665. package/dist/server/auth-marketing.d.ts.map +1 -1
  666. package/dist/server/auth-marketing.js +0 -10
  667. package/dist/server/auth-marketing.js.map +1 -1
  668. package/dist/server/auth.d.ts.map +1 -1
  669. package/dist/server/auth.js +6 -4
  670. package/dist/server/auth.js.map +1 -1
  671. package/dist/server/builder-design-systems.d.ts +61 -0
  672. package/dist/server/builder-design-systems.d.ts.map +1 -1
  673. package/dist/server/builder-design-systems.js +219 -14
  674. package/dist/server/builder-design-systems.js.map +1 -1
  675. package/dist/server/collab-plugin.d.ts.map +1 -1
  676. package/dist/server/collab-plugin.js +45 -13
  677. package/dist/server/collab-plugin.js.map +1 -1
  678. package/dist/server/design-token-utils.d.ts +1 -1
  679. package/dist/server/design-token-utils.js +1 -1
  680. package/dist/server/design-token-utils.js.map +1 -1
  681. package/dist/server/fusion-app.d.ts +107 -0
  682. package/dist/server/fusion-app.d.ts.map +1 -0
  683. package/dist/server/fusion-app.js +371 -0
  684. package/dist/server/fusion-app.js.map +1 -0
  685. package/dist/server/index.d.ts +2 -1
  686. package/dist/server/index.d.ts.map +1 -1
  687. package/dist/server/index.js +2 -1
  688. package/dist/server/index.js.map +1 -1
  689. package/dist/server/onboarding-html.d.ts.map +1 -1
  690. package/dist/server/onboarding-html.js +3 -4
  691. package/dist/server/onboarding-html.js.map +1 -1
  692. package/dist/server/poll-events.d.ts +2 -0
  693. package/dist/server/poll-events.d.ts.map +1 -1
  694. package/dist/server/poll-events.js +7 -2
  695. package/dist/server/poll-events.js.map +1 -1
  696. package/dist/server/poll.d.ts +47 -3
  697. package/dist/server/poll.d.ts.map +1 -1
  698. package/dist/server/poll.js +176 -10
  699. package/dist/server/poll.js.map +1 -1
  700. package/dist/server/schema-prompt.d.ts.map +1 -1
  701. package/dist/server/schema-prompt.js +24 -7
  702. package/dist/server/schema-prompt.js.map +1 -1
  703. package/dist/settings/store.d.ts.map +1 -1
  704. package/dist/settings/store.js +32 -3
  705. package/dist/settings/store.js.map +1 -1
  706. package/dist/sharing/actions/set-resource-visibility.js +2 -0
  707. package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
  708. package/dist/sharing/actions/share-resource.d.ts.map +1 -1
  709. package/dist/sharing/actions/share-resource.js +3 -0
  710. package/dist/sharing/actions/share-resource.js.map +1 -1
  711. package/dist/sharing/actions/unshare-resource.js +2 -0
  712. package/dist/sharing/actions/unshare-resource.js.map +1 -1
  713. package/dist/styles/agent-native.css +0 -5
  714. package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  715. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  716. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  717. package/dist/templates/default/app/root.tsx +47 -0
  718. package/dist/templates/default/package.json +3 -3
  719. package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  720. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  721. package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  722. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  723. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  724. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  725. package/dist/tracking/providers.d.ts.map +1 -1
  726. package/dist/tracking/providers.js +17 -1
  727. package/dist/tracking/providers.js.map +1 -1
  728. package/docs/content/cloneable-saas.mdx +0 -1
  729. package/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
  730. package/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
  731. package/docs/content/locales/ar-SA/sharing.mdx +1 -2
  732. package/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
  733. package/docs/content/locales/ar-SA/template-plan.mdx +23 -7
  734. package/docs/content/locales/ar-SA/tracking.mdx +1 -1
  735. package/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
  736. package/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
  737. package/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
  738. package/docs/content/locales/de-DE/sharing.mdx +1 -2
  739. package/docs/content/locales/de-DE/skills-guide.mdx +2 -3
  740. package/docs/content/locales/de-DE/template-plan.mdx +23 -7
  741. package/docs/content/locales/de-DE/tracking.mdx +1 -1
  742. package/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
  743. package/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
  744. package/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
  745. package/docs/content/locales/es-ES/sharing.mdx +1 -2
  746. package/docs/content/locales/es-ES/skills-guide.mdx +2 -3
  747. package/docs/content/locales/es-ES/template-plan.mdx +23 -7
  748. package/docs/content/locales/es-ES/tracking.mdx +1 -1
  749. package/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
  750. package/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
  751. package/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
  752. package/docs/content/locales/fr-FR/sharing.mdx +1 -2
  753. package/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
  754. package/docs/content/locales/fr-FR/template-plan.mdx +23 -7
  755. package/docs/content/locales/fr-FR/tracking.mdx +1 -1
  756. package/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
  757. package/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
  758. package/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
  759. package/docs/content/locales/hi-IN/sharing.mdx +1 -2
  760. package/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
  761. package/docs/content/locales/hi-IN/template-plan.mdx +23 -7
  762. package/docs/content/locales/hi-IN/tracking.mdx +1 -1
  763. package/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
  764. package/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
  765. package/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
  766. package/docs/content/locales/ja-JP/sharing.mdx +1 -2
  767. package/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
  768. package/docs/content/locales/ja-JP/template-plan.mdx +23 -7
  769. package/docs/content/locales/ja-JP/tracking.mdx +1 -1
  770. package/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
  771. package/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
  772. package/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
  773. package/docs/content/locales/ko-KR/sharing.mdx +1 -2
  774. package/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
  775. package/docs/content/locales/ko-KR/template-plan.mdx +23 -7
  776. package/docs/content/locales/ko-KR/tracking.mdx +1 -1
  777. package/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
  778. package/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
  779. package/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
  780. package/docs/content/locales/pt-BR/sharing.mdx +1 -2
  781. package/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
  782. package/docs/content/locales/pt-BR/template-plan.mdx +23 -7
  783. package/docs/content/locales/pt-BR/tracking.mdx +1 -1
  784. package/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
  785. package/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
  786. package/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
  787. package/docs/content/locales/zh-CN/sharing.mdx +1 -2
  788. package/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
  789. package/docs/content/locales/zh-CN/template-plan.mdx +23 -7
  790. package/docs/content/locales/zh-CN/tracking.mdx +1 -1
  791. package/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
  792. package/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
  793. package/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
  794. package/docs/content/locales/zh-TW/sharing.mdx +1 -2
  795. package/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
  796. package/docs/content/locales/zh-TW/template-plan.mdx +23 -7
  797. package/docs/content/locales/zh-TW/tracking.mdx +1 -1
  798. package/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
  799. package/docs/content/plan-plugin.mdx +12 -11
  800. package/docs/content/sharing.mdx +1 -2
  801. package/docs/content/skills-guide.mdx +4 -3
  802. package/docs/content/template-plan.mdx +37 -6
  803. package/docs/content/tracking.mdx +1 -1
  804. package/docs/content/workspace-connections.mdx +1 -1
  805. package/package.json +7 -6
  806. package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
  807. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
  808. package/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
  809. package/src/templates/default/app/root.tsx +47 -0
  810. package/src/templates/default/package.json +3 -3
  811. package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
  812. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
  813. package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
  814. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
  815. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
  816. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
  817. package/corpus/core/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  818. package/corpus/core/docs/content/locales/de-DE/template-videos.mdx +0 -336
  819. package/corpus/core/docs/content/locales/es-ES/template-videos.mdx +0 -336
  820. package/corpus/core/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  821. package/corpus/core/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  822. package/corpus/core/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  823. package/corpus/core/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  824. package/corpus/core/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  825. package/corpus/core/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  826. package/corpus/core/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  827. package/corpus/core/docs/content/template-videos.mdx +0 -332
  828. package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +0 -697
  829. package/corpus/templates/slides/app/components/editor/CodeEditor.tsx +0 -49
  830. package/corpus/templates/videos/.agents/skills/actions/SKILL.md +0 -432
  831. package/corpus/templates/videos/.agents/skills/animation-tracks/SKILL.md +0 -99
  832. package/corpus/templates/videos/.agents/skills/capture-learnings/SKILL.md +0 -76
  833. package/corpus/templates/videos/.agents/skills/composition-management/SKILL.md +0 -106
  834. package/corpus/templates/videos/.agents/skills/create-skill/SKILL.md +0 -221
  835. package/corpus/templates/videos/.agents/skills/delegate-to-agent/SKILL.md +0 -263
  836. package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +0 -112
  837. package/corpus/templates/videos/.agents/skills/security/SKILL.md +0 -263
  838. package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +0 -88
  839. package/corpus/templates/videos/.agents/skills/shadcn-ui/SKILL.md +0 -95
  840. package/corpus/templates/videos/.builder/ANIMATED_PROPERTIES.md +0 -335
  841. package/corpus/templates/videos/.builder/KEYFRAME_SYNC.md +0 -166
  842. package/corpus/templates/videos/.builder/SEAMLESS_SYNC.md +0 -211
  843. package/corpus/templates/videos/.builder/rules/deploy-app.mdc +0 -22
  844. package/corpus/templates/videos/.builder/rules/organize-ui.mdc +0 -9
  845. package/corpus/templates/videos/.builder/skills/remotion/SKILL.md +0 -29
  846. package/corpus/templates/videos/.builder/skills/remotion/rules/animations.md +0 -27
  847. package/corpus/templates/videos/.builder/skills/remotion/rules/assets.md +0 -43
  848. package/corpus/templates/videos/.builder/skills/remotion/rules/audio.md +0 -58
  849. package/corpus/templates/videos/.builder/skills/remotion/rules/compositions.md +0 -129
  850. package/corpus/templates/videos/.builder/skills/remotion/rules/fonts.md +0 -47
  851. package/corpus/templates/videos/.builder/skills/remotion/rules/images.md +0 -41
  852. package/corpus/templates/videos/.builder/skills/remotion/rules/parameters.md +0 -59
  853. package/corpus/templates/videos/.builder/skills/remotion/rules/sequencing.md +0 -54
  854. package/corpus/templates/videos/.builder/skills/remotion/rules/tailwind.md +0 -11
  855. package/corpus/templates/videos/.builder/skills/remotion/rules/text-animations.md +0 -18
  856. package/corpus/templates/videos/.builder/skills/remotion/rules/timing.md +0 -103
  857. package/corpus/templates/videos/.builder/skills/remotion/rules/transparent-videos.md +0 -35
  858. package/corpus/templates/videos/.builder/skills/remotion/rules/trimming.md +0 -38
  859. package/corpus/templates/videos/.builder/skills/remotion/rules/videos.md +0 -54
  860. package/corpus/templates/videos/.dockerignore +0 -62
  861. package/corpus/templates/videos/.env.example +0 -4
  862. package/corpus/templates/videos/.oxfmtrc.json +0 -8
  863. package/corpus/templates/videos/AGENTS.md +0 -46
  864. package/corpus/templates/videos/CHANGELOG.md +0 -10
  865. package/corpus/templates/videos/DEVELOPING.md +0 -143
  866. package/corpus/templates/videos/README.md +0 -33
  867. package/corpus/templates/videos/actions/analyze-brand-assets.ts +0 -151
  868. package/corpus/templates/videos/actions/apply-design-system.ts +0 -35
  869. package/corpus/templates/videos/actions/create-design-system.ts +0 -84
  870. package/corpus/templates/videos/actions/create-folder.ts +0 -48
  871. package/corpus/templates/videos/actions/db-connect.ts +0 -45
  872. package/corpus/templates/videos/actions/db-health.ts +0 -24
  873. package/corpus/templates/videos/actions/db-status.ts +0 -28
  874. package/corpus/templates/videos/actions/delete-composition.ts +0 -272
  875. package/corpus/templates/videos/actions/delete-folder.ts +0 -27
  876. package/corpus/templates/videos/actions/generate-animated-component.ts +0 -470
  877. package/corpus/templates/videos/actions/get-composition.ts +0 -36
  878. package/corpus/templates/videos/actions/get-design-system.ts +0 -35
  879. package/corpus/templates/videos/actions/import-code.ts +0 -67
  880. package/corpus/templates/videos/actions/import-design-project.ts +0 -42
  881. package/corpus/templates/videos/actions/import-document.ts +0 -125
  882. package/corpus/templates/videos/actions/import-from-url.ts +0 -18
  883. package/corpus/templates/videos/actions/import-github.ts +0 -182
  884. package/corpus/templates/videos/actions/list-compositions.ts +0 -32
  885. package/corpus/templates/videos/actions/list-design-systems.ts +0 -66
  886. package/corpus/templates/videos/actions/list-folders.ts +0 -61
  887. package/corpus/templates/videos/actions/move-composition-to-folder.ts +0 -59
  888. package/corpus/templates/videos/actions/navigate.ts +0 -40
  889. package/corpus/templates/videos/actions/rename-folder.ts +0 -41
  890. package/corpus/templates/videos/actions/run.ts +0 -2
  891. package/corpus/templates/videos/actions/save-composition.ts +0 -114
  892. package/corpus/templates/videos/actions/set-default-design-system.ts +0 -40
  893. package/corpus/templates/videos/actions/update-composition.ts +0 -81
  894. package/corpus/templates/videos/actions/update-design-system.ts +0 -68
  895. package/corpus/templates/videos/actions/validate-compositions.ts +0 -502
  896. package/corpus/templates/videos/actions/view-screen.ts +0 -48
  897. package/corpus/templates/videos/app/components/CameraControls.tsx +0 -442
  898. package/corpus/templates/videos/app/components/CameraToolbar.tsx +0 -437
  899. package/corpus/templates/videos/app/components/CloudUpgrade.tsx +0 -287
  900. package/corpus/templates/videos/app/components/CollabPresenceBar.tsx +0 -81
  901. package/corpus/templates/videos/app/components/CompSettingsEditor.tsx +0 -107
  902. package/corpus/templates/videos/app/components/ComponentLibrarySidebar.tsx +0 -457
  903. package/corpus/templates/videos/app/components/ComponentPropsPanel.tsx +0 -227
  904. package/corpus/templates/videos/app/components/ComponentsSidebar.tsx +0 -70
  905. package/corpus/templates/videos/app/components/CompositionCard.tsx +0 -147
  906. package/corpus/templates/videos/app/components/CurrentElementPanel.tsx +0 -835
  907. package/corpus/templates/videos/app/components/CursorControls.tsx +0 -664
  908. package/corpus/templates/videos/app/components/CursorPositioningOverlay.tsx +0 -238
  909. package/corpus/templates/videos/app/components/LibraryFolderRow.tsx +0 -235
  910. package/corpus/templates/videos/app/components/MotionCurveSelect.tsx +0 -61
  911. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +0 -192
  912. package/corpus/templates/videos/app/components/PropsEditor.tsx +0 -210
  913. package/corpus/templates/videos/app/components/QuestionFlow.tsx +0 -40
  914. package/corpus/templates/videos/app/components/Sidebar.tsx +0 -609
  915. package/corpus/templates/videos/app/components/StudioHeader.tsx +0 -109
  916. package/corpus/templates/videos/app/components/Timeline.tsx +0 -2067
  917. package/corpus/templates/videos/app/components/TrackPropertiesPanel.tsx +0 -922
  918. package/corpus/templates/videos/app/components/TweaksPanel.tsx +0 -277
  919. package/corpus/templates/videos/app/components/VideoPlayer.tsx +0 -487
  920. package/corpus/templates/videos/app/components/design-system/DesignSystemCard.tsx +0 -134
  921. package/corpus/templates/videos/app/components/design-system/DesignSystemPreview.tsx +0 -176
  922. package/corpus/templates/videos/app/components/design-system/DesignSystemSetup.tsx +0 -707
  923. package/corpus/templates/videos/app/components/keyframes/KeyframeActionButtons.tsx +0 -78
  924. package/corpus/templates/videos/app/components/keyframes/KeyframeNavigation.tsx +0 -59
  925. package/corpus/templates/videos/app/components/keyframes/PropertyNumberInput.tsx +0 -56
  926. package/corpus/templates/videos/app/components/layout/Header.tsx +0 -64
  927. package/corpus/templates/videos/app/components/layout/HeaderActions.tsx +0 -84
  928. package/corpus/templates/videos/app/components/layout/Layout.tsx +0 -132
  929. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -180
  930. package/corpus/templates/videos/app/components/ui/accordion.tsx +0 -56
  931. package/corpus/templates/videos/app/components/ui/alert-dialog.tsx +0 -139
  932. package/corpus/templates/videos/app/components/ui/alert.tsx +0 -59
  933. package/corpus/templates/videos/app/components/ui/aspect-ratio.tsx +0 -5
  934. package/corpus/templates/videos/app/components/ui/avatar.tsx +0 -48
  935. package/corpus/templates/videos/app/components/ui/badge.tsx +0 -37
  936. package/corpus/templates/videos/app/components/ui/breadcrumb.tsx +0 -115
  937. package/corpus/templates/videos/app/components/ui/button.tsx +0 -57
  938. package/corpus/templates/videos/app/components/ui/calendar.tsx +0 -68
  939. package/corpus/templates/videos/app/components/ui/card.tsx +0 -86
  940. package/corpus/templates/videos/app/components/ui/carousel.tsx +0 -260
  941. package/corpus/templates/videos/app/components/ui/chart.tsx +0 -375
  942. package/corpus/templates/videos/app/components/ui/checkbox.tsx +0 -28
  943. package/corpus/templates/videos/app/components/ui/collapsible.tsx +0 -9
  944. package/corpus/templates/videos/app/components/ui/command.tsx +0 -154
  945. package/corpus/templates/videos/app/components/ui/context-menu.tsx +0 -202
  946. package/corpus/templates/videos/app/components/ui/dialog.tsx +0 -130
  947. package/corpus/templates/videos/app/components/ui/drawer.tsx +0 -116
  948. package/corpus/templates/videos/app/components/ui/dropdown-menu.tsx +0 -212
  949. package/corpus/templates/videos/app/components/ui/form.tsx +0 -177
  950. package/corpus/templates/videos/app/components/ui/hover-card.tsx +0 -27
  951. package/corpus/templates/videos/app/components/ui/input-otp.tsx +0 -69
  952. package/corpus/templates/videos/app/components/ui/input.tsx +0 -22
  953. package/corpus/templates/videos/app/components/ui/label.tsx +0 -24
  954. package/corpus/templates/videos/app/components/ui/menubar.tsx +0 -234
  955. package/corpus/templates/videos/app/components/ui/navigation-menu.tsx +0 -128
  956. package/corpus/templates/videos/app/components/ui/pagination.tsx +0 -121
  957. package/corpus/templates/videos/app/components/ui/popover.tsx +0 -44
  958. package/corpus/templates/videos/app/components/ui/progress.tsx +0 -26
  959. package/corpus/templates/videos/app/components/ui/radio-group.tsx +0 -42
  960. package/corpus/templates/videos/app/components/ui/resizable.tsx +0 -43
  961. package/corpus/templates/videos/app/components/ui/scroll-area.tsx +0 -46
  962. package/corpus/templates/videos/app/components/ui/select.tsx +0 -158
  963. package/corpus/templates/videos/app/components/ui/separator.tsx +0 -29
  964. package/corpus/templates/videos/app/components/ui/sheet.tsx +0 -160
  965. package/corpus/templates/videos/app/components/ui/sidebar.tsx +0 -782
  966. package/corpus/templates/videos/app/components/ui/skeleton.tsx +0 -15
  967. package/corpus/templates/videos/app/components/ui/slider.tsx +0 -26
  968. package/corpus/templates/videos/app/components/ui/sonner.tsx +0 -58
  969. package/corpus/templates/videos/app/components/ui/spinner.tsx +0 -17
  970. package/corpus/templates/videos/app/components/ui/switch.tsx +0 -27
  971. package/corpus/templates/videos/app/components/ui/table.tsx +0 -117
  972. package/corpus/templates/videos/app/components/ui/tabs.tsx +0 -53
  973. package/corpus/templates/videos/app/components/ui/textarea.tsx +0 -23
  974. package/corpus/templates/videos/app/components/ui/toast.tsx +0 -127
  975. package/corpus/templates/videos/app/components/ui/toaster.tsx +0 -33
  976. package/corpus/templates/videos/app/components/ui/toggle-group.tsx +0 -59
  977. package/corpus/templates/videos/app/components/ui/toggle.tsx +0 -43
  978. package/corpus/templates/videos/app/components/ui/tooltip.tsx +0 -30
  979. package/corpus/templates/videos/app/components/ui/use-toast.ts +0 -3
  980. package/corpus/templates/videos/app/config/constants.ts +0 -204
  981. package/corpus/templates/videos/app/contexts/CompositionContext.tsx +0 -437
  982. package/corpus/templates/videos/app/contexts/CurrentElementContext.tsx +0 -247
  983. package/corpus/templates/videos/app/contexts/PlaybackContext.tsx +0 -65
  984. package/corpus/templates/videos/app/contexts/TimelineContext.tsx +0 -433
  985. package/corpus/templates/videos/app/entry.client.tsx +0 -19
  986. package/corpus/templates/videos/app/entry.server.tsx +0 -10
  987. package/corpus/templates/videos/app/global.css +0 -171
  988. package/corpus/templates/videos/app/hooks/use-composition-collab.ts +0 -269
  989. package/corpus/templates/videos/app/hooks/use-composition-design-system.ts +0 -47
  990. package/corpus/templates/videos/app/hooks/use-database-compositions.ts +0 -144
  991. package/corpus/templates/videos/app/hooks/use-db-status.ts +0 -30
  992. package/corpus/templates/videos/app/hooks/use-design-systems.ts +0 -21
  993. package/corpus/templates/videos/app/hooks/use-folders.ts +0 -199
  994. package/corpus/templates/videos/app/hooks/use-mobile.tsx +0 -21
  995. package/corpus/templates/videos/app/hooks/use-navigation-state.ts +0 -52
  996. package/corpus/templates/videos/app/hooks/use-question-flow.ts +0 -14
  997. package/corpus/templates/videos/app/hooks/use-toast.ts +0 -188
  998. package/corpus/templates/videos/app/hooks/useUnsavedChanges.ts +0 -48
  999. package/corpus/templates/videos/app/i18n/ar-SA.ts +0 -446
  1000. package/corpus/templates/videos/app/i18n/de-DE.ts +0 -470
  1001. package/corpus/templates/videos/app/i18n/en-US.ts +0 -452
  1002. package/corpus/templates/videos/app/i18n/es-ES.ts +0 -468
  1003. package/corpus/templates/videos/app/i18n/fr-FR.ts +0 -469
  1004. package/corpus/templates/videos/app/i18n/hi-IN.ts +0 -446
  1005. package/corpus/templates/videos/app/i18n/index.ts +0 -34
  1006. package/corpus/templates/videos/app/i18n/ja-JP.ts +0 -460
  1007. package/corpus/templates/videos/app/i18n/ko-KR.ts +0 -453
  1008. package/corpus/templates/videos/app/i18n/pt-BR.ts +0 -461
  1009. package/corpus/templates/videos/app/i18n/zh-CN.ts +0 -437
  1010. package/corpus/templates/videos/app/i18n/zh-TW.ts +0 -437
  1011. package/corpus/templates/videos/app/lib/database-compositions.ts +0 -67
  1012. package/corpus/templates/videos/app/lib/design-systems.ts +0 -234
  1013. package/corpus/templates/videos/app/lib/tab-id.ts +0 -1
  1014. package/corpus/templates/videos/app/lib/utils.ts +0 -1
  1015. package/corpus/templates/videos/app/pages/ComponentLibrary.tsx +0 -134
  1016. package/corpus/templates/videos/app/pages/ComponentLibraryView.tsx +0 -362
  1017. package/corpus/templates/videos/app/pages/CompositionView.tsx +0 -715
  1018. package/corpus/templates/videos/app/pages/DesignSystems.tsx +0 -180
  1019. package/corpus/templates/videos/app/pages/Index.tsx +0 -258
  1020. package/corpus/templates/videos/app/pages/NewComposition.tsx +0 -95
  1021. package/corpus/templates/videos/app/pages/NotFound.tsx +0 -36
  1022. package/corpus/templates/videos/app/remotion/CameraHost.tsx +0 -223
  1023. package/corpus/templates/videos/app/remotion/animations/useElementAnimations.ts +0 -231
  1024. package/corpus/templates/videos/app/remotion/componentRegistry.ts +0 -473
  1025. package/corpus/templates/videos/app/remotion/components/AnimatedElement.tsx +0 -436
  1026. package/corpus/templates/videos/app/remotion/components/InteractiveElement.tsx +0 -159
  1027. package/corpus/templates/videos/app/remotion/components/InteractiveZoneEditor.tsx +0 -273
  1028. package/corpus/templates/videos/app/remotion/compositions/BlankComposition.tsx +0 -204
  1029. package/corpus/templates/videos/app/remotion/compositions/index.ts +0 -2
  1030. package/corpus/templates/videos/app/remotion/createComponentCursorTrack.ts +0 -132
  1031. package/corpus/templates/videos/app/remotion/docs/CURSOR_TRACKS.md +0 -285
  1032. package/corpus/templates/videos/app/remotion/easingFunctions.ts +0 -255
  1033. package/corpus/templates/videos/app/remotion/hooks/createInteractiveComposition.tsx +0 -283
  1034. package/corpus/templates/videos/app/remotion/hooks/useCursorHistory.ts +0 -72
  1035. package/corpus/templates/videos/app/remotion/hooks/useCursorTypeFromHover.ts +0 -35
  1036. package/corpus/templates/videos/app/remotion/hooks/useEditableZones.tsx +0 -267
  1037. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimation.ts +0 -90
  1038. package/corpus/templates/videos/app/remotion/hooks/useHoverAnimationSmooth.ts +0 -146
  1039. package/corpus/templates/videos/app/remotion/hooks/useInteractiveComponent.ts +0 -711
  1040. package/corpus/templates/videos/app/remotion/hooks/useRegisterInteractiveElement.ts +0 -56
  1041. package/corpus/templates/videos/app/remotion/library-components/Button.tsx +0 -252
  1042. package/corpus/templates/videos/app/remotion/library-components/Card.tsx +0 -241
  1043. package/corpus/templates/videos/app/remotion/library-components/CodePanel.tsx +0 -1005
  1044. package/corpus/templates/videos/app/remotion/library-components/FileItem.tsx +0 -196
  1045. package/corpus/templates/videos/app/remotion/library-components/FolderItem.tsx +0 -199
  1046. package/corpus/templates/videos/app/remotion/library-components/PrimaryButton.tsx +0 -177
  1047. package/corpus/templates/videos/app/remotion/library-components/SecondaryButton.tsx +0 -167
  1048. package/corpus/templates/videos/app/remotion/library-components/SectionHeader.tsx +0 -108
  1049. package/corpus/templates/videos/app/remotion/library-components/TEMPLATE.tsx +0 -211
  1050. package/corpus/templates/videos/app/remotion/library-components/index.ts +0 -10
  1051. package/corpus/templates/videos/app/remotion/registry.ts +0 -91
  1052. package/corpus/templates/videos/app/remotion/testing/animationTestUtils.ts +0 -366
  1053. package/corpus/templates/videos/app/remotion/trackAnimation.ts +0 -179
  1054. package/corpus/templates/videos/app/remotion/trackHelpers.ts +0 -110
  1055. package/corpus/templates/videos/app/remotion/ui-components/Cursor.tsx +0 -182
  1056. package/corpus/templates/videos/app/remotion/ui-components/InteractiveButton.tsx +0 -116
  1057. package/corpus/templates/videos/app/remotion/ui-components/InteractiveCard.tsx +0 -126
  1058. package/corpus/templates/videos/app/remotion/ui-components/index.ts +0 -16
  1059. package/corpus/templates/videos/app/remotion/utils/animationHelpers.ts +0 -444
  1060. package/corpus/templates/videos/app/remotion/utils/interactiveElements.ts +0 -235
  1061. package/corpus/templates/videos/app/remotion/utils/springAnimation.ts +0 -76
  1062. package/corpus/templates/videos/app/root.tsx +0 -165
  1063. package/corpus/templates/videos/app/routes/$.tsx +0 -10
  1064. package/corpus/templates/videos/app/routes/_index.tsx +0 -25
  1065. package/corpus/templates/videos/app/routes/c.$compositionId.tsx +0 -10
  1066. package/corpus/templates/videos/app/routes/components.tsx +0 -10
  1067. package/corpus/templates/videos/app/routes/design-systems.tsx +0 -10
  1068. package/corpus/templates/videos/app/routes/extensions.$id.$slug.tsx +0 -2
  1069. package/corpus/templates/videos/app/routes/extensions.$id.tsx +0 -5
  1070. package/corpus/templates/videos/app/routes/extensions._index.tsx +0 -5
  1071. package/corpus/templates/videos/app/routes/extensions.tsx +0 -5
  1072. package/corpus/templates/videos/app/routes/settings.tsx +0 -88
  1073. package/corpus/templates/videos/app/routes/team.tsx +0 -11
  1074. package/corpus/templates/videos/app/routes.ts +0 -4
  1075. package/corpus/templates/videos/app/state.ts +0 -77
  1076. package/corpus/templates/videos/app/types/elementAnimations.ts +0 -121
  1077. package/corpus/templates/videos/app/types.ts +0 -200
  1078. package/corpus/templates/videos/app/utils/compositionHelpers.ts +0 -445
  1079. package/corpus/templates/videos/app/utils/debug.ts +0 -63
  1080. package/corpus/templates/videos/app/utils/keyframeUtils.ts +0 -331
  1081. package/corpus/templates/videos/app/utils/timelineCoordinates.ts +0 -79
  1082. package/corpus/templates/videos/app/vite-env.d.ts +0 -6
  1083. package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
  1084. package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
  1085. package/corpus/templates/videos/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
  1086. package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +0 -6
  1087. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
  1088. package/corpus/templates/videos/components.json +0 -20
  1089. package/corpus/templates/videos/data/sync-config.json +0 -3
  1090. package/corpus/templates/videos/docs/examples/create-composition.example.ts +0 -128
  1091. package/corpus/templates/videos/drizzle.config.ts +0 -3
  1092. package/corpus/templates/videos/learnings.defaults.md +0 -5
  1093. package/corpus/templates/videos/package.json +0 -114
  1094. package/corpus/templates/videos/public/agent-native-icon-dark.svg +0 -10
  1095. package/corpus/templates/videos/public/agent-native-icon-light.svg +0 -10
  1096. package/corpus/templates/videos/public/agent-native-logo-dark.svg +0 -21
  1097. package/corpus/templates/videos/public/agent-native-logo-light.svg +0 -21
  1098. package/corpus/templates/videos/public/favicon.svg +0 -1
  1099. package/corpus/templates/videos/public/icon-180.svg +0 -1
  1100. package/corpus/templates/videos/public/icon-192.svg +0 -1
  1101. package/corpus/templates/videos/public/icon-512.svg +0 -1
  1102. package/corpus/templates/videos/public/manifest.json +0 -21
  1103. package/corpus/templates/videos/public/placeholder.svg +0 -1
  1104. package/corpus/templates/videos/public/robots.txt +0 -14
  1105. package/corpus/templates/videos/react-router.config.ts +0 -7
  1106. package/corpus/templates/videos/server/db/index.ts +0 -37
  1107. package/corpus/templates/videos/server/db/schema.ts +0 -55
  1108. package/corpus/templates/videos/server/middleware/auth.ts +0 -15
  1109. package/corpus/templates/videos/server/plugins/agent-chat.ts +0 -29
  1110. package/corpus/templates/videos/server/plugins/auth.ts +0 -14
  1111. package/corpus/templates/videos/server/plugins/collab.ts +0 -11
  1112. package/corpus/templates/videos/server/plugins/core-routes.ts +0 -22
  1113. package/corpus/templates/videos/server/plugins/db.ts +0 -160
  1114. package/corpus/templates/videos/server/routes/[...page].get.ts +0 -5
  1115. package/corpus/templates/videos/server/routes/[...page].head.ts +0 -1
  1116. package/corpus/templates/videos/server/routes/api/save-composition-defaults.post.ts +0 -274
  1117. package/corpus/templates/videos/shared/api.ts +0 -75
  1118. package/corpus/templates/videos/ssr-entry.ts +0 -15
  1119. package/corpus/templates/videos/tsconfig.json +0 -21
  1120. package/corpus/templates/videos/vite.config.ts +0 -19
  1121. package/corpus/templates/videos/vitest.config.ts +0 -16
  1122. package/docs/content/locales/ar-SA/template-videos.mdx +0 -336
  1123. package/docs/content/locales/de-DE/template-videos.mdx +0 -336
  1124. package/docs/content/locales/es-ES/template-videos.mdx +0 -336
  1125. package/docs/content/locales/fr-FR/template-videos.mdx +0 -336
  1126. package/docs/content/locales/hi-IN/template-videos.mdx +0 -336
  1127. package/docs/content/locales/ja-JP/template-videos.mdx +0 -336
  1128. package/docs/content/locales/ko-KR/template-videos.mdx +0 -336
  1129. package/docs/content/locales/pt-BR/template-videos.mdx +0 -336
  1130. package/docs/content/locales/zh-CN/template-videos.mdx +0 -334
  1131. package/docs/content/locales/zh-TW/template-videos.mdx +0 -334
  1132. package/docs/content/template-videos.mdx +0 -332
@@ -1,7 +1,11 @@
1
1
  import {
2
+ agentNativePath,
2
3
  appBasePath,
3
4
  callAction,
5
+ createLocalOpUndoController,
4
6
  isEmbedAuthActive,
7
+ type LocalOpUndoController,
8
+ type LocalOpUndoEntry,
5
9
  } from "@agent-native/core/client";
6
10
  import { nanoid } from "nanoid";
7
11
  import {
@@ -28,7 +32,7 @@ type GranularOp =
28
32
  slideId: string;
29
33
  fields: Partial<Omit<Slide, "id">>;
30
34
  }
31
- | { op: "delete-slide"; slideId: string }
35
+ | { op: "delete-slide"; slideId: string; allowEmpty?: boolean }
32
36
  | { op: "reorder-slides"; orderedIds: string[] }
33
37
  | {
34
38
  op: "add-slide";
@@ -50,7 +54,19 @@ type GranularOp =
50
54
  /** Sentinel: discard all accumulated ops and do a full PUT instead. */
51
55
  | { op: "full-replace"; deck: Deck };
52
56
 
53
- type PatchDeckOp = Exclude<GranularOp, { op: "full-replace" }>;
57
+ export type PatchDeckOp = Exclude<GranularOp, { op: "full-replace" }>;
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Inverse-op undo
61
+ // ---------------------------------------------------------------------------
62
+ // Undo/redo is per-user and is granular for ordinary slide/deck-field edits.
63
+ // Deck lifecycle and generated/imported full replacements use explicit
64
+ // deck-level ops because those user actions are whole-resource mutations.
65
+ export type DeckUndoOp =
66
+ | ({ deckId: string } & PatchDeckOp)
67
+ | { op: "delete-deck"; deckId: string }
68
+ | { op: "restore-deck"; deckId: string; deck: Deck; index?: number }
69
+ | { op: "replace-deck"; deckId: string; deck: Deck };
54
70
 
55
71
  export type SlideLayout =
56
72
  | "title"
@@ -115,12 +131,6 @@ export interface Deck {
115
131
  aspectRatio?: AspectRatio;
116
132
  }
117
133
 
118
- export interface HistoryEntry {
119
- timestamp: number;
120
- label: string;
121
- decks: Deck[];
122
- }
123
-
124
134
  interface DeckContextType {
125
135
  decks: Deck[];
126
136
  loading: boolean;
@@ -170,19 +180,15 @@ interface DeckContextType {
170
180
  * in-progress edit.
171
181
  */
172
182
  markDeckDirty: (deckId: string) => void;
173
- // Undo/Redo
183
+ // Undo/Redo — per-user inverse-op undo (see DeckUndoOp above).
174
184
  undo: () => void;
175
185
  redo: () => void;
176
186
  canUndo: boolean;
177
187
  canRedo: boolean;
178
- history: HistoryEntry[];
179
- historyIndex: number;
180
- restoreFromHistory: (index: number) => void;
181
188
  }
182
189
 
183
190
  const DeckContext = createContext<DeckContextType | null>(null);
184
191
 
185
- const MAX_HISTORY = 50;
186
192
  const OPEN_DECK_FALLBACK_POLL_MS = 5_000;
187
193
  const DECK_LIST_FALLBACK_POLL_MS = 15_000;
188
194
 
@@ -268,12 +274,15 @@ export function getSaveSnapshot(): { saving: boolean } {
268
274
  *
269
275
  * When a `full-replace` op is enqueued, all previously-queued ops for that
270
276
  * deck are discarded because the full replace already captures the authoritative
271
- * state (used by undo/redo and bulk generation which produce a known good
272
- * snapshot).
277
+ * state at that moment (used by undo/redo and bulk generation which produce a
278
+ * known good snapshot). Later granular edits inside the same debounce window
279
+ * must still be appended after that snapshot so quick follow-up user edits are
280
+ * not dropped on reload.
273
281
  *
274
282
  * The debounce fires after 500 ms of quiet, draining the queue via the
275
- * granular `patch-deck` action. If the queue contains a `full-replace` op,
276
- * a direct PUT to `/api/decks/:id` is used instead (backwards-compatible).
283
+ * granular `patch-deck` action. If the queue starts with a `full-replace` op,
284
+ * a direct PUT to `/api/decks/:id` is used first, then any trailing granular
285
+ * ops are sent through `patch-deck`.
277
286
  */
278
287
  function enqueueDeckOp(deckId: string, op: GranularOp) {
279
288
  // Clear any pending save timer — we're about to reset it
@@ -285,17 +294,8 @@ function enqueueDeckOp(deckId: string, op: GranularOp) {
285
294
  pendingOpsQueue.set(deckId, [op]);
286
295
  } else {
287
296
  const queue = pendingOpsQueue.get(deckId) ?? [];
288
- // If there's already a full-replace queued, leave it alone — it dominates
289
- if (queue.length > 0 && queue[0].op === "full-replace") {
290
- // Replace the stored deck snapshot with the latest state by replacing
291
- // the full-replace op rather than appending more granular ops on top.
292
- // (The op already carries the full deck; newer state comes via the
293
- // dirty-deck save effect which will enqueue a fresh full-replace when
294
- // it runs.)
295
- } else {
296
- queue.push(op);
297
- pendingOpsQueue.set(deckId, queue);
298
- }
297
+ queue.push(op);
298
+ pendingOpsQueue.set(deckId, queue);
299
299
  }
300
300
 
301
301
  // Arm the debounce
@@ -318,6 +318,13 @@ function enqueueDeckOp(deckId: string, op: GranularOp) {
318
318
  headers: { "Content-Type": "application/json" },
319
319
  body: JSON.stringify(deck),
320
320
  });
321
+ const trailingOps = ops.slice(1) as PatchDeckOp[];
322
+ if (trailingOps.length > 0) {
323
+ await callAction("patch-deck", {
324
+ deckId,
325
+ operations: trailingOps,
326
+ });
327
+ }
321
328
  } else {
322
329
  // Granular patch — concurrent-safe
323
330
  await callAction("patch-deck", {
@@ -346,6 +353,291 @@ function saveDeckToAPI(deck: Deck) {
346
353
  enqueueDeckOp(deck.id, { op: "full-replace", deck });
347
354
  }
348
355
 
356
+ /**
357
+ * Synchronously flush every pending (debounced) deck op queue using
358
+ * `fetch(..., { keepalive: true })` so in-flight edits survive a tab
359
+ * close / navigation. Called from a `pagehide` / `visibilitychange(hidden)`
360
+ * handler — without it there is a ~500ms window (the debounce) where the
361
+ * user's most recent edits are only in memory and are lost on tab close.
362
+ *
363
+ * keepalive requests are best-effort and capped (~64KB by the browser), which
364
+ * is fine: granular ops are small, and if a full-replace payload is too large
365
+ * to send keepalive the normal debounce/poll path still catches up on reopen.
366
+ */
367
+ function flushPendingSaves() {
368
+ const actionUrl = `${agentNativePath("/_agent-native/actions")}/patch-deck`;
369
+ for (const [deckId, timer] of pendingSaves) {
370
+ clearTimeout(timer);
371
+ const ops = pendingOpsQueue.get(deckId);
372
+ pendingOpsQueue.delete(deckId);
373
+ if (!ops || ops.length === 0) continue;
374
+ try {
375
+ if (ops[0].op === "full-replace") {
376
+ const deck = ops[0].deck;
377
+ void fetch(`${appBasePath()}/api/decks/${deckId}`, {
378
+ method: "PUT",
379
+ headers: { "Content-Type": "application/json" },
380
+ body: JSON.stringify(deck),
381
+ keepalive: true,
382
+ });
383
+ const trailingOps = ops.slice(1) as PatchDeckOp[];
384
+ if (trailingOps.length === 0) continue;
385
+ void fetch(actionUrl, {
386
+ method: "POST",
387
+ headers: {
388
+ "Content-Type": "application/json",
389
+ "X-Agent-Native-Frontend": "1",
390
+ },
391
+ body: JSON.stringify({
392
+ deckId,
393
+ operations: trailingOps,
394
+ }),
395
+ keepalive: true,
396
+ });
397
+ } else {
398
+ void fetch(actionUrl, {
399
+ method: "POST",
400
+ headers: {
401
+ "Content-Type": "application/json",
402
+ "X-Agent-Native-Frontend": "1",
403
+ },
404
+ body: JSON.stringify({
405
+ deckId,
406
+ operations: ops as PatchDeckOp[],
407
+ }),
408
+ keepalive: true,
409
+ });
410
+ }
411
+ } catch {
412
+ // Best-effort — nothing more we can do as the page is unloading.
413
+ }
414
+ }
415
+ pendingSaves.clear();
416
+ notifySaveListeners();
417
+ }
418
+
419
+ function discardPendingDeckOps(deckId: string) {
420
+ const timer = pendingSaves.get(deckId);
421
+ if (timer) clearTimeout(timer);
422
+ pendingSaves.delete(deckId);
423
+ pendingOpsQueue.delete(deckId);
424
+ notifySaveListeners();
425
+ }
426
+
427
+ // ---------------------------------------------------------------------------
428
+ // Local op application + inverse derivation (for inverse-op undo)
429
+ // ---------------------------------------------------------------------------
430
+ // These mirror the server-side merge in actions/patch-deck.ts but operate on
431
+ // the in-memory Deck[] so undo/redo can apply optimistically. They are pure:
432
+ // they return a new slides array / deck rather than mutating in place.
433
+
434
+ /** Fields carried by a `patch-deck-fields` op. */
435
+ type PatchDeckFields = Extract<
436
+ PatchDeckOp,
437
+ { op: "patch-deck-fields" }
438
+ >["fields"];
439
+
440
+ /**
441
+ * Apply a single granular op to a deck's slides/fields, returning the updated
442
+ * Deck. Unknown/no-op cases (slide already gone, etc.) return the deck
443
+ * unchanged so undo entries that no longer apply fail soft instead of
444
+ * corrupting state.
445
+ */
446
+ export function applyOpToDeck(deck: Deck, op: PatchDeckOp): Deck {
447
+ switch (op.op) {
448
+ case "patch-slide": {
449
+ let changed = false;
450
+ const slides = deck.slides.map((s) => {
451
+ if (s.id !== op.slideId) return s;
452
+ changed = true;
453
+ return { ...s, ...op.fields };
454
+ });
455
+ if (!changed) return deck; // slide concurrently deleted — skip
456
+ return { ...deck, slides, updatedAt: new Date().toISOString() };
457
+ }
458
+ case "delete-slide": {
459
+ const slides = deck.slides.filter((s) => s.id !== op.slideId);
460
+ if (slides.length === deck.slides.length) return deck; // already gone
461
+ // NOTE: unlike the user-facing `deleteSlide` handler and the server merge,
462
+ // undo/redo application does NOT inject a fallback blank slide when the
463
+ // deck empties out. Undo must restore the EXACT prior state — if the deck
464
+ // was legitimately empty before an add-slide (e.g. a freshly reloaded
465
+ // empty deck), undoing that add must return it to empty, not to a
466
+ // spurious blank slide.
467
+ return { ...deck, slides, updatedAt: new Date().toISOString() };
468
+ }
469
+ case "reorder-slides": {
470
+ const byId = new Map(deck.slides.map((s) => [s.id, s]));
471
+ const reordered: Slide[] = [];
472
+ for (const id of op.orderedIds) {
473
+ const slide = byId.get(id);
474
+ if (slide) reordered.push(slide);
475
+ }
476
+ // Preserve slides not named in orderedIds (concurrent adds) at the end.
477
+ const named = new Set(op.orderedIds);
478
+ for (const s of deck.slides) {
479
+ if (!named.has(s.id)) reordered.push(s);
480
+ }
481
+ return {
482
+ ...deck,
483
+ slides: reordered,
484
+ updatedAt: new Date().toISOString(),
485
+ };
486
+ }
487
+ case "add-slide": {
488
+ if (deck.slides.some((s) => s.id === op.slideId)) return deck; // idempotent
489
+ const newSlide: Slide = {
490
+ id: op.slideId,
491
+ content: op.fields.content,
492
+ notes: op.fields.notes ?? "",
493
+ layout: (op.fields.layout as SlideLayout) ?? "content",
494
+ ...(op.fields.background !== undefined
495
+ ? { background: op.fields.background }
496
+ : {}),
497
+ };
498
+ const slides = [...deck.slides];
499
+ const afterIdx = op.afterSlideId
500
+ ? slides.findIndex((s) => s.id === op.afterSlideId)
501
+ : -1;
502
+ if (afterIdx !== -1) slides.splice(afterIdx + 1, 0, newSlide);
503
+ else slides.push(newSlide);
504
+ return { ...deck, slides, updatedAt: new Date().toISOString() };
505
+ }
506
+ case "patch-deck-fields": {
507
+ return {
508
+ ...deck,
509
+ ...op.fields,
510
+ updatedAt: new Date().toISOString(),
511
+ } as Deck;
512
+ }
513
+ }
514
+ }
515
+
516
+ export function applyUndoOpToDecks(decks: Deck[], op: DeckUndoOp): Deck[] {
517
+ switch (op.op) {
518
+ case "delete-deck":
519
+ return decks.filter((deck) => deck.id !== op.deckId);
520
+ case "restore-deck": {
521
+ const nextDeck = op.deck;
522
+ const existingIndex = decks.findIndex((deck) => deck.id === op.deckId);
523
+ if (existingIndex >= 0) {
524
+ const next = [...decks];
525
+ next[existingIndex] = nextDeck;
526
+ return next;
527
+ }
528
+ const next = [...decks];
529
+ const index =
530
+ typeof op.index === "number" && op.index >= 0
531
+ ? Math.min(op.index, next.length)
532
+ : next.length;
533
+ next.splice(index, 0, nextDeck);
534
+ return next;
535
+ }
536
+ case "replace-deck": {
537
+ const existingIndex = decks.findIndex((deck) => deck.id === op.deckId);
538
+ if (existingIndex < 0) return [...decks, op.deck];
539
+ const next = [...decks];
540
+ next[existingIndex] = op.deck;
541
+ return next;
542
+ }
543
+ default: {
544
+ const idx = decks.findIndex((deck) => deck.id === op.deckId);
545
+ if (idx < 0) return decks;
546
+ const { deckId: _deckId, ...granular } = op;
547
+ void _deckId;
548
+ const updated = applyOpToDeck(decks[idx], granular);
549
+ if (updated === decks[idx]) return decks;
550
+ const next = [...decks];
551
+ next[idx] = updated;
552
+ return next;
553
+ }
554
+ }
555
+ }
556
+
557
+ /**
558
+ * Build the inverse of a granular op given the deck state BEFORE the op was
559
+ * applied. Returns an array of ops to apply (usually one, occasionally two) or
560
+ * `null` when the op has no meaningful inverse (e.g. a no-op patch) so the
561
+ * caller skips pushing an undo entry.
562
+ */
563
+ export function deriveInverseOp(
564
+ before: Deck,
565
+ op: PatchDeckOp,
566
+ ): PatchDeckOp[] | null {
567
+ switch (op.op) {
568
+ case "patch-slide": {
569
+ const prior = before.slides.find((s) => s.id === op.slideId);
570
+ if (!prior) return null; // slide didn't exist before — nothing to restore
571
+ const priorFields: Partial<Omit<Slide, "id">> = {};
572
+ for (const key of Object.keys(op.fields) as (keyof Omit<Slide, "id">)[]) {
573
+ // Capture the prior value for every field this op touches, so undo
574
+ // restores exactly what changed (including clearing fields back to
575
+ // undefined).
576
+ (priorFields as Record<string, unknown>)[key] = prior[key];
577
+ }
578
+ return [{ op: "patch-slide", slideId: op.slideId, fields: priorFields }];
579
+ }
580
+ case "delete-slide": {
581
+ const prior = before.slides.find((s) => s.id === op.slideId);
582
+ if (!prior) return null;
583
+ const idx = before.slides.findIndex((s) => s.id === op.slideId);
584
+ const afterSlideId = idx > 0 ? before.slides[idx - 1]?.id : undefined;
585
+ // Re-add the deleted slide with its full prior content, then reorder to
586
+ // the exact prior order. The add-slide op alone can only express "after
587
+ // slide X" or "append", so it cannot restore a slide to the HEAD of the
588
+ // deck; the follow-up reorder guarantees exact position regardless.
589
+ return [
590
+ {
591
+ op: "add-slide",
592
+ slideId: prior.id,
593
+ afterSlideId,
594
+ fields: {
595
+ content: prior.content,
596
+ notes: prior.notes,
597
+ layout: prior.layout,
598
+ ...(prior.background !== undefined
599
+ ? { background: prior.background }
600
+ : {}),
601
+ },
602
+ },
603
+ {
604
+ op: "reorder-slides",
605
+ orderedIds: before.slides.map((s) => s.id),
606
+ },
607
+ ];
608
+ }
609
+ case "add-slide": {
610
+ // Inverse of adding a slide is deleting it.
611
+ return [
612
+ {
613
+ op: "delete-slide",
614
+ slideId: op.slideId,
615
+ ...(before.slides.length === 0 ? { allowEmpty: true } : {}),
616
+ },
617
+ ];
618
+ }
619
+ case "reorder-slides": {
620
+ // Inverse reorder = the order the slides were in before.
621
+ return [
622
+ { op: "reorder-slides", orderedIds: before.slides.map((s) => s.id) },
623
+ ];
624
+ }
625
+ case "patch-deck-fields": {
626
+ const priorFields: Record<string, unknown> = {};
627
+ const beforeRecord = before as unknown as Record<string, unknown>;
628
+ for (const key of Object.keys(op.fields)) {
629
+ priorFields[key] = beforeRecord[key];
630
+ }
631
+ return [
632
+ {
633
+ op: "patch-deck-fields",
634
+ fields: priorFields as PatchDeckFields,
635
+ },
636
+ ];
637
+ }
638
+ }
639
+ }
640
+
349
641
  /**
350
642
  * Fetch the deck list. Returns `null` on any failure (network error, non-2xx
351
643
  * response) so callers can distinguish "authoritative empty list" from
@@ -532,10 +824,14 @@ export function DeckProvider({ children }: { children: ReactNode }) {
532
824
  const [loading, setLoading] = useState(true);
533
825
  const decksRef = useRef<Deck[]>([]);
534
826
 
535
- // History for undo/redo
536
- const [history, setHistory] = useState<HistoryEntry[]>([]);
537
- const [historyIndex, setHistoryIndex] = useState(-1);
538
- const skipHistoryRef = useRef(false);
827
+ // Per-user inverse-op undo/redo. `canUndo`/`canRedo` are React state kept in
828
+ // sync with the controller via its onChange callback. The controller and its
829
+ // apply path are wired below once `decks`/enqueue are in scope.
830
+ const [canUndo, setCanUndo] = useState(false);
831
+ const [canRedo, setCanRedo] = useState(false);
832
+ const undoControllerRef = useRef<LocalOpUndoController<DeckUndoOp> | null>(
833
+ null,
834
+ );
539
835
  // Track when external (SSE) updates happen so the save effect doesn't echo them back
540
836
  const lastExternalUpdateRef = useRef(0);
541
837
  // Track client-created decks that haven't been confirmed on the server yet.
@@ -553,28 +849,103 @@ export function DeckProvider({ children }: { children: ReactNode }) {
553
849
  dirtyDeckIdsRef.current.add(deckId);
554
850
  }, []);
555
851
 
556
- const markChangedDecksDirty = useCallback((nextDecks: Deck[]) => {
557
- const ids = changedDeckIds(decksRef.current, nextDecks);
558
- if (ids.length === 0) return;
559
- lastExternalUpdateRef.current = 0;
560
- for (const id of ids) dirtyDeckIdsRef.current.add(id);
852
+ const deleteDeckAfterPendingCreate = useCallback((deckId: string) => {
853
+ const pendingCreate = pendingCreatePromisesRef.current.get(deckId);
854
+ if (!pendingCreate) {
855
+ void deleteDeckFromAPI(deckId);
856
+ return;
857
+ }
858
+
859
+ void pendingCreate
860
+ .then(() => deleteDeckFromAPI(deckId))
861
+ .catch(() => {
862
+ // The create/duplicate failed, so there is nothing on the server to delete.
863
+ });
864
+ }, []);
865
+
866
+ // Plain local decks update. Undo entries are recorded explicitly by each
867
+ // mutation via `recordUndo` (inverse ops), so this no longer snapshots the
868
+ // whole decks array the way the old `setDecksWithHistory` did.
869
+ const setDecksLocal = useCallback((updater: (prev: Deck[]) => Deck[]) => {
870
+ setDecks(updater);
561
871
  }, []);
562
872
 
563
873
  useEffect(() => {
564
874
  decksRef.current = decks;
565
875
  }, [decks]);
566
876
 
877
+ // ── Inverse-op undo controller ────────────────────────────────────────────
878
+ // Applying an undo/redo entry runs each tagged op through the SAME optimistic
879
+ // local update + granular persist path as a normal edit. Because we only ever
880
+ // send granular ops (never full-replace), undo/redo can never clobber a
881
+ // concurrent edit to a different slide by another human or the agent. Entries
882
+ // that no longer apply (e.g. the slide was deleted remotely) fail soft:
883
+ // applyOpToDeck returns the deck unchanged and the granular server merge
884
+ // ignores the missing target.
885
+ if (!undoControllerRef.current) {
886
+ undoControllerRef.current = createLocalOpUndoController<DeckUndoOp>({
887
+ apply: (ops) => {
888
+ // Apply all ops to local state in one pass, then persist each.
889
+ setDecks((prev) => {
890
+ let next = prev;
891
+ for (const op of ops) {
892
+ next = applyUndoOpToDecks(next, op);
893
+ }
894
+ return next;
895
+ });
896
+ for (const op of ops) {
897
+ markDeckDirty(op.deckId);
898
+ if (op.op === "delete-deck") {
899
+ discardPendingDeckOps(op.deckId);
900
+ deleteDeckAfterPendingCreate(op.deckId);
901
+ } else if (op.op === "restore-deck" || op.op === "replace-deck") {
902
+ enqueueDeckOp(op.deckId, { op: "full-replace", deck: op.deck });
903
+ } else {
904
+ const { deckId, ...granular } = op;
905
+ enqueueDeckOp(deckId, granular);
906
+ }
907
+ }
908
+ },
909
+ onChange: () => {
910
+ const c = undoControllerRef.current;
911
+ setCanUndo(c ? c.canUndo() : false);
912
+ setCanRedo(c ? c.canRedo() : false);
913
+ },
914
+ });
915
+ }
916
+
917
+ /**
918
+ * Record an undo entry for a just-applied local mutation. `before` is the
919
+ * deck state prior to the mutation (for inverse derivation); `redoOp` is the
920
+ * forward op that was applied. Same `coalesceKey` within the controller's
921
+ * window merges bursts (e.g. rapid text edits to one slide).
922
+ */
923
+ const recordUndo = useCallback(
924
+ (
925
+ before: Deck,
926
+ redoOp: PatchDeckOp,
927
+ opts?: { label?: string; coalesceKey?: string },
928
+ ) => {
929
+ const inverseOps = deriveInverseOp(before, redoOp);
930
+ if (!inverseOps || inverseOps.length === 0) return;
931
+ const entry: LocalOpUndoEntry<DeckUndoOp> = {
932
+ undo: inverseOps.map((o) => ({ deckId: before.id, ...o })),
933
+ redo: [{ deckId: before.id, ...redoOp }],
934
+ label: opts?.label,
935
+ coalesceKey: opts?.coalesceKey,
936
+ };
937
+ undoControllerRef.current?.push(entry);
938
+ },
939
+ [],
940
+ );
941
+
567
942
  const resetDeckBaseline = useCallback((nextDecks: Deck[]) => {
568
- skipHistoryRef.current = false;
569
943
  setDecks(nextDecks);
570
- setHistory([
571
- {
572
- timestamp: Date.now(),
573
- label: "Initial state",
574
- decks: JSON.parse(JSON.stringify(nextDecks)),
575
- },
576
- ]);
577
- setHistoryIndex(0);
944
+ // A baseline reset (initial mount, route change, or access reload) starts a
945
+ // fresh undo timeline. Note: this is NOT the SSE/poll "remote update" path —
946
+ // those call setDecks directly and intentionally leave the undo stack
947
+ // intact so a collaborator's edit doesn't wipe your local undo history.
948
+ undoControllerRef.current?.clear();
578
949
  }, []);
579
950
 
580
951
  const reloadDecks = useCallback(async () => {
@@ -816,92 +1187,31 @@ export function DeckProvider({ children }: { children: ReactNode }) {
816
1187
  return () => evtSource.close();
817
1188
  }, []);
818
1189
 
819
- const pushHistory = useCallback(
820
- (label: string, newDecks: Deck[]) => {
821
- setHistory((prev) => {
822
- const truncated = prev.slice(0, historyIndex + 1);
823
- const newHistory = [
824
- ...truncated,
825
- {
826
- timestamp: Date.now(),
827
- label,
828
- decks: JSON.parse(JSON.stringify(newDecks)),
829
- },
830
- ];
831
- if (newHistory.length > MAX_HISTORY) {
832
- newHistory.shift();
833
- return newHistory;
834
- }
835
- return newHistory;
836
- });
837
- setHistoryIndex((prev) => {
838
- const truncatedLen = Math.min(prev + 1, history.length);
839
- return Math.min(truncatedLen, MAX_HISTORY - 1);
840
- });
841
- },
842
- [historyIndex, history.length],
843
- );
844
-
845
- const setDecksWithHistory = useCallback(
846
- (label: string, updater: (prev: Deck[]) => Deck[]) => {
847
- setDecks((prev) => {
848
- const next = updater(prev);
849
- // Push to history after state update
850
- setTimeout(() => {
851
- if (!skipHistoryRef.current) {
852
- pushHistory(label, next);
853
- }
854
- skipHistoryRef.current = false;
855
- }, 0);
856
- return next;
857
- });
858
- },
859
- [pushHistory],
860
- );
1190
+ // Flush pending (debounced) saves before the tab is hidden or unloaded so the
1191
+ // last ~500ms of edits aren't lost on close/navigation. `pagehide` is the
1192
+ // reliable unload signal on modern browsers (incl. bfcache); we also flush on
1193
+ // `visibilitychange(hidden)` which fires on mobile tab-switch / app-background
1194
+ // where `pagehide` may not.
1195
+ useEffect(() => {
1196
+ const onHidden = () => {
1197
+ if (document.visibilityState === "hidden") flushPendingSaves();
1198
+ };
1199
+ const onPageHide = () => flushPendingSaves();
1200
+ document.addEventListener("visibilitychange", onHidden);
1201
+ window.addEventListener("pagehide", onPageHide);
1202
+ return () => {
1203
+ document.removeEventListener("visibilitychange", onHidden);
1204
+ window.removeEventListener("pagehide", onPageHide);
1205
+ };
1206
+ }, []);
861
1207
 
862
1208
  const undo = useCallback(() => {
863
- if (historyIndex <= 0) return;
864
- const newIndex = historyIndex - 1;
865
- const nextDecks = JSON.parse(JSON.stringify(history[newIndex].decks));
866
- // Undo restores a complete known-good snapshot — use full-replace so
867
- // we don't try to infer granular ops from the diff.
868
- for (const deck of nextDecks) {
869
- enqueueDeckOp(deck.id, { op: "full-replace", deck });
870
- }
871
- markChangedDecksDirty(nextDecks);
872
- setHistoryIndex(newIndex);
873
- skipHistoryRef.current = true;
874
- setDecks(nextDecks);
875
- }, [historyIndex, history, markChangedDecksDirty]);
1209
+ void undoControllerRef.current?.undo();
1210
+ }, []);
876
1211
 
877
1212
  const redo = useCallback(() => {
878
- if (historyIndex >= history.length - 1) return;
879
- const newIndex = historyIndex + 1;
880
- const nextDecks = JSON.parse(JSON.stringify(history[newIndex].decks));
881
- // Same as undo — restore from a complete snapshot.
882
- for (const deck of nextDecks) {
883
- enqueueDeckOp(deck.id, { op: "full-replace", deck });
884
- }
885
- markChangedDecksDirty(nextDecks);
886
- setHistoryIndex(newIndex);
887
- skipHistoryRef.current = true;
888
- setDecks(nextDecks);
889
- }, [historyIndex, history, markChangedDecksDirty]);
890
-
891
- const restoreFromHistory = useCallback(
892
- (index: number) => {
893
- if (index < 0 || index >= history.length) return;
894
- const nextDecks = JSON.parse(JSON.stringify(history[index].decks));
895
- for (const deck of nextDecks) {
896
- enqueueDeckOp(deck.id, { op: "full-replace", deck });
897
- }
898
- markChangedDecksDirty(nextDecks);
899
- setHistoryIndex(index);
900
- skipHistoryRef.current = true;
901
- setDecks(nextDecks);
902
- },
903
- [history, markChangedDecksDirty],
904
- );
1213
+ void undoControllerRef.current?.redo();
1214
+ }, []);
905
1215
 
906
1216
  // Keyboard shortcuts for undo/redo
907
1217
  useEffect(() => {
@@ -937,6 +1247,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
937
1247
  title?: string,
938
1248
  options?: { noDefaultSlides?: boolean; designSystemId?: string | null },
939
1249
  ): Deck => {
1250
+ const insertIndex = decksRef.current.length;
940
1251
  const newDeck: Deck = {
941
1252
  id: nanoid(10),
942
1253
  title: title || "Untitled Deck",
@@ -980,10 +1291,22 @@ export function DeckProvider({ children }: { children: ReactNode }) {
980
1291
  pendingCreatePromisesRef.current.delete(newDeck.id);
981
1292
  }
982
1293
  });
983
- setDecksWithHistory("Create deck", (prev) => [...prev, newDeck]);
1294
+ setDecksLocal((prev) => [...prev, newDeck]);
1295
+ undoControllerRef.current?.push({
1296
+ undo: [{ op: "delete-deck", deckId: newDeck.id }],
1297
+ redo: [
1298
+ {
1299
+ op: "restore-deck",
1300
+ deckId: newDeck.id,
1301
+ deck: newDeck,
1302
+ index: insertIndex,
1303
+ },
1304
+ ],
1305
+ label: "Create deck",
1306
+ });
984
1307
  return newDeck;
985
1308
  },
986
- [setDecksWithHistory],
1309
+ [setDecksLocal],
987
1310
  );
988
1311
 
989
1312
  const ensureDeckPersisted = useCallback(async (id: string) => {
@@ -1008,6 +1331,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1008
1331
 
1009
1332
  const now = new Date().toISOString();
1010
1333
  const newTitle = title || `Copy of ${source.title}`;
1334
+ const insertIndex = decksRef.current.length;
1011
1335
  // Re-id slides so optimistic edits to the copy don't collide with the
1012
1336
  // original. The server does the same thing — these client ids will be
1013
1337
  // replaced by server-generated ones once the duplicate action lands and
@@ -1035,11 +1359,16 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1035
1359
  pendingDuplicateSourceIdsRef.current.add(sourceDeckId);
1036
1360
 
1037
1361
  // Fire the action in the background. On error, roll back.
1038
- callAction<DuplicateDeckActionResult>("duplicate-deck", {
1039
- deckId: sourceDeckId,
1040
- newId,
1041
- title,
1042
- })
1362
+ const duplicatePromise = callAction<DuplicateDeckActionResult>(
1363
+ "duplicate-deck",
1364
+ {
1365
+ deckId: sourceDeckId,
1366
+ newId,
1367
+ title,
1368
+ },
1369
+ ).then(() => undefined);
1370
+ pendingCreatePromisesRef.current.set(newId, duplicatePromise);
1371
+ duplicatePromise
1043
1372
  .catch((err) => {
1044
1373
  console.error("Duplicate failed:", err);
1045
1374
  // Roll back: drop the optimistic deck from local state.
@@ -1047,23 +1376,55 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1047
1376
  })
1048
1377
  .finally(() => {
1049
1378
  pendingCreateIdsRef.current.delete(newId);
1379
+ if (
1380
+ pendingCreatePromisesRef.current.get(newId) === duplicatePromise
1381
+ ) {
1382
+ pendingCreatePromisesRef.current.delete(newId);
1383
+ }
1050
1384
  pendingDuplicateSourceIdsRef.current.delete(sourceDeckId);
1051
1385
  });
1052
1386
 
1053
- setDecksWithHistory("Duplicate deck", (prev) => [...prev, optimistic]);
1387
+ setDecksLocal((prev) => [...prev, optimistic]);
1388
+ undoControllerRef.current?.push({
1389
+ undo: [{ op: "delete-deck", deckId: optimistic.id }],
1390
+ redo: [
1391
+ {
1392
+ op: "restore-deck",
1393
+ deckId: optimistic.id,
1394
+ deck: optimistic,
1395
+ index: insertIndex,
1396
+ },
1397
+ ],
1398
+ label: "Duplicate deck",
1399
+ });
1054
1400
  return optimistic;
1055
1401
  },
1056
- [decks, setDecksWithHistory],
1402
+ [decks, setDecksLocal],
1057
1403
  );
1058
1404
 
1059
1405
  const deleteDeck = useCallback(
1060
1406
  (id: string) => {
1061
- deleteDeckFromAPI(id);
1062
- setDecksWithHistory("Delete deck", (prev) =>
1063
- prev.filter((d) => d.id !== id),
1064
- );
1407
+ const beforeDeck = decksRef.current.find((deck) => deck.id === id);
1408
+ const beforeIndex = decksRef.current.findIndex((deck) => deck.id === id);
1409
+ discardPendingDeckOps(id);
1410
+ deleteDeckAfterPendingCreate(id);
1411
+ setDecksLocal((prev) => prev.filter((d) => d.id !== id));
1412
+ if (beforeDeck) {
1413
+ undoControllerRef.current?.push({
1414
+ undo: [
1415
+ {
1416
+ op: "restore-deck",
1417
+ deckId: id,
1418
+ deck: beforeDeck,
1419
+ index: beforeIndex,
1420
+ },
1421
+ ],
1422
+ redo: [{ op: "delete-deck", deckId: id }],
1423
+ label: "Delete deck",
1424
+ });
1425
+ }
1065
1426
  },
1066
- [setDecksWithHistory],
1427
+ [deleteDeckAfterPendingCreate, setDecksLocal],
1067
1428
  );
1068
1429
 
1069
1430
  const updateDeck = useCallback(
@@ -1071,6 +1432,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1071
1432
  // Clear the external-update suppression window so a rename/update that
1072
1433
  // happens within 2s of page load (or an SSE event) is not silently dropped.
1073
1434
  markDeckDirty(id);
1435
+ const before = decksRef.current.find((d) => d.id === id);
1074
1436
  setDecks((prev) =>
1075
1437
  prev.map((d) =>
1076
1438
  d.id === id
@@ -1093,13 +1455,24 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1093
1455
  void _slides;
1094
1456
  _ca;
1095
1457
  if (Object.keys(persistableUpdates).length > 0) {
1096
- enqueueDeckOp(id, {
1458
+ const op: PatchDeckOp = {
1097
1459
  op: "patch-deck-fields",
1098
- fields: persistableUpdates,
1099
- });
1460
+ fields: persistableUpdates as PatchDeckFields,
1461
+ };
1462
+ enqueueDeckOp(id, op);
1463
+ if (before) {
1464
+ // Coalesce rapid deck-field edits (e.g. title typing, tweak sliders)
1465
+ // per field-set so a burst becomes one undo step.
1466
+ recordUndo(before, op, {
1467
+ label: "Update deck",
1468
+ coalesceKey: `${id}:deck-fields:${Object.keys(persistableUpdates)
1469
+ .sort()
1470
+ .join(",")}`,
1471
+ });
1472
+ }
1100
1473
  }
1101
1474
  },
1102
- [markDeckDirty],
1475
+ [markDeckDirty, recordUndo],
1103
1476
  );
1104
1477
 
1105
1478
  const getDeck = useCallback(
@@ -1118,8 +1491,9 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1118
1491
  background: "bg-[#000000]",
1119
1492
  };
1120
1493
 
1494
+ const before = decksRef.current.find((d) => d.id === deckId);
1121
1495
  let afterSlideId: string | undefined;
1122
- setDecksWithHistory("Add slide", (prev) =>
1496
+ setDecksLocal((prev) =>
1123
1497
  prev.map((d) => {
1124
1498
  if (d.id !== deckId) return d;
1125
1499
  const slides = [...d.slides];
@@ -1134,7 +1508,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1134
1508
 
1135
1509
  // Granular op — the server splices in only this slide, preserving any
1136
1510
  // concurrent changes to other slides.
1137
- enqueueDeckOp(deckId, {
1511
+ const op: PatchDeckOp = {
1138
1512
  op: "add-slide",
1139
1513
  slideId: newSlide.id,
1140
1514
  afterSlideId,
@@ -1144,11 +1518,13 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1144
1518
  layout: newSlide.layout,
1145
1519
  background: newSlide.background,
1146
1520
  },
1147
- });
1521
+ };
1522
+ enqueueDeckOp(deckId, op);
1523
+ if (before) recordUndo(before, op, { label: "Add slide" });
1148
1524
 
1149
1525
  return newSlide.id;
1150
1526
  },
1151
- [markDeckDirty, setDecksWithHistory],
1527
+ [markDeckDirty, recordUndo, setDecksLocal],
1152
1528
  );
1153
1529
 
1154
1530
  const updateSlide = useCallback(
@@ -1161,7 +1537,8 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1161
1537
  : updates.content
1162
1538
  ? "Update content"
1163
1539
  : "Edit slide";
1164
- setDecksWithHistory(label, (prev: Deck[]) =>
1540
+ const before = decksRef.current.find((d) => d.id === deckId);
1541
+ setDecksLocal((prev: Deck[]) =>
1165
1542
  prev.map((d) => {
1166
1543
  if (d.id !== deckId) return d;
1167
1544
  return {
@@ -1174,15 +1551,28 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1174
1551
  }),
1175
1552
  );
1176
1553
  // Granular op — only this slide's changed fields reach the server.
1177
- enqueueDeckOp(deckId, { op: "patch-slide", slideId, fields: updates });
1554
+ const op: PatchDeckOp = { op: "patch-slide", slideId, fields: updates };
1555
+ enqueueDeckOp(deckId, op);
1556
+ if (before) {
1557
+ // Coalesce a burst of edits to the SAME slide's SAME field-set into one
1558
+ // undo step (e.g. typing characters into inline text). Distinct
1559
+ // field-sets (content vs background vs layout) get distinct undo steps.
1560
+ recordUndo(before, op, {
1561
+ label,
1562
+ coalesceKey: `${deckId}:${slideId}:${Object.keys(updates)
1563
+ .sort()
1564
+ .join(",")}`,
1565
+ });
1566
+ }
1178
1567
  },
1179
- [markDeckDirty, setDecksWithHistory],
1568
+ [markDeckDirty, recordUndo, setDecksLocal],
1180
1569
  );
1181
1570
 
1182
1571
  const deleteSlide = useCallback(
1183
1572
  (deckId: string, slideId: string) => {
1184
1573
  markDeckDirty(deckId);
1185
- setDecksWithHistory("Delete slide", (prev) =>
1574
+ const before = decksRef.current.find((d) => d.id === deckId);
1575
+ setDecksLocal((prev) =>
1186
1576
  prev.map((d) => {
1187
1577
  if (d.id !== deckId) return d;
1188
1578
  const slides = d.slides.filter((s) => s.id !== slideId);
@@ -1198,16 +1588,22 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1198
1588
  }),
1199
1589
  );
1200
1590
  // Granular op — server deletes only this slide from the blob.
1201
- enqueueDeckOp(deckId, { op: "delete-slide", slideId });
1591
+ const op: PatchDeckOp = { op: "delete-slide", slideId };
1592
+ enqueueDeckOp(deckId, op);
1593
+ // Inverse re-adds the full prior slide at its old position, so undo
1594
+ // restores content/notes/layout/background exactly. (This is the case
1595
+ // behind the "Undo delete" toast in DeckEditor.)
1596
+ if (before) recordUndo(before, op, { label: "Delete slide" });
1202
1597
  },
1203
- [markDeckDirty, setDecksWithHistory],
1598
+ [markDeckDirty, recordUndo, setDecksLocal],
1204
1599
  );
1205
1600
 
1206
1601
  const duplicateSlide = useCallback(
1207
1602
  (deckId: string, slideId: string) => {
1208
1603
  markDeckDirty(deckId);
1604
+ const before = decksRef.current.find((d) => d.id === deckId);
1209
1605
  let copiedSlide: Slide | undefined;
1210
- setDecksWithHistory("Duplicate slide", (prev) =>
1606
+ setDecksLocal((prev) =>
1211
1607
  prev.map((d) => {
1212
1608
  if (d.id !== deckId) return d;
1213
1609
  const idx = d.slides.findIndex((s) => s.id === slideId);
@@ -1223,7 +1619,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1223
1619
  if (copiedSlide) {
1224
1620
  // Granular add-slide op — inserts the copy after the original.
1225
1621
  const { id: newSlideId, ...rest } = copiedSlide;
1226
- enqueueDeckOp(deckId, {
1622
+ const op: PatchDeckOp = {
1227
1623
  op: "add-slide",
1228
1624
  slideId: newSlideId,
1229
1625
  afterSlideId: slideId,
@@ -1233,17 +1629,20 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1233
1629
  layout: rest.layout,
1234
1630
  background: rest.background,
1235
1631
  },
1236
- });
1632
+ };
1633
+ enqueueDeckOp(deckId, op);
1634
+ if (before) recordUndo(before, op, { label: "Duplicate slide" });
1237
1635
  }
1238
1636
  },
1239
- [markDeckDirty, setDecksWithHistory],
1637
+ [markDeckDirty, recordUndo, setDecksLocal],
1240
1638
  );
1241
1639
 
1242
1640
  const reorderSlides = useCallback(
1243
1641
  (deckId: string, oldIndex: number, newIndex: number) => {
1244
1642
  markDeckDirty(deckId);
1643
+ const before = decksRef.current.find((d) => d.id === deckId);
1245
1644
  let orderedIds: string[] | undefined;
1246
- setDecksWithHistory("Reorder slides", (prev) =>
1645
+ setDecksLocal((prev) =>
1247
1646
  prev.map((d) => {
1248
1647
  if (d.id !== deckId) return d;
1249
1648
  const slides = [...d.slides];
@@ -1256,28 +1655,45 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1256
1655
  if (orderedIds) {
1257
1656
  // Granular op — server reorders by slide ID rather than by index,
1258
1657
  // so concurrent adds from other writers don't get dropped.
1259
- enqueueDeckOp(deckId, { op: "reorder-slides", orderedIds });
1658
+ const op: PatchDeckOp = { op: "reorder-slides", orderedIds };
1659
+ enqueueDeckOp(deckId, op);
1660
+ if (before) recordUndo(before, op, { label: "Reorder slides" });
1260
1661
  }
1261
1662
  },
1262
- [markDeckDirty, setDecksWithHistory],
1663
+ [markDeckDirty, recordUndo, setDecksLocal],
1263
1664
  );
1264
1665
 
1265
1666
  const setDeckSlides = useCallback(
1266
1667
  (deckId: string, slides: Slide[]) => {
1267
1668
  markDeckDirty(deckId);
1268
- setDecksWithHistory("Generate slides", (prev) =>
1669
+ const before = decksRef.current.find((deck) => deck.id === deckId);
1670
+ const after = before
1671
+ ? { ...before, slides, updatedAt: new Date().toISOString() }
1672
+ : null;
1673
+ // setDeckSlides replaces ALL slides wholesale (used by AI generation and
1674
+ // imports), so its undo entry is a deck-level full replacement instead of
1675
+ // a fine-grained slide patch.
1676
+ setDecksLocal((prev) =>
1269
1677
  prev.map((d) => {
1270
1678
  if (d.id !== deckId) return d;
1271
- const updated = { ...d, slides, updatedAt: new Date().toISOString() };
1272
- // setDeckSlides replaces ALL slides wholesale (used by AI generation
1273
- // and imports). Use a full-replace so the server state always exactly
1274
- // matches the generated result, regardless of any concurrent changes.
1275
- enqueueDeckOp(deckId, { op: "full-replace", deck: updated });
1276
- return updated;
1679
+ const next = after ?? {
1680
+ ...d,
1681
+ slides,
1682
+ updatedAt: new Date().toISOString(),
1683
+ };
1684
+ enqueueDeckOp(deckId, { op: "full-replace", deck: next });
1685
+ return next;
1277
1686
  }),
1278
1687
  );
1688
+ if (before && after) {
1689
+ undoControllerRef.current?.push({
1690
+ undo: [{ op: "replace-deck", deckId, deck: before }],
1691
+ redo: [{ op: "replace-deck", deckId, deck: after }],
1692
+ label: "Replace slides",
1693
+ });
1694
+ }
1279
1695
  },
1280
- [markDeckDirty, setDecksWithHistory],
1696
+ [markDeckDirty, setDecksLocal],
1281
1697
  );
1282
1698
 
1283
1699
  return (
@@ -1301,11 +1717,8 @@ export function DeckProvider({ children }: { children: ReactNode }) {
1301
1717
  markDeckDirty,
1302
1718
  undo,
1303
1719
  redo,
1304
- canUndo: historyIndex > 0,
1305
- canRedo: historyIndex < history.length - 1,
1306
- history,
1307
- historyIndex,
1308
- restoreFromHistory,
1720
+ canUndo,
1721
+ canRedo,
1309
1722
  }}
1310
1723
  >
1311
1724
  {children}