@agent-native/core-corpus 0.214.0 → 0.217.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1217) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +1 -1
  3. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
  4. package/corpus/templates/analytics/AGENTS.md +1 -1
  5. package/corpus/templates/analytics/CHANGELOG.md +2 -0
  6. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
  7. package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
  8. package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
  9. package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
  10. package/corpus/templates/analytics/actions/view-screen.ts +29 -3
  11. package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
  12. package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
  13. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
  14. package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
  15. package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
  16. package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
  17. package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
  18. package/corpus/templates/analytics/changelog/2026-10-21-session-replay-quotas-count-the-compressed-size-analytics-st.md +6 -0
  19. package/corpus/templates/analytics/server/db/schema.ts +67 -0
  20. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
  21. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
  22. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
  23. package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
  24. package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
  25. package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
  26. package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
  27. package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
  28. package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
  29. package/corpus/templates/analytics/server/lib/session-replay.ts +85 -40
  30. package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
  31. package/corpus/templates/analytics/server/plugins/db.ts +46 -0
  32. package/corpus/templates/analytics/shared/output-use.ts +205 -0
  33. package/corpus/templates/analytics/shared/session-events.ts +6 -0
  34. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +1 -1
  35. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +1 -1
  36. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +1 -1
  37. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  38. package/corpus/templates/calendar/actions/create-event.ts +2 -4
  39. package/corpus/templates/calendar/actions/update-event.ts +35 -6
  40. package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
  41. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +1 -1
  42. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +1 -1
  43. package/corpus/templates/community/qr/package.json +1 -1
  44. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +1 -1
  45. package/corpus/templates/content/.agents/skills/content-product-development/references/verification.md +36 -0
  46. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +10 -0
  47. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +17 -3
  48. package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +4 -2
  49. package/corpus/templates/content/CHANGELOG.md +54 -0
  50. package/corpus/templates/content/acceptance/_writers.ts +604 -0
  51. package/corpus/templates/content/acceptance/generate-action-registry.ts +10 -0
  52. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/create-document.json +75 -0
  53. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/duplicate-page.json +75 -0
  54. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/edit-document-find-and-replace-without-a-revision.json +75 -0
  55. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/edit-document-find-and-replace.json +75 -0
  56. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/edit-document-initializecontent.json +75 -0
  57. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/import-content.json +75 -0
  58. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/local-files-import-content-source-existing-page.json +75 -0
  59. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/local-files-import-content-source-new-page.json +75 -0
  60. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/local-files-sync-local-folder-source-changed-file.json +75 -0
  61. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/local-files-sync-local-folder-source-new-file.json +75 -0
  62. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/restore-document-version.json +75 -0
  63. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/set-document-property-primary-blocks-field.json +75 -0
  64. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/set-image-alt-text.json +75 -0
  65. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/undo-content-import.json +75 -0
  66. package/corpus/templates/content/acceptance/golden/gate-off-stored-bytes/update-document-browser-full-rewrite.json +75 -0
  67. package/corpus/templates/content/acceptance/known-loss.ts +69 -0
  68. package/corpus/templates/content/acceptance/round-trip-corpus.ts +1397 -0
  69. package/corpus/templates/content/actions/_blocks-field-identity.ts +35 -1
  70. package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
  71. package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
  72. package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
  73. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
  74. package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
  75. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
  76. package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
  77. package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
  78. package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
  79. package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
  80. package/corpus/templates/content/actions/_content-database-lifecycle.ts +11 -11
  81. package/corpus/templates/content/actions/_content-database-personal-view.ts +2 -0
  82. package/corpus/templates/content/actions/_content-favorites.ts +37 -0
  83. package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
  84. package/corpus/templates/content/actions/_database-membership-lock.ts +39 -8
  85. package/corpus/templates/content/actions/_database-navigation.ts +19 -21
  86. package/corpus/templates/content/actions/_database-row-batch.ts +41 -18
  87. package/corpus/templates/content/actions/_database-row-mutation.ts +17 -28
  88. package/corpus/templates/content/actions/_database-source-utils.ts +308 -9
  89. package/corpus/templates/content/actions/_database-utils.ts +96 -7
  90. package/corpus/templates/content/actions/_database-view-setup.ts +37 -2
  91. package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
  92. package/corpus/templates/content/actions/_document-edit-mutation.ts +33 -32
  93. package/corpus/templates/content/actions/_document-flush.ts +593 -171
  94. package/corpus/templates/content/actions/_document-save-locks.ts +54 -0
  95. package/corpus/templates/content/actions/_document-save-row.ts +133 -0
  96. package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
  97. package/corpus/templates/content/actions/_github-source.ts +18 -8
  98. package/corpus/templates/content/actions/_move-page-to-space.ts +189 -25
  99. package/corpus/templates/content/actions/_nfm-syntax.ts +2 -0
  100. package/corpus/templates/content/actions/_row-lock-order.ts +115 -0
  101. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
  102. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
  103. package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
  104. package/corpus/templates/content/actions/create-content-database.ts +11 -0
  105. package/corpus/templates/content/actions/create-document.ts +13 -1
  106. package/corpus/templates/content/actions/delete-content-database.ts +17 -14
  107. package/corpus/templates/content/actions/delete-document-property.ts +5 -0
  108. package/corpus/templates/content/actions/delete-document.ts +311 -100
  109. package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
  110. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
  111. package/corpus/templates/content/actions/edit-document.ts +18 -5
  112. package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
  113. package/corpus/templates/content/actions/export-document.ts +3 -0
  114. package/corpus/templates/content/actions/get-blocks-field-word-count.ts +22 -5
  115. package/corpus/templates/content/actions/get-document.ts +2 -0
  116. package/corpus/templates/content/actions/import-content-source.ts +170 -54
  117. package/corpus/templates/content/actions/move-document.ts +191 -73
  118. package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
  119. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
  120. package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
  121. package/corpus/templates/content/actions/pull-document.ts +8 -2
  122. package/corpus/templates/content/actions/remove-database-items.ts +23 -4
  123. package/corpus/templates/content/actions/restore-content-database.ts +76 -55
  124. package/corpus/templates/content/actions/restore-document-version.ts +19 -35
  125. package/corpus/templates/content/actions/restore-document.ts +65 -53
  126. package/corpus/templates/content/actions/rollback-created-slash-document.ts +11 -16
  127. package/corpus/templates/content/actions/set-document-property.ts +15 -1
  128. package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
  129. package/corpus/templates/content/actions/share-local-file-document.ts +18 -14
  130. package/corpus/templates/content/actions/suggest-document-edit.ts +2 -3
  131. package/corpus/templates/content/actions/sync-local-folder-source.ts +35 -1
  132. package/corpus/templates/content/actions/undo-content-import.ts +16 -12
  133. package/corpus/templates/content/actions/update-content-database-view.ts +2 -0
  134. package/corpus/templates/content/actions/update-document.ts +158 -34
  135. package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
  136. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -6
  137. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +49 -3
  138. package/corpus/templates/content/app/components/editor/ContentImportDialog.tsx +2 -0
  139. package/corpus/templates/content/app/components/editor/DocumentBlockFields.tsx +53 -11
  140. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +608 -353
  141. package/corpus/templates/content/app/components/editor/StaticDocumentBody.tsx +765 -0
  142. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +233 -80
  143. package/corpus/templates/content/app/components/editor/blockFieldSaveController.ts +32 -3
  144. package/corpus/templates/content/app/components/editor/blockFieldSaveRegistry.ts +88 -23
  145. package/corpus/templates/content/app/components/editor/comment-anchors.ts +19 -0
  146. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +228 -71
  147. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
  148. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
  149. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
  150. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
  151. package/corpus/templates/content/app/components/editor/document-flush-participant.ts +253 -0
  152. package/corpus/templates/content/app/components/editor/editor-transaction-meta.ts +28 -0
  153. package/corpus/templates/content/app/components/editor/extensions/AudioBlock.tsx +2 -1
  154. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +27 -6
  155. package/corpus/templates/content/app/components/editor/extensions/LocalMdxComponentNode.tsx +0 -1
  156. package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +4 -4
  157. package/corpus/templates/content/app/components/editor/extensions/SuggestionHighlight.ts +24 -2
  158. package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +25 -6
  159. package/corpus/templates/content/app/components/editor/pending-document-persistence.ts +59 -1
  160. package/corpus/templates/content/app/components/editor/stored-body-images.ts +27 -0
  161. package/corpus/templates/content/app/components/editor/suggestions/presentation-rebase.ts +17 -0
  162. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -4
  163. package/corpus/templates/content/app/components/trash/TrashBrowser.tsx +4 -1
  164. package/corpus/templates/content/app/global.css +21 -0
  165. package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
  166. package/corpus/templates/content/app/hooks/use-content-trash.ts +8 -0
  167. package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
  168. package/corpus/templates/content/app/i18n-data.ts +1 -0
  169. package/corpus/templates/content/app/import-messages.ts +25 -0
  170. package/corpus/templates/content/app/trash-messages.ts +16 -0
  171. package/corpus/templates/content/changelog/2026-10-16-a-page-open-in-a-tab-that-shows-it-s-in-the-trash-reopens-wi.md +6 -0
  172. package/corpus/templates/content/changelog/2026-10-16-a-page-restored-from-trash-returns-to-its-place-in-its-colle.md +6 -0
  173. package/corpus/templates/content/changelog/2026-10-16-agent-written-markdown-keeps-nested-lists-indented-with-spac.md +6 -0
  174. package/corpus/templates/content/changelog/2026-10-16-opening-a-page-you-can-edit-no-longer-rebuilds-its-editor-on.md +6 -0
  175. package/corpus/templates/content/changelog/2026-10-16-pages-moved-or-imported-at-the-same-time-can-no-longer-end-u.md +6 -0
  176. package/corpus/templates/content/changelog/2026-10-16-re-importing-a-markdown-export-of-a-page-no-longer-reports-t.md +6 -0
  177. package/corpus/templates/content/changelog/2026-10-16-saving-a-page-or-restoring-one-of-its-versions-no-longer-fai.md +6 -0
  178. package/corpus/templates/content/changelog/2026-10-17-agents-reading-a-page-you-have-open-no-longer-wait-or-fail-w.md +6 -0
  179. package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
  180. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
  181. package/corpus/templates/content/changelog/2026-10-19-fresh-document-reads-require-confirmation-from-every-open-ed.md +6 -0
  182. package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
  183. package/corpus/templates/content/changelog/2026-10-20-html-table-cells-preserve-a-trailing-backslash-when-opening-.md +6 -0
  184. package/corpus/templates/content/changelog/2026-10-20-preserve-empty-paragraph-colors-and-refuse-unsupported-inlin.md +6 -0
  185. package/corpus/templates/content/changelog/2026-10-20-the-advanced-filter-and-each-filter-group-inside-it-have-the.md +6 -0
  186. package/corpus/templates/content/changelog/2026-10-21-blocks-fields-keep-unconfirmed-saves-visible-and-agent-reads.md +6 -0
  187. package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
  188. package/corpus/templates/content/changelog/2026-10-21-preserve-compact-table-row-groups-and-report-invalid-block-colors.md +6 -0
  189. package/corpus/templates/content/changelog/2026-10-21-suggested-edits-show-an-agent-s-whole-reason-open-a-group-to.md +6 -0
  190. package/corpus/templates/content/changelog/2026-10-21-suggestions-keep-refusing-unmatched-emphasis-in-stored-markd.md +6 -0
  191. package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
  192. package/corpus/templates/content/changelog/2026-10-22-accept-remaining-and-reject-remaining-show-the-result-for-ev.md +6 -0
  193. package/corpus/templates/content/changelog/2026-10-22-accepting-a-group-of-suggested-edits-saves-faster-and-adds-o.md +6 -0
  194. package/corpus/templates/content/changelog/2026-10-22-agents-read-saved-page-text-after-failed-icon-change.md +6 -0
  195. package/corpus/templates/content/changelog/2026-10-22-changes-made-from-a-menu-or-popover-such-as-an-image-s-alt-t.md +6 -0
  196. package/corpus/templates/content/changelog/2026-10-22-text-an-agent-writes-inside-a-callout-column-or-synced-block.md +6 -0
  197. package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
  198. package/corpus/templates/content/e2e/auth-files.ts +28 -0
  199. package/corpus/templates/content/e2e/convergence-summary.ts +84 -29
  200. package/corpus/templates/content/e2e/gate-arm.ts +15 -0
  201. package/corpus/templates/content/e2e/global-setup.ts +43 -75
  202. package/corpus/templates/content/e2e/global-teardown.ts +34 -0
  203. package/corpus/templates/content/e2e/helpers.ts +598 -92
  204. package/corpus/templates/content/e2e/live-body-gate.ts +188 -0
  205. package/corpus/templates/content/e2e/playwright.config.ts +12 -3
  206. package/corpus/templates/content/e2e/server-identity.ts +47 -0
  207. package/corpus/templates/content/e2e/stored-form/collab-blocks.ts +123 -0
  208. package/corpus/templates/content/package.json +1 -1
  209. package/corpus/templates/content/parity/matrix.md +42 -42
  210. package/corpus/templates/content/parity/matrix.ts +3 -1
  211. package/corpus/templates/content/scripts/_stored-form-census-kinds.ts +30 -2
  212. package/corpus/templates/content/scripts/historical-icon-apply.ts +9 -1
  213. package/corpus/templates/content/scripts/trace-startup.mjs +460 -133
  214. package/corpus/templates/content/server/db/exec.ts +58 -0
  215. package/corpus/templates/content/server/db/schema.ts +21 -0
  216. package/corpus/templates/content/server/lib/content-trash-purge.ts +222 -144
  217. package/corpus/templates/content/server/lib/document-metadata-updated-at.ts +43 -1
  218. package/corpus/templates/content/server/lib/document-revision.ts +23 -0
  219. package/corpus/templates/content/server/lib/live-body-writer.ts +311 -0
  220. package/corpus/templates/content/server/lib/notion-sync.ts +63 -29
  221. package/corpus/templates/content/server/lib/notion.ts +5 -5
  222. package/corpus/templates/content/server/lib/suggested-edits.ts +357 -230
  223. package/corpus/templates/content/server/live-copy/binding.ts +31 -0
  224. package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
  225. package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
  226. package/corpus/templates/content/server/live-copy/holds.ts +59 -0
  227. package/corpus/templates/content/server/live-copy/persist.ts +656 -0
  228. package/corpus/templates/content/server/live-copy/switch.ts +136 -0
  229. package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
  230. package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
  231. package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
  232. package/corpus/templates/content/server/live-copy/write.ts +85 -0
  233. package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
  234. package/corpus/templates/content/server/plugins/collab.ts +3 -0
  235. package/corpus/templates/content/server/plugins/db.ts +33 -0
  236. package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +50 -168
  237. package/corpus/templates/content/shared/api.ts +32 -1
  238. package/corpus/templates/content/shared/blocks-fields-manifest.ts +106 -0
  239. package/corpus/templates/content/shared/database-item-layout.ts +54 -22
  240. package/corpus/templates/content/shared/database-query.ts +192 -30
  241. package/corpus/templates/content/shared/document-export.ts +12 -14
  242. package/corpus/templates/content/shared/document-flush-protocol.ts +88 -0
  243. package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
  244. package/corpus/templates/content/shared/editor-save-state.ts +97 -0
  245. package/corpus/templates/content/shared/feature-flags.ts +10 -0
  246. package/corpus/templates/content/shared/import/markdown.ts +91 -7
  247. package/corpus/templates/content/shared/import/types.ts +2 -0
  248. package/corpus/templates/content/shared/inline-math.ts +6 -4
  249. package/corpus/templates/content/shared/nfm-fidelity.ts +247 -0
  250. package/corpus/templates/content/shared/nfm.ts +1933 -340
  251. package/corpus/templates/content/shared/og-description.ts +258 -36
  252. package/corpus/templates/content/shared/suggestion-diff.ts +13 -0
  253. package/corpus/templates/content/shared/suggestion-formatting.ts +143 -20
  254. package/corpus/templates/content/shared/suggestion-text.ts +39 -0
  255. package/corpus/templates/content/vitest.config.ts +10 -0
  256. package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +1 -1
  257. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +1 -1
  258. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
  259. package/corpus/templates/design/.agents/skills/native-effects/SKILL.md +9 -2
  260. package/corpus/templates/design/.generated/bridge/shader-fill-preview.generated.ts +3 -249
  261. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +30 -92
  262. package/corpus/templates/design/AGENTS.md +1 -3
  263. package/corpus/templates/design/CHANGELOG.md +34 -0
  264. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -6
  265. package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -7
  266. package/corpus/templates/design/actions/_native-local-export-server.ts +44 -31
  267. package/corpus/templates/design/actions/_native-render-contexts.ts +76 -21
  268. package/corpus/templates/design/actions/claim-native-local-export.ts +5 -0
  269. package/corpus/templates/design/actions/finish-native-local-export.ts +13 -2
  270. package/corpus/templates/design/actions/get-getting-started.ts +53 -0
  271. package/corpus/templates/design/actions/get-native-shader-approvals.ts +35 -0
  272. package/corpus/templates/design/actions/get-shader.ts +134 -219
  273. package/corpus/templates/design/actions/list-design-extensions.ts +3 -58
  274. package/corpus/templates/design/actions/request-native-local-export.ts +32 -6
  275. package/corpus/templates/design/actions/run-design-extension-action.ts +0 -29
  276. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +141 -108
  277. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +0 -218
  278. package/corpus/templates/design/app/components/design/DesignThumbnail.tsx +59 -3
  279. package/corpus/templates/design/app/components/design/EditPanel.tsx +35 -47
  280. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +242 -62
  281. package/corpus/templates/design/app/components/design/NativePreviewErrorBoundary.tsx +53 -0
  282. package/corpus/templates/design/app/components/design/bridge/codegen.ts +18 -3
  283. package/corpus/templates/design/app/components/design/bridge/native-composition-clock.ts +5 -4
  284. package/corpus/templates/design/app/components/design/bridge/native-dom.ts +37 -0
  285. package/corpus/templates/design/app/components/design/bridge/native-intrinsic-image.ts +2 -1
  286. package/corpus/templates/design/app/components/design/bridge/native-mesh-grid.ts +90 -0
  287. package/corpus/templates/design/app/components/design/bridge/native-opaque-public-asset.ts +363 -0
  288. package/corpus/templates/design/app/components/design/bridge/native-opaque-worker-lease.ts +179 -0
  289. package/corpus/templates/design/app/components/design/bridge/native-preview-geometry.ts +132 -7
  290. package/corpus/templates/design/app/components/design/bridge/native-preview-policy.ts +4 -0
  291. package/corpus/templates/design/app/components/design/bridge/native-preview-viewport-transport.ts +378 -0
  292. package/corpus/templates/design/app/components/design/bridge/native-scene-body-transform.ts +62 -0
  293. package/corpus/templates/design/app/components/design/bridge/native-scene-presentation-element.ts +10 -0
  294. package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +778 -246
  295. package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +512 -320
  296. package/corpus/templates/design/app/components/design/bridge/native-stateful-resource.ts +2 -2
  297. package/corpus/templates/design/app/components/design/bridge/native-stateless-compute.ts +5 -2
  298. package/corpus/templates/design/app/components/design/bridge/native-svg-source.ts +11 -1
  299. package/corpus/templates/design/app/components/design/bridge/native-worker-broker.ts +6 -0
  300. package/corpus/templates/design/app/components/design/bridge/native-worker-entry.ts +761 -303
  301. package/corpus/templates/design/app/components/design/bridge/native-worker-fill.ts +16 -6
  302. package/corpus/templates/design/app/components/design/bridge/native-worker-gpu.ts +854 -91
  303. package/corpus/templates/design/app/components/design/bridge/native-worker-host.ts +15 -1
  304. package/corpus/templates/design/app/components/design/bridge/native-worker-input-assets.ts +200 -0
  305. package/corpus/templates/design/app/components/design/bridge/native-worker-protocol.ts +40 -1
  306. package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +540 -161
  307. package/corpus/templates/design/app/components/design/bridge/native-worker-source-snapshot.ts +21 -0
  308. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +5 -371
  309. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +45 -150
  310. package/corpus/templates/design/app/components/design/design-canvas/interact-document.ts +5 -9
  311. package/corpus/templates/design/app/components/design/design-canvas/native-preview-surface.ts +150 -0
  312. package/corpus/templates/design/app/components/design/design-canvas/native-public-asset-preview.ts +409 -0
  313. package/corpus/templates/design/app/components/design/design-canvas/native-public-worker-preview.ts +1086 -0
  314. package/corpus/templates/design/app/components/design/design-canvas/private-replay-screenshot-preview.ts +2 -2
  315. package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
  316. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +15 -26
  317. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +74 -67
  318. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +7 -19
  319. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +39 -30
  320. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +4 -0
  321. package/corpus/templates/design/app/components/design/inspector/NativeShaderColorControls.tsx +3 -7
  322. package/corpus/templates/design/app/components/design/inspector/NativeShaderPaintControls.tsx +12 -5
  323. package/corpus/templates/design/app/components/design/inspector/NativeShaderPanel.tsx +705 -190
  324. package/corpus/templates/design/app/components/design/inspector/fill-field-reading.ts +1 -2
  325. package/corpus/templates/design/app/components/design/inspector/index.ts +0 -6
  326. package/corpus/templates/design/app/components/design/inspector/native-catalog-display.ts +26 -5
  327. package/corpus/templates/design/app/components/design/inspector/native-shader-catalog-fixture.ts +42 -0
  328. package/corpus/templates/design/app/components/design/inspector/native-shader-catalog-preview.ts +51 -33
  329. package/corpus/templates/design/app/components/design/inspector/native-shader-paint.ts +1 -0
  330. package/corpus/templates/design/app/components/design/inspector/native-shader-property-draft.ts +89 -23
  331. package/corpus/templates/design/app/components/design/inspector/saved-glsl-shader.tsx +243 -0
  332. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +54 -5
  333. package/corpus/templates/design/app/components/design/shader-source-coordination.ts +66 -0
  334. package/corpus/templates/design/app/components/design/use-native-shader-approvals.ts +36 -8
  335. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
  336. package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
  337. package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
  338. package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
  339. package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
  340. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +66 -5
  341. package/corpus/templates/design/app/global.css +6 -0
  342. package/corpus/templates/design/app/i18n/en-US.ts +95 -2
  343. package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
  344. package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
  345. package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
  346. package/corpus/templates/design/app/pages/Index.tsx +10 -3
  347. package/corpus/templates/design/app/pages/Present.tsx +36 -1
  348. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +1 -1
  349. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +1 -1
  350. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +88 -32
  351. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +1 -1
  352. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +1 -1
  353. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +1 -1
  354. package/corpus/templates/design/app/pages/design-editor/commands/paste-svg-layer.ts +1 -1
  355. package/corpus/templates/design/app/pages/design-editor/commands/prepare-content-history-replay.ts +1 -1
  356. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1 -1
  357. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +84 -41
  358. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
  359. package/corpus/templates/design/app/pages/design-editor/commands/shader-source-edit.ts +1 -1
  360. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +1 -1
  361. package/corpus/templates/design/app/pages/design-editor/design-editor-view.tsx +0 -2
  362. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-content-and-components.ts +4 -33
  363. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
  364. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-export-and-handoff.ts +2 -2
  365. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
  366. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-history.ts +0 -11
  367. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-layer-models.ts +2 -55
  368. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-screen-rendering.tsx +0 -3
  369. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-selection-and-styles.ts +0 -24
  370. package/corpus/templates/design/app/pages/design-editor/text-edit-utils.ts +0 -16
  371. package/corpus/templates/design/app/pages/design-editor/use-native-local-export-requests.ts +70 -11
  372. package/corpus/templates/design/app/pages/design-editor/view/single-screen-canvas.tsx +0 -2
  373. package/corpus/templates/design/changelog/2026-10-20-folded-mesh-adds-an-animated-sculpted-fill-with-editable-col.md +6 -0
  374. package/corpus/templates/design/changelog/2026-10-20-read-only-design-views-render-native-shader-previews-in-nest.md +6 -0
  375. package/corpus/templates/design/changelog/2026-10-20-selected-screens-with-shader-effects-can-be-exported-togethe.md +6 -0
  376. package/corpus/templates/design/changelog/2026-10-20-shader-previews-reuse-validated-scene-data-while-panning-inc.md +6 -0
  377. package/corpus/templates/design/changelog/2026-10-20-shader-previews-stay-aligned-while-you-zoom.md +6 -0
  378. package/corpus/templates/design/changelog/2026-10-20-signed-out-sessions-no-longer-watch-for-private-png-export-r.md +6 -0
  379. package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
  380. package/corpus/templates/design/changelog/2026-10-21-effect-thumbnails-show-richer-previews.md +6 -0
  381. package/corpus/templates/design/changelog/2026-10-21-native-effects-render-across-previews.md +6 -0
  382. package/corpus/templates/design/changelog/2026-10-21-native-shader-previews-use-less-gpu-memory-when-many-mesh-ef.md +6 -0
  383. package/corpus/templates/design/changelog/2026-10-21-opening-design-no-longer-shows-png-export-errors-when-no-exp.md +6 -0
  384. package/corpus/templates/design/changelog/2026-10-21-pulsing-border-controls-grouped.md +6 -0
  385. package/corpus/templates/design/changelog/2026-10-21-pulsing-border-follows-element-outlines.md +6 -0
  386. package/corpus/templates/design/changelog/2026-10-21-remove-obsolete-shader-fills.md +6 -0
  387. package/corpus/templates/design/changelog/2026-10-21-shader-previews-recover-from-temporary-gpu-and-image-failure.md +6 -0
  388. package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
  389. package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
  390. package/corpus/templates/design/changelog/2026-10-22-shader-fills-and-effects-render-correctly-on-canvas-elements.md +6 -0
  391. package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
  392. package/corpus/templates/design/package.json +0 -1
  393. package/corpus/templates/design/playwright.config.ts +1 -1
  394. package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
  395. package/corpus/templates/design/scripts/runtime-budget-zoom.ts +9 -21
  396. package/corpus/templates/design/scripts/runtime-budget.ts +10 -24
  397. package/corpus/templates/design/server/lib/getting-started.ts +267 -0
  398. package/corpus/templates/design/server/plugins/agent-chat.ts +0 -2
  399. package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
  400. package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
  401. package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
  402. package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
  403. package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
  404. package/corpus/templates/design/shared/design-template-presets.ts +8 -0
  405. package/corpus/templates/design/shared/effect-graph.ts +46 -1
  406. package/corpus/templates/design/shared/generation-output-receipt.ts +0 -1
  407. package/corpus/templates/design/shared/native-effect-animation-capability.ts +6 -0
  408. package/corpus/templates/design/shared/native-effect-folded-mesh-v1.ts +268 -0
  409. package/corpus/templates/design/shared/native-effect-folded-mesh-v2.ts +310 -0
  410. package/corpus/templates/design/shared/native-effect-folded-mesh.ts +489 -0
  411. package/corpus/templates/design/shared/native-effect-gallery-additions.ts +2 -2
  412. package/corpus/templates/design/shared/native-effect-paint.ts +8 -0
  413. package/corpus/templates/design/shared/native-effect-parameter-constraints.ts +8 -1
  414. package/corpus/templates/design/shared/native-effect-presets.ts +18 -1
  415. package/corpus/templates/design/shared/native-effect-property-presentation.ts +81 -0
  416. package/corpus/templates/design/shared/native-effect-pulsing-border-v3.ts +281 -0
  417. package/corpus/templates/design/shared/native-effect-pulsing-border-v4.ts +284 -0
  418. package/corpus/templates/design/shared/native-effect-pulsing-border.ts +300 -0
  419. package/corpus/templates/design/shared/native-effect-trust.ts +6 -0
  420. package/corpus/templates/design/shared/native-effect-wgsl.ts +12 -0
  421. package/corpus/templates/design/shared/native-effects.ts +528 -15
  422. package/corpus/templates/design/shared/native-input-resources.ts +36 -0
  423. package/corpus/templates/design/shared/native-local-export.ts +14 -0
  424. package/corpus/templates/design/shared/native-parameter-animation.ts +1 -0
  425. package/corpus/templates/design/shared/native-pointer-input.ts +9 -11
  426. package/corpus/templates/design/shared/shader-fills.ts +0 -513
  427. package/corpus/templates/design/vitest.setup.ts +7 -1
  428. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +1 -1
  429. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +20 -18
  430. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +1 -1
  431. package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +36 -80
  432. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +54 -16
  433. package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +50 -73
  434. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +20 -18
  435. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +1 -1
  436. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  437. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +1 -1
  438. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +1 -1
  439. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +1 -1
  440. package/corpus/templates/slides/CHANGELOG.md +6 -0
  441. package/corpus/templates/slides/actions/delete-deck.ts +7 -0
  442. package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
  443. package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
  444. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
  445. package/corpus/templates/slides-2/.agents/skills/actions/SKILL.md +199 -0
  446. package/corpus/templates/slides-2/.agents/skills/actions/references/action-fields.md +145 -0
  447. package/corpus/templates/slides-2/.agents/skills/actions/references/examples.md +96 -0
  448. package/corpus/templates/slides-2/.agents/skills/actions/references/provider-apis.md +95 -0
  449. package/corpus/templates/slides-2/.agents/skills/adding-a-feature/SKILL.md +235 -0
  450. package/corpus/templates/slides-2/.agents/skills/agent-native-docs/SKILL.md +161 -0
  451. package/corpus/templates/slides-2/.agents/skills/agent-native-toolkit/SKILL.md +314 -0
  452. package/corpus/templates/slides-2/.agents/skills/analytics-data-for-decks/SKILL.md +44 -0
  453. package/corpus/templates/slides-2/.agents/skills/apply-slide-comments/SKILL.md +63 -0
  454. package/corpus/templates/slides-2/.agents/skills/client-side-routing/SKILL.md +87 -0
  455. package/corpus/templates/slides-2/.agents/skills/context-awareness/SKILL.md +282 -0
  456. package/corpus/templates/slides-2/.agents/skills/create-deck/SKILL.md +263 -0
  457. package/corpus/templates/slides-2/.agents/skills/create-deck/references/reference-inputs.md +72 -0
  458. package/corpus/templates/slides-2/.agents/skills/create-deck/references/slide-templates.md +93 -0
  459. package/corpus/templates/slides-2/.agents/skills/create-deck/references/source-material.md +16 -0
  460. package/corpus/templates/slides-2/.agents/skills/creative-context/SKILL.md +134 -0
  461. package/corpus/templates/slides-2/.agents/skills/customizing-agent-native/SKILL.md +219 -0
  462. package/corpus/templates/slides-2/.agents/skills/deck-management/SKILL.md +182 -0
  463. package/corpus/templates/slides-2/.agents/skills/delegate-to-agent/SKILL.md +62 -0
  464. package/corpus/templates/slides-2/.agents/skills/design-systems/SKILL.md +233 -0
  465. package/corpus/templates/slides-2/.agents/skills/external-agents/SKILL.md +549 -0
  466. package/corpus/templates/slides-2/.agents/skills/external-agents/references/mcp-apps-embedding.md +169 -0
  467. package/corpus/templates/slides-2/.agents/skills/frontend-design/SKILL.md +102 -0
  468. package/corpus/templates/slides-2/.agents/skills/frontend-design/references/visual-direction.md +102 -0
  469. package/corpus/templates/slides-2/.agents/skills/image-generation-via-a2a/SKILL.md +134 -0
  470. package/corpus/templates/slides-2/.agents/skills/performance/SKILL.md +79 -0
  471. package/corpus/templates/slides-2/.agents/skills/portability/SKILL.md +108 -0
  472. package/corpus/templates/slides-2/.agents/skills/real-time-sync/SKILL.md +146 -0
  473. package/corpus/templates/slides-2/.agents/skills/reliable-mutations/SKILL.md +72 -0
  474. package/corpus/templates/slides-2/.agents/skills/secrets/SKILL.md +82 -0
  475. package/corpus/templates/slides-2/.agents/skills/security/SKILL.md +80 -0
  476. package/corpus/templates/slides-2/.agents/skills/self-modifying-code/SKILL.md +123 -0
  477. package/corpus/templates/slides-2/.agents/skills/shadcn-ui/SKILL.md +71 -0
  478. package/corpus/templates/slides-2/.agents/skills/sharing/SKILL.md +286 -0
  479. package/corpus/templates/slides-2/.agents/skills/slide-design/SKILL.md +144 -0
  480. package/corpus/templates/slides-2/.agents/skills/slide-editing/SKILL.md +284 -0
  481. package/corpus/templates/slides-2/.agents/skills/slide-editing/references/animations.md +33 -0
  482. package/corpus/templates/slides-2/.agents/skills/slide-editing/references/freeform-objects.md +22 -0
  483. package/corpus/templates/slides-2/.agents/skills/slide-editing/references/restyling.md +69 -0
  484. package/corpus/templates/slides-2/.agents/skills/slide-editing/references/transitions.md +26 -0
  485. package/corpus/templates/slides-2/.agents/skills/slide-editing/references/video.md +45 -0
  486. package/corpus/templates/slides-2/.agents/skills/slide-images/SKILL.md +136 -0
  487. package/corpus/templates/slides-2/.agents/skills/storing-data/SKILL.md +234 -0
  488. package/corpus/templates/slides-2/.agents/skills/turn-into-skill/SKILL.md +112 -0
  489. package/corpus/templates/slides-2/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  490. package/corpus/templates/slides-2/.agents/skills/workspace-conventions/SKILL.md +103 -0
  491. package/corpus/templates/slides-2/.builder/rules/deploy-app.mdc +22 -0
  492. package/corpus/templates/slides-2/.builder/rules/organize-ui.mdc +9 -0
  493. package/corpus/templates/slides-2/.dockerignore +61 -0
  494. package/corpus/templates/slides-2/.env.example +23 -0
  495. package/corpus/templates/slides-2/.ignore +3 -0
  496. package/corpus/templates/slides-2/.mcp.json +8 -0
  497. package/corpus/templates/slides-2/.oxfmtrc.json +8 -0
  498. package/corpus/templates/slides-2/AGENTS.md +66 -0
  499. package/corpus/templates/slides-2/CHANGELOG.md +76 -0
  500. package/corpus/templates/slides-2/DEVELOPING.md +157 -0
  501. package/corpus/templates/slides-2/README.md +58 -0
  502. package/corpus/templates/slides-2/THIRD_PARTY_NOTICES.md +33 -0
  503. package/corpus/templates/slides-2/_gitignore +38 -0
  504. package/corpus/templates/slides-2/actions/_agent-presence.ts +29 -0
  505. package/corpus/templates/slides-2/actions/_app-url.ts +37 -0
  506. package/corpus/templates/slides-2/actions/_deck-app-scope.ts +30 -0
  507. package/corpus/templates/slides-2/actions/_deck-tracking.ts +260 -0
  508. package/corpus/templates/slides-2/actions/_deck-write.ts +206 -0
  509. package/corpus/templates/slides-2/actions/_imported-slide-source.ts +162 -0
  510. package/corpus/templates/slides-2/actions/_inline-image-storage.ts +1013 -0
  511. package/corpus/templates/slides-2/actions/_linked-design-system.ts +83 -0
  512. package/corpus/templates/slides-2/actions/_presenter-notes.ts +83 -0
  513. package/corpus/templates/slides-2/actions/_render-artifacts.ts +111 -0
  514. package/corpus/templates/slides-2/actions/_slide-source.ts +344 -0
  515. package/corpus/templates/slides-2/actions/_tab-state.ts +51 -0
  516. package/corpus/templates/slides-2/actions/_test-brandfetch.ts +31 -0
  517. package/corpus/templates/slides-2/actions/_uploaded-files.ts +114 -0
  518. package/corpus/templates/slides-2/actions/add-deck.ts +123 -0
  519. package/corpus/templates/slides-2/actions/add-slide-comment.ts +267 -0
  520. package/corpus/templates/slides-2/actions/add-slide.ts +888 -0
  521. package/corpus/templates/slides-2/actions/analyze-brand-assets.ts +66 -0
  522. package/corpus/templates/slides-2/actions/apply-design-system.ts +111 -0
  523. package/corpus/templates/slides-2/actions/approve-deck-access-request.ts +287 -0
  524. package/corpus/templates/slides-2/actions/audit-contrast.ts +143 -0
  525. package/corpus/templates/slides-2/actions/check-image-gen.ts +17 -0
  526. package/corpus/templates/slides-2/actions/clone-context-slide.ts +340 -0
  527. package/corpus/templates/slides-2/actions/clone-creative-context-deck.ts +143 -0
  528. package/corpus/templates/slides-2/actions/control-deck-history.ts +186 -0
  529. package/corpus/templates/slides-2/actions/control-presenter.ts +117 -0
  530. package/corpus/templates/slides-2/actions/control-slide-editor.ts +267 -0
  531. package/corpus/templates/slides-2/actions/create-deck-from-template.ts +274 -0
  532. package/corpus/templates/slides-2/actions/create-deck.ts +1243 -0
  533. package/corpus/templates/slides-2/actions/create-design-system.ts +131 -0
  534. package/corpus/templates/slides-2/actions/create-share-link.ts +127 -0
  535. package/corpus/templates/slides-2/actions/db-health.ts +22 -0
  536. package/corpus/templates/slides-2/actions/delete-asset.ts +154 -0
  537. package/corpus/templates/slides-2/actions/delete-deck.ts +427 -0
  538. package/corpus/templates/slides-2/actions/delete-design-system.ts +167 -0
  539. package/corpus/templates/slides-2/actions/delete-slide-comment.ts +143 -0
  540. package/corpus/templates/slides-2/actions/delete-staged-dataset.ts +30 -0
  541. package/corpus/templates/slides-2/actions/duplicate-deck.ts +299 -0
  542. package/corpus/templates/slides-2/actions/export-google-slides.ts +41 -0
  543. package/corpus/templates/slides-2/actions/export-html.ts +770 -0
  544. package/corpus/templates/slides-2/actions/export-pptx.ts +2203 -0
  545. package/corpus/templates/slides-2/actions/extract-pdf.ts +49 -0
  546. package/corpus/templates/slides-2/actions/generate-image-api.ts +671 -0
  547. package/corpus/templates/slides-2/actions/generate-image.ts +492 -0
  548. package/corpus/templates/slides-2/actions/get-agent-access-deck.ts +52 -0
  549. package/corpus/templates/slides-2/actions/get-deck-access-status.ts +130 -0
  550. package/corpus/templates/slides-2/actions/get-deck-design-tokens.ts +62 -0
  551. package/corpus/templates/slides-2/actions/get-deck-reference-context.ts +232 -0
  552. package/corpus/templates/slides-2/actions/get-deck-template.ts +38 -0
  553. package/corpus/templates/slides-2/actions/get-deck-version.ts +85 -0
  554. package/corpus/templates/slides-2/actions/get-deck.ts +562 -0
  555. package/corpus/templates/slides-2/actions/get-design-system-decode-job-status.ts +57 -0
  556. package/corpus/templates/slides-2/actions/get-design-system-tier-limit.ts +12 -0
  557. package/corpus/templates/slides-2/actions/get-design-system.ts +453 -0
  558. package/corpus/templates/slides-2/actions/get-google-docs-status.ts +106 -0
  559. package/corpus/templates/slides-2/actions/get-google-picker-token.ts +84 -0
  560. package/corpus/templates/slides-2/actions/get-home-suggestions.ts +52 -0
  561. package/corpus/templates/slides-2/actions/get-layout-overflows.ts +192 -0
  562. package/corpus/templates/slides-2/actions/get-slide-render.ts +125 -0
  563. package/corpus/templates/slides-2/actions/get-slide-source.ts +115 -0
  564. package/corpus/templates/slides-2/actions/get-slide-theme-preview.ts +64 -0
  565. package/corpus/templates/slides-2/actions/get-slide-theme.ts +44 -0
  566. package/corpus/templates/slides-2/actions/get-slides-2-upload-storage-status.ts +17 -0
  567. package/corpus/templates/slides-2/actions/get-slides-notification-prefs.ts +37 -0
  568. package/corpus/templates/slides-2/actions/get-workspace-defaults.ts +51 -0
  569. package/corpus/templates/slides-2/actions/image-gen-status.ts +35 -0
  570. package/corpus/templates/slides-2/actions/import-code.ts +26 -0
  571. package/corpus/templates/slides-2/actions/import-design-project.ts +49 -0
  572. package/corpus/templates/slides-2/actions/import-document.ts +125 -0
  573. package/corpus/templates/slides-2/actions/import-docx.ts +57 -0
  574. package/corpus/templates/slides-2/actions/import-file.ts +1273 -0
  575. package/corpus/templates/slides-2/actions/import-from-url.ts +25 -0
  576. package/corpus/templates/slides-2/actions/import-google-doc.ts +208 -0
  577. package/corpus/templates/slides-2/actions/import-google-font.ts +110 -0
  578. package/corpus/templates/slides-2/actions/import-google-slides-reference.ts +667 -0
  579. package/corpus/templates/slides-2/actions/import-pptx.ts +564 -0
  580. package/corpus/templates/slides-2/actions/import-svgl-logo.ts +115 -0
  581. package/corpus/templates/slides-2/actions/index-design-system-with-builder.ts +181 -0
  582. package/corpus/templates/slides-2/actions/index-uploaded-design-system-files.ts +77 -0
  583. package/corpus/templates/slides-2/actions/list-assets.ts +286 -0
  584. package/corpus/templates/slides-2/actions/list-deck-templates.ts +83 -0
  585. package/corpus/templates/slides-2/actions/list-deck-versions.ts +185 -0
  586. package/corpus/templates/slides-2/actions/list-decks.ts +591 -0
  587. package/corpus/templates/slides-2/actions/list-design-systems.ts +180 -0
  588. package/corpus/templates/slides-2/actions/list-slide-comments.ts +128 -0
  589. package/corpus/templates/slides-2/actions/list-slide-themes.ts +17 -0
  590. package/corpus/templates/slides-2/actions/list-staged-datasets.ts +14 -0
  591. package/corpus/templates/slides-2/actions/logo-config.ts +13 -0
  592. package/corpus/templates/slides-2/actions/navigate.ts +158 -0
  593. package/corpus/templates/slides-2/actions/patch-deck.ts +2378 -0
  594. package/corpus/templates/slides-2/actions/provider-api-catalog.ts +25 -0
  595. package/corpus/templates/slides-2/actions/provider-api-docs.ts +83 -0
  596. package/corpus/templates/slides-2/actions/provider-api-request.ts +222 -0
  597. package/corpus/templates/slides-2/actions/query-staged-dataset.ts +83 -0
  598. package/corpus/templates/slides-2/actions/read-composer-source.ts +108 -0
  599. package/corpus/templates/slides-2/actions/request-deck-access.ts +803 -0
  600. package/corpus/templates/slides-2/actions/restore-deck-version.ts +203 -0
  601. package/corpus/templates/slides-2/actions/run.ts +2 -0
  602. package/corpus/templates/slides-2/actions/save-deck.ts +313 -0
  603. package/corpus/templates/slides-2/actions/search-google-fonts.ts +61 -0
  604. package/corpus/templates/slides-2/actions/search-images.ts +30 -0
  605. package/corpus/templates/slides-2/actions/search-logos.ts +23 -0
  606. package/corpus/templates/slides-2/actions/search-svgl-logos.ts +42 -0
  607. package/corpus/templates/slides-2/actions/set-default-design-system.ts +88 -0
  608. package/corpus/templates/slides-2/actions/set-workspace-defaults.ts +61 -0
  609. package/corpus/templates/slides-2/actions/start-design-system-upload.ts +80 -0
  610. package/corpus/templates/slides-2/actions/sync-design-system-with-builder.ts +84 -0
  611. package/corpus/templates/slides-2/actions/toggle-slide-comment-reaction.ts +150 -0
  612. package/corpus/templates/slides-2/actions/transform-slide-objects.ts +1528 -0
  613. package/corpus/templates/slides-2/actions/update-asset.ts +499 -0
  614. package/corpus/templates/slides-2/actions/update-deck-aspect-ratio.ts +95 -0
  615. package/corpus/templates/slides-2/actions/update-deck-design-tokens.ts +175 -0
  616. package/corpus/templates/slides-2/actions/update-design-system.ts +95 -0
  617. package/corpus/templates/slides-2/actions/update-slide-comment.ts +213 -0
  618. package/corpus/templates/slides-2/actions/update-slide-source.ts +353 -0
  619. package/corpus/templates/slides-2/actions/update-slide.ts +850 -0
  620. package/corpus/templates/slides-2/actions/update-slides-notification-prefs.ts +55 -0
  621. package/corpus/templates/slides-2/actions/view-screen.ts +1098 -0
  622. package/corpus/templates/slides-2/agent-native.app-skill.json +165 -0
  623. package/corpus/templates/slides-2/agent-native.config.ts +20 -0
  624. package/corpus/templates/slides-2/agent-native.json +18 -0
  625. package/corpus/templates/slides-2/app/components/ThemeToggle.tsx +47 -0
  626. package/corpus/templates/slides-2/app/components/comments/SlideCommentPins.tsx +781 -0
  627. package/corpus/templates/slides-2/app/components/comments/SlideCommentsPanel.tsx +1075 -0
  628. package/corpus/templates/slides-2/app/components/deck/DeckCard.tsx +304 -0
  629. package/corpus/templates/slides-2/app/components/deck/DeckFilterMenu.tsx +83 -0
  630. package/corpus/templates/slides-2/app/components/deck/ExcalidrawSlide.tsx +183 -0
  631. package/corpus/templates/slides-2/app/components/deck/MermaidRenderer.tsx +126 -0
  632. package/corpus/templates/slides-2/app/components/deck/SlideRenderer.tsx +1553 -0
  633. package/corpus/templates/slides-2/app/components/design-system/DesignSystemCard.tsx +235 -0
  634. package/corpus/templates/slides-2/app/components/design-system/DesignSystemSetup.tsx +2021 -0
  635. package/corpus/templates/slides-2/app/components/design-system/builder-design-system-upload.ts +200 -0
  636. package/corpus/templates/slides-2/app/components/design-system/builder-index-response.ts +104 -0
  637. package/corpus/templates/slides-2/app/components/editor/AddSlidePopover.tsx +461 -0
  638. package/corpus/templates/slides-2/app/components/editor/AiEditingMarker.tsx +24 -0
  639. package/corpus/templates/slides-2/app/components/editor/AlignmentGuides.tsx +82 -0
  640. package/corpus/templates/slides-2/app/components/editor/AnimationsPanel.tsx +698 -0
  641. package/corpus/templates/slides-2/app/components/editor/AppMenu.tsx +356 -0
  642. package/corpus/templates/slides-2/app/components/editor/AssetLibraryPanel.tsx +508 -0
  643. package/corpus/templates/slides-2/app/components/editor/BlockBubbleMenu.tsx +548 -0
  644. package/corpus/templates/slides-2/app/components/editor/DeckAccessDeniedPage.tsx +173 -0
  645. package/corpus/templates/slides-2/app/components/editor/DeckEditorSkeleton.tsx +35 -0
  646. package/corpus/templates/slides-2/app/components/editor/DeferredPopoverFallback.tsx +148 -0
  647. package/corpus/templates/slides-2/app/components/editor/EditorActionCluster.tsx +217 -0
  648. package/corpus/templates/slides-2/app/components/editor/EditorSidebar.tsx +1047 -0
  649. package/corpus/templates/slides-2/app/components/editor/EditorToolbar.tsx +1285 -0
  650. package/corpus/templates/slides-2/app/components/editor/ExportMenu.tsx +579 -0
  651. package/corpus/templates/slides-2/app/components/editor/GeneratingOverlay.tsx +21 -0
  652. package/corpus/templates/slides-2/app/components/editor/GeneratingSlidePreview.tsx +73 -0
  653. package/corpus/templates/slides-2/app/components/editor/GoogleDocImportHint.tsx +378 -0
  654. package/corpus/templates/slides-2/app/components/editor/GoogleDriveConnectionCta.tsx +154 -0
  655. package/corpus/templates/slides-2/app/components/editor/GoogleSlidesReferenceImport.tsx +299 -0
  656. package/corpus/templates/slides-2/app/components/editor/HistoryPanel.tsx +354 -0
  657. package/corpus/templates/slides-2/app/components/editor/HomeQuickStartDialog.tsx +202 -0
  658. package/corpus/templates/slides-2/app/components/editor/ImageCropOverlay.tsx +820 -0
  659. package/corpus/templates/slides-2/app/components/editor/ImageDropPromptPopover.tsx +280 -0
  660. package/corpus/templates/slides-2/app/components/editor/ImageGenPanel.tsx +244 -0
  661. package/corpus/templates/slides-2/app/components/editor/ImageOverlay.tsx +280 -0
  662. package/corpus/templates/slides-2/app/components/editor/ImageSearchPanel.tsx +183 -0
  663. package/corpus/templates/slides-2/app/components/editor/ImportDeckButton.tsx +247 -0
  664. package/corpus/templates/slides-2/app/components/editor/LogoSearchPanel.tsx +366 -0
  665. package/corpus/templates/slides-2/app/components/editor/MermaidToExcalidrawPanel.tsx +99 -0
  666. package/corpus/templates/slides-2/app/components/editor/MissingDeckAccessPane.tsx +219 -0
  667. package/corpus/templates/slides-2/app/components/editor/NewDeckReferenceStep.tsx +812 -0
  668. package/corpus/templates/slides-2/app/components/editor/PromptDialog.tsx +963 -0
  669. package/corpus/templates/slides-2/app/components/editor/QuestionFlow.tsx +119 -0
  670. package/corpus/templates/slides-2/app/components/editor/RequestDeckAccessDialog.tsx +101 -0
  671. package/corpus/templates/slides-2/app/components/editor/ShareDialog.tsx +126 -0
  672. package/corpus/templates/slides-2/app/components/editor/SlideContextToolbar.tsx +1471 -0
  673. package/corpus/templates/slides-2/app/components/editor/SlideEditor.tsx +11183 -0
  674. package/corpus/templates/slides-2/app/components/editor/SlideOverflowWarning.tsx +82 -0
  675. package/corpus/templates/slides-2/app/components/editor/SlideScrollStack.tsx +307 -0
  676. package/corpus/templates/slides-2/app/components/editor/SlideSlashCommandMenu.tsx +692 -0
  677. package/corpus/templates/slides-2/app/components/editor/SlideThumbnailContextMenu.tsx +52 -0
  678. package/corpus/templates/slides-2/app/components/editor/SlideTransitionControl.tsx +184 -0
  679. package/corpus/templates/slides-2/app/components/editor/SlidesComposerContext.tsx +789 -0
  680. package/corpus/templates/slides-2/app/components/editor/SlidesComposerContextProvider.tsx +74 -0
  681. package/corpus/templates/slides-2/app/components/editor/SlidesLayersPanel.tsx +377 -0
  682. package/corpus/templates/slides-2/app/components/editor/SpeakerNotesPanel.tsx +119 -0
  683. package/corpus/templates/slides-2/app/components/editor/TweaksPanel.tsx +77 -0
  684. package/corpus/templates/slides-2/app/components/editor/UploadStorageGate.tsx +37 -0
  685. package/corpus/templates/slides-2/app/components/editor/bullet-editing.ts +880 -0
  686. package/corpus/templates/slides-2/app/components/editor/canvas-interactions/index.ts +12 -0
  687. package/corpus/templates/slides-2/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +119 -0
  688. package/corpus/templates/slides-2/app/components/editor/commit-active-edit.ts +14 -0
  689. package/corpus/templates/slides-2/app/components/editor/editor-command-model.ts +35 -0
  690. package/corpus/templates/slides-2/app/components/editor/fit-text-object.ts +31 -0
  691. package/corpus/templates/slides-2/app/components/editor/in-place-text-session.browser-cases.json +983 -0
  692. package/corpus/templates/slides-2/app/components/editor/in-place-text-session.browser-host.ts +747 -0
  693. package/corpus/templates/slides-2/app/components/editor/in-place-text-session.ts +6891 -0
  694. package/corpus/templates/slides-2/app/components/editor/inline-edit-session.ts +26 -0
  695. package/corpus/templates/slides-2/app/components/editor/list-editing.ts +501 -0
  696. package/corpus/templates/slides-2/app/components/editor/rich-text-selection.ts +975 -0
  697. package/corpus/templates/slides-2/app/components/editor/rte-test-utils.ts +265 -0
  698. package/corpus/templates/slides-2/app/components/editor/selection-overlay-measurement.ts +191 -0
  699. package/corpus/templates/slides-2/app/components/editor/slide-escape-arbiter.ts +34 -0
  700. package/corpus/templates/slides-2/app/components/editor/slide-object-interactions.ts +6362 -0
  701. package/corpus/templates/slides-2/app/components/editor/slide-object-transform.browser-host.ts +19 -0
  702. package/corpus/templates/slides-2/app/components/editor/slide-pointer-target.ts +432 -0
  703. package/corpus/templates/slides-2/app/components/editor/slide-presence.ts +13 -0
  704. package/corpus/templates/slides-2/app/components/editor/slide-style.ts +243 -0
  705. package/corpus/templates/slides-2/app/components/editor/slide-text-targets.ts +540 -0
  706. package/corpus/templates/slides-2/app/components/editor/style-clipboard.ts +102 -0
  707. package/corpus/templates/slides-2/app/components/editor/use-prompt-import.ts +159 -0
  708. package/corpus/templates/slides-2/app/components/home/SlidesHomeLibrary.tsx +79 -0
  709. package/corpus/templates/slides-2/app/components/layout/AgentWorkIndicator.tsx +193 -0
  710. package/corpus/templates/slides-2/app/components/layout/Header.tsx +110 -0
  711. package/corpus/templates/slides-2/app/components/layout/Layout.tsx +492 -0
  712. package/corpus/templates/slides-2/app/components/layout/Sidebar.tsx +109 -0
  713. package/corpus/templates/slides-2/app/components/layout/layout-route-policy.ts +28 -0
  714. package/corpus/templates/slides-2/app/components/presentation/PresentationView.tsx +1116 -0
  715. package/corpus/templates/slides-2/app/components/presentation/PresenterView.tsx +526 -0
  716. package/corpus/templates/slides-2/app/components/presentation/SlideTransitionStage.tsx +297 -0
  717. package/corpus/templates/slides-2/app/components/presentation/present-channel.ts +31 -0
  718. package/corpus/templates/slides-2/app/components/presentation/slide-build-steps.ts +231 -0
  719. package/corpus/templates/slides-2/app/components/presentation/slide-navigation.ts +273 -0
  720. package/corpus/templates/slides-2/app/components/presentation/slide-transition-motion.ts +258 -0
  721. package/corpus/templates/slides-2/app/components/presentation/voice-navigation.ts +61 -0
  722. package/corpus/templates/slides-2/app/components/settings/notification-settings.tsx +93 -0
  723. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2AssetBrowser.tsx +726 -0
  724. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2CommandMenu.tsx +453 -0
  725. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2Comments.tsx +571 -0
  726. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2DesignTokens.tsx +345 -0
  727. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2Editor.tsx +4804 -0
  728. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2ExportMenu.tsx +671 -0
  729. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2FontBrowser.tsx +342 -0
  730. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2ImageCropControls.tsx +487 -0
  731. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2Inspector.tsx +950 -0
  732. package/corpus/templates/slides-2/app/components/slides-2-editor/Slides2SvglLogoSearchDialog.tsx +332 -0
  733. package/corpus/templates/slides-2/app/components/slides-2-editor/cross-slide-edit-history.ts +151 -0
  734. package/corpus/templates/slides-2/app/components/slides-2-editor/design-token-presets.ts +140 -0
  735. package/corpus/templates/slides-2/app/components/slides-2-editor/editor-action-errors.ts +47 -0
  736. package/corpus/templates/slides-2/app/components/slides-2-editor/editor-export-errors.ts +60 -0
  737. package/corpus/templates/slides-2/app/components/slides-2-editor/geometry.ts +275 -0
  738. package/corpus/templates/slides-2/app/components/slides-2-editor/image-crop-style.ts +82 -0
  739. package/corpus/templates/slides-2/app/components/slides-2-editor/inline-text-editor.tsx +631 -0
  740. package/corpus/templates/slides-2/app/components/slides-2-editor/keyboard-shortcuts.ts +104 -0
  741. package/corpus/templates/slides-2/app/components/slides-2-editor/object-insertion.ts +13 -0
  742. package/corpus/templates/slides-2/app/components/slides-2-editor/preview-patches.ts +54 -0
  743. package/corpus/templates/slides-2/app/components/slides-2-editor/sandbox-render-errors.ts +23 -0
  744. package/corpus/templates/slides-2/app/components/slides-2-editor/source-structure.ts +634 -0
  745. package/corpus/templates/slides-2/app/components/slides-2-editor/types.ts +85 -0
  746. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2BlackoutOverlay.tsx +13 -0
  747. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2ControlBar.tsx +285 -0
  748. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2HelpDialog.tsx +72 -0
  749. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2JumpInput.tsx +38 -0
  750. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2LaserPointer.tsx +67 -0
  751. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2Overview.tsx +161 -0
  752. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2Player.tsx +839 -0
  753. package/corpus/templates/slides-2/app/components/slides-2-presenter/Slides2ProgressBar.tsx +23 -0
  754. package/corpus/templates/slides-2/app/components/slides-2-presenter/keyboard.ts +36 -0
  755. package/corpus/templates/slides-2/app/components/slides-2-presenter/presenter-channel.ts +131 -0
  756. package/corpus/templates/slides-2/app/components/slides-2-presenter/use-presenter-action-bridge.ts +296 -0
  757. package/corpus/templates/slides-2/app/components/slides-2-presenter/useSlides2TouchSwipe.ts +80 -0
  758. package/corpus/templates/slides-2/app/components/slides-2-runtime/SandboxedSlide.tsx +724 -0
  759. package/corpus/templates/slides-2/app/components/slides-2-runtime/SlideContentRenderer.tsx +137 -0
  760. package/corpus/templates/slides-2/app/components/slides-2-runtime/Slides2Canvas.tsx +99 -0
  761. package/corpus/templates/slides-2/app/components/slides-2-runtime/constants.ts +4 -0
  762. package/corpus/templates/slides-2/app/components/slides-2-runtime/protocol.ts +1400 -0
  763. package/corpus/templates/slides-2/app/components/slides-2-runtime/transition-frame.ts +439 -0
  764. package/corpus/templates/slides-2/app/components/templates/DeckTemplateLibrary.tsx +314 -0
  765. package/corpus/templates/slides-2/app/components/templates/DeckTemplatePreview.tsx +60 -0
  766. package/corpus/templates/slides-2/app/components/templates/DeckTemplateStage.tsx +45 -0
  767. package/corpus/templates/slides-2/app/components/ui/alert-dialog.tsx +1 -0
  768. package/corpus/templates/slides-2/app/components/ui/avatar.tsx +1 -0
  769. package/corpus/templates/slides-2/app/components/ui/button-group.tsx +1 -0
  770. package/corpus/templates/slides-2/app/components/ui/button.tsx +1 -0
  771. package/corpus/templates/slides-2/app/components/ui/card.tsx +1 -0
  772. package/corpus/templates/slides-2/app/components/ui/command.tsx +1 -0
  773. package/corpus/templates/slides-2/app/components/ui/context-menu.tsx +32 -0
  774. package/corpus/templates/slides-2/app/components/ui/dialog.tsx +1 -0
  775. package/corpus/templates/slides-2/app/components/ui/dropdown-menu.tsx +1 -0
  776. package/corpus/templates/slides-2/app/components/ui/filter-trigger.tsx +1 -0
  777. package/corpus/templates/slides-2/app/components/ui/input.tsx +1 -0
  778. package/corpus/templates/slides-2/app/components/ui/label.tsx +1 -0
  779. package/corpus/templates/slides-2/app/components/ui/popover.tsx +1 -0
  780. package/corpus/templates/slides-2/app/components/ui/progress.tsx +1 -0
  781. package/corpus/templates/slides-2/app/components/ui/scroll-area.tsx +1 -0
  782. package/corpus/templates/slides-2/app/components/ui/select.tsx +1 -0
  783. package/corpus/templates/slides-2/app/components/ui/separator.tsx +1 -0
  784. package/corpus/templates/slides-2/app/components/ui/sheet.tsx +1 -0
  785. package/corpus/templates/slides-2/app/components/ui/skeleton.tsx +1 -0
  786. package/corpus/templates/slides-2/app/components/ui/slider.tsx +1 -0
  787. package/corpus/templates/slides-2/app/components/ui/spinner.tsx +1 -0
  788. package/corpus/templates/slides-2/app/components/ui/switch.tsx +1 -0
  789. package/corpus/templates/slides-2/app/components/ui/tabs.tsx +59 -0
  790. package/corpus/templates/slides-2/app/components/ui/textarea.tsx +1 -0
  791. package/corpus/templates/slides-2/app/components/ui/toggle-group.tsx +1 -0
  792. package/corpus/templates/slides-2/app/components/ui/toolkit-provider.tsx +17 -0
  793. package/corpus/templates/slides-2/app/components/ui/tooltip.tsx +1 -0
  794. package/corpus/templates/slides-2/app/components/visual-editor/CanvasCommentPins.tsx +46 -0
  795. package/corpus/templates/slides-2/app/components/visual-editor/DrawOverlay.tsx +27 -0
  796. package/corpus/templates/slides-2/app/components/visual-editor/MultiSelectChip.tsx +97 -0
  797. package/corpus/templates/slides-2/app/components/visual-editor/SaveStatusIndicator.tsx +273 -0
  798. package/corpus/templates/slides-2/app/components/visual-editor/index.ts +4 -0
  799. package/corpus/templates/slides-2/app/context/DeckContext.tsx +7486 -0
  800. package/corpus/templates/slides-2/app/design-system.ts +3 -0
  801. package/corpus/templates/slides-2/app/entry.client.tsx +10 -0
  802. package/corpus/templates/slides-2/app/entry.server.tsx +10 -0
  803. package/corpus/templates/slides-2/app/global.css +1626 -0
  804. package/corpus/templates/slides-2/app/hooks/use-agent-generating.ts +517 -0
  805. package/corpus/templates/slides-2/app/hooks/use-compact-editor-layout.ts +26 -0
  806. package/corpus/templates/slides-2/app/hooks/use-contrast-audit-bridge.ts +49 -0
  807. package/corpus/templates/slides-2/app/hooks/use-deck-access-reload.ts +70 -0
  808. package/corpus/templates/slides-2/app/hooks/use-deck-access.ts +50 -0
  809. package/corpus/templates/slides-2/app/hooks/use-deck-design-system.ts +146 -0
  810. package/corpus/templates/slides-2/app/hooks/use-deck-history-action-bridge.ts +266 -0
  811. package/corpus/templates/slides-2/app/hooks/use-deck-presence.ts +89 -0
  812. package/corpus/templates/slides-2/app/hooks/use-deck-role.ts +46 -0
  813. package/corpus/templates/slides-2/app/hooks/use-deck-versions.ts +43 -0
  814. package/corpus/templates/slides-2/app/hooks/use-design-system-workflows.ts +11 -0
  815. package/corpus/templates/slides-2/app/hooks/use-design-systems.ts +31 -0
  816. package/corpus/templates/slides-2/app/hooks/use-generating-slide-preview.ts +115 -0
  817. package/corpus/templates/slides-2/app/hooks/use-navigation-state.ts +229 -0
  818. package/corpus/templates/slides-2/app/hooks/use-new-deck-generation-signal.ts +46 -0
  819. package/corpus/templates/slides-2/app/hooks/use-new-deck-generation.ts +710 -0
  820. package/corpus/templates/slides-2/app/hooks/use-sidebar-collapsed.ts +95 -0
  821. package/corpus/templates/slides-2/app/hooks/use-slide-comments.ts +202 -0
  822. package/corpus/templates/slides-2/app/hooks/use-slide-file-storage-status.ts +55 -0
  823. package/corpus/templates/slides-2/app/hooks/use-undo-selection.ts +66 -0
  824. package/corpus/templates/slides-2/app/hooks/use-workspace-defaults.ts +31 -0
  825. package/corpus/templates/slides-2/app/i18n/en-US.ts +1507 -0
  826. package/corpus/templates/slides-2/app/i18n/index.ts +19 -0
  827. package/corpus/templates/slides-2/app/lib/agent-visible-message.ts +21 -0
  828. package/corpus/templates/slides-2/app/lib/aspect-ratios.ts +7 -0
  829. package/corpus/templates/slides-2/app/lib/asset-error-copy.ts +59 -0
  830. package/corpus/templates/slides-2/app/lib/canvas-zoom.ts +47 -0
  831. package/corpus/templates/slides-2/app/lib/composer-context.ts +152 -0
  832. package/corpus/templates/slides-2/app/lib/composer-draft.ts +34 -0
  833. package/corpus/templates/slides-2/app/lib/contrast-audit.ts +390 -0
  834. package/corpus/templates/slides-2/app/lib/contrast-resolve.ts +240 -0
  835. package/corpus/templates/slides-2/app/lib/create-deck-generation.ts +938 -0
  836. package/corpus/templates/slides-2/app/lib/deck-access-lost.ts +4 -0
  837. package/corpus/templates/slides-2/app/lib/deck-backup.ts +186 -0
  838. package/corpus/templates/slides-2/app/lib/deck-editor-loading.ts +91 -0
  839. package/corpus/templates/slides-2/app/lib/deck-export-file-client.ts +77 -0
  840. package/corpus/templates/slides-2/app/lib/deck-filter.ts +52 -0
  841. package/corpus/templates/slides-2/app/lib/deck-list-loading.ts +16 -0
  842. package/corpus/templates/slides-2/app/lib/deck-preview-frame.ts +15 -0
  843. package/corpus/templates/slides-2/app/lib/deck-share-links.ts +9 -0
  844. package/corpus/templates/slides-2/app/lib/deck-sorting.ts +19 -0
  845. package/corpus/templates/slides-2/app/lib/deferred-editor-surfaces.ts +24 -0
  846. package/corpus/templates/slides-2/app/lib/design-system-selection.ts +11 -0
  847. package/corpus/templates/slides-2/app/lib/design-systems.ts +231 -0
  848. package/corpus/templates/slides-2/app/lib/dynamic-import.ts +50 -0
  849. package/corpus/templates/slides-2/app/lib/editor-agent-handoff.ts +92 -0
  850. package/corpus/templates/slides-2/app/lib/editor-shortcuts.ts +153 -0
  851. package/corpus/templates/slides-2/app/lib/export-google-slides-client.ts +168 -0
  852. package/corpus/templates/slides-2/app/lib/export-pdf-client.ts +684 -0
  853. package/corpus/templates/slides-2/app/lib/export-pptx-client.ts +2420 -0
  854. package/corpus/templates/slides-2/app/lib/generation-lifecycle.ts +24 -0
  855. package/corpus/templates/slides-2/app/lib/generation-state.ts +247 -0
  856. package/corpus/templates/slides-2/app/lib/google-docs-client.ts +110 -0
  857. package/corpus/templates/slides-2/app/lib/google-slides-export-availability-client.ts +53 -0
  858. package/corpus/templates/slides-2/app/lib/google-slides-reference-source.ts +18 -0
  859. package/corpus/templates/slides-2/app/lib/image-download.ts +85 -0
  860. package/corpus/templates/slides-2/app/lib/image-drop-to-agent.ts +210 -0
  861. package/corpus/templates/slides-2/app/lib/import-uploaded-deck.ts +132 -0
  862. package/corpus/templates/slides-2/app/lib/inline-edit-remote.ts +55 -0
  863. package/corpus/templates/slides-2/app/lib/localize-slides-changelog.ts +47 -0
  864. package/corpus/templates/slides-2/app/lib/mermaid-blocks.ts +18 -0
  865. package/corpus/templates/slides-2/app/lib/new-deck-reference-selection.ts +174 -0
  866. package/corpus/templates/slides-2/app/lib/normalize-slide-padding.ts +74 -0
  867. package/corpus/templates/slides-2/app/lib/pending-deck-changes.ts +33 -0
  868. package/corpus/templates/slides-2/app/lib/portaled-layer.ts +7 -0
  869. package/corpus/templates/slides-2/app/lib/pptx-google-slides.ts +204 -0
  870. package/corpus/templates/slides-2/app/lib/prompt-attachment-adapter.ts +37 -0
  871. package/corpus/templates/slides-2/app/lib/prompt-file-uploads.ts +557 -0
  872. package/corpus/templates/slides-2/app/lib/recent-references.ts +106 -0
  873. package/corpus/templates/slides-2/app/lib/reference-document-hydration.ts +406 -0
  874. package/corpus/templates/slides-2/app/lib/register-chat-renderers.tsx +645 -0
  875. package/corpus/templates/slides-2/app/lib/reveal-after-delete.ts +55 -0
  876. package/corpus/templates/slides-2/app/lib/sanitize-slide-html.ts +752 -0
  877. package/corpus/templates/slides-2/app/lib/slide-agent-context.ts +148 -0
  878. package/corpus/templates/slides-2/app/lib/slide-animation-elements.ts +900 -0
  879. package/corpus/templates/slides-2/app/lib/slide-clipboard.ts +534 -0
  880. package/corpus/templates/slides-2/app/lib/slide-comment-anchor.ts +286 -0
  881. package/corpus/templates/slides-2/app/lib/slide-content-merge.ts +295 -0
  882. package/corpus/templates/slides-2/app/lib/slide-image-replacement.ts +1919 -0
  883. package/corpus/templates/slides-2/app/lib/slide-live-remote.ts +215 -0
  884. package/corpus/templates/slides-2/app/lib/slide-scroll.ts +56 -0
  885. package/corpus/templates/slides-2/app/lib/slide-source-map.ts +1099 -0
  886. package/corpus/templates/slides-2/app/lib/slide-video.ts +390 -0
  887. package/corpus/templates/slides-2/app/lib/slides-relay-tracking.ts +114 -0
  888. package/corpus/templates/slides-2/app/lib/streaming-slide-html.ts +77 -0
  889. package/corpus/templates/slides-2/app/lib/tab-id.ts +3 -0
  890. package/corpus/templates/slides-2/app/lib/template-navigation.ts +15 -0
  891. package/corpus/templates/slides-2/app/lib/text-tool-shortcut.ts +75 -0
  892. package/corpus/templates/slides-2/app/lib/undo-reveal.ts +332 -0
  893. package/corpus/templates/slides-2/app/lib/upload-response.ts +107 -0
  894. package/corpus/templates/slides-2/app/lib/utils.ts +38 -0
  895. package/corpus/templates/slides-2/app/lib/validate-native-slide-html.ts +177 -0
  896. package/corpus/templates/slides-2/app/lib/video-upload.ts +354 -0
  897. package/corpus/templates/slides-2/app/pages/DeckEditor.tsx +6158 -0
  898. package/corpus/templates/slides-2/app/pages/DesignSystems.tsx +363 -0
  899. package/corpus/templates/slides-2/app/pages/Index.tsx +3254 -0
  900. package/corpus/templates/slides-2/app/pages/NotFound.tsx +33 -0
  901. package/corpus/templates/slides-2/app/pages/Presentation.tsx +120 -0
  902. package/corpus/templates/slides-2/app/pages/SharedPresentation.tsx +174 -0
  903. package/corpus/templates/slides-2/app/pages/SlideThemeDetail.tsx +227 -0
  904. package/corpus/templates/slides-2/app/pages/SlideThemes.tsx +155 -0
  905. package/corpus/templates/slides-2/app/pages/Slides2EditorPage.tsx +1618 -0
  906. package/corpus/templates/slides-2/app/pages/Slides2PresenterConsolePage.tsx +937 -0
  907. package/corpus/templates/slides-2/app/pages/Slides2PresenterPage.tsx +979 -0
  908. package/corpus/templates/slides-2/app/pages/Templates.tsx +32 -0
  909. package/corpus/templates/slides-2/app/root.tsx +391 -0
  910. package/corpus/templates/slides-2/app/routes/$.tsx +10 -0
  911. package/corpus/templates/slides-2/app/routes/_index.tsx +1 -0
  912. package/corpus/templates/slides-2/app/routes/access-request.approve.tsx +276 -0
  913. package/corpus/templates/slides-2/app/routes/agent.tsx +19 -0
  914. package/corpus/templates/slides-2/app/routes/chat.$threadId.tsx +1 -0
  915. package/corpus/templates/slides-2/app/routes/chat.tsx +190 -0
  916. package/corpus/templates/slides-2/app/routes/deck.$id.tsx +10 -0
  917. package/corpus/templates/slides-2/app/routes/deck.$id_.present.tsx +10 -0
  918. package/corpus/templates/slides-2/app/routes/design-systems.tsx +10 -0
  919. package/corpus/templates/slides-2/app/routes/extensions.$id.$slug.tsx +2 -0
  920. package/corpus/templates/slides-2/app/routes/extensions.$id.tsx +5 -0
  921. package/corpus/templates/slides-2/app/routes/extensions._index.tsx +5 -0
  922. package/corpus/templates/slides-2/app/routes/extensions.tsx +5 -0
  923. package/corpus/templates/slides-2/app/routes/home-layout.tsx +31 -0
  924. package/corpus/templates/slides-2/app/routes/home.tsx +22 -0
  925. package/corpus/templates/slides-2/app/routes/p.$id.tsx +195 -0
  926. package/corpus/templates/slides-2/app/routes/settings.$.tsx +1 -0
  927. package/corpus/templates/slides-2/app/routes/settings.tsx +131 -0
  928. package/corpus/templates/slides-2/app/routes/share.$token.tsx +137 -0
  929. package/corpus/templates/slides-2/app/routes/slide.tsx +209 -0
  930. package/corpus/templates/slides-2/app/routes/team.tsx +11 -0
  931. package/corpus/templates/slides-2/app/routes/templates.tsx +1 -0
  932. package/corpus/templates/slides-2/app/routes/themes.$id.tsx +1 -0
  933. package/corpus/templates/slides-2/app/routes/themes.tsx +1 -0
  934. package/corpus/templates/slides-2/app/routes.ts +24 -0
  935. package/corpus/templates/slides-2/app/vite-env.d.ts +24 -0
  936. package/corpus/templates/slides-2/changelog/2026-10-17-find-and-switch-decks-from-the-presenter-console.md +6 -0
  937. package/corpus/templates/slides-2/changelog/2026-10-17-meet-slides-2-create-and-refine-editable-slide-decks-with-an.md +6 -0
  938. package/corpus/templates/slides-2/changelog/2026-10-17-pptx-image-export.md +6 -0
  939. package/corpus/templates/slides-2/changelog/2026-10-17-visual-editor.md +6 -0
  940. package/corpus/templates/slides-2/changelog/2026-10-18-apply-an-accessible-design-system-to-an-existing-deck-before.md +6 -0
  941. package/corpus/templates/slides-2/changelog/2026-10-18-choose-a-template-or-start-a-new-presentation-from-the-slide.md +6 -0
  942. package/corpus/templates/slides-2/changelog/2026-10-18-generate-decks-from-a-prompt-then-edit-slide-elements-direct.md +6 -0
  943. package/corpus/templates/slides-2/changelog/2026-10-18-generate-presentation-decks-from-a-prompt-and-edit-every-sli.md +6 -0
  944. package/corpus/templates/slides-2/changelog/2026-10-18-new-presentation-drafts-save-before-generation-starts-so-you.md +6 -0
  945. package/corpus/templates/slides-2/changelog/2026-10-18-the-assistant-can-undo-redo-and-save-pending-edits-in-the-vi.md +6 -0
  946. package/corpus/templates/slides-2/changelog/2026-10-19-agents-can-insert-duplicate-and-delete-canvas-objects-in-sou.md +6 -0
  947. package/corpus/templates/slides-2/changelog/2026-10-19-browse-slide-themes.md +6 -0
  948. package/corpus/templates/slides-2/changelog/2026-10-19-clicking-a-slide-preview-in-overview-selects-that-slide.md +6 -0
  949. package/corpus/templates/slides-2/changelog/2026-10-19-create-decks-from-a-prompt-and-edit-slides-directly-on-the-c.md +6 -0
  950. package/corpus/templates/slides-2/changelog/2026-10-19-creating-and-editing-decks-now-starts-with-the-tools-most-re.md +6 -0
  951. package/corpus/templates/slides-2/changelog/2026-10-19-deleting-decks-revokes-public-links.md +7 -0
  952. package/corpus/templates/slides-2/changelog/2026-10-19-escape-cancels-an-in-progress-slide-drag-without-losing-earl.md +6 -0
  953. package/corpus/templates/slides-2/changelog/2026-10-19-import-failures-now-show-clear-localized-descriptions-instea.md +6 -0
  954. package/corpus/templates/slides-2/changelog/2026-10-19-presenter-controls-stay-responsive-when-moving-over-the-slid.md +6 -0
  955. package/corpus/templates/slides-2/changelog/2026-10-19-presenter-notes-stay-private-from-viewers-and-commenters.md +6 -0
  956. package/corpus/templates/slides-2/changelog/2026-10-19-preview-generated-slides-in-chat.md +6 -0
  957. package/corpus/templates/slides-2/changelog/2026-10-19-remote-image-previews-accept-supported-raster-formats.md +6 -0
  958. package/corpus/templates/slides-2/changelog/2026-10-19-search-the-svgl-catalog-and-add-svg-logos-to-a-deck-or-the-g.md +6 -0
  959. package/corpus/templates/slides-2/changelog/2026-10-19-slide-generation-explains-when-react-effect-hooks-cannot-run.md +6 -0
  960. package/corpus/templates/slides-2/changelog/2026-10-19-the-editor-highlights-hovered-objects-and-keeps-image-replac.md +6 -0
  961. package/corpus/templates/slides-2/changelog/2026-10-19-the-slide-editor-uses-the-shared-agent-native-controls.md +6 -0
  962. package/corpus/templates/slides-2/changelog/2026-10-20-deck-editing-controls-match-the-workspace.md +6 -0
  963. package/corpus/templates/slides-2/changelog/2026-10-20-google-slides-imports-now-report-missing-image-data-instead-.md +6 -0
  964. package/corpus/templates/slides-2/changelog/2026-10-20-imports-and-previews-handle-large-content-safely.md +6 -0
  965. package/corpus/templates/slides-2/changelog/2026-10-20-public-deck-pages-can-export-source-backed-slides-to-pdf.md +6 -0
  966. package/corpus/templates/slides-2/changelog/2026-10-20-saved-slide-images-stay-available.md +6 -0
  967. package/corpus/templates/slides-2/changelog/2026-10-20-scope-global-media-picks-to-assets-the-current-deck-can-reso.md +6 -0
  968. package/corpus/templates/slides-2/components.json +20 -0
  969. package/corpus/templates/slides-2/drizzle.config.ts +3 -0
  970. package/corpus/templates/slides-2/learnings.defaults.md +5 -0
  971. package/corpus/templates/slides-2/netlify.toml +24 -0
  972. package/corpus/templates/slides-2/package.json +147 -0
  973. package/corpus/templates/slides-2/public/agent-native-icon-dark.svg +10 -0
  974. package/corpus/templates/slides-2/public/agent-native-icon-light.svg +10 -0
  975. package/corpus/templates/slides-2/public/agent-native-logo-dark.svg +21 -0
  976. package/corpus/templates/slides-2/public/agent-native-logo-light.svg +21 -0
  977. package/corpus/templates/slides-2/public/assets/builder-logo-white.svg +15 -0
  978. package/corpus/templates/slides-2/public/assets/generated/preview.html +61 -0
  979. package/corpus/templates/slides-2/public/assets/intuit-logo-white.svg +6 -0
  980. package/corpus/templates/slides-2/public/assets/walmart-logo-white.svg +6 -0
  981. package/corpus/templates/slides-2/public/chatgpt-plugin-icon.svg +7 -0
  982. package/corpus/templates/slides-2/public/favicon.svg +1 -0
  983. package/corpus/templates/slides-2/public/icon-180.svg +1 -0
  984. package/corpus/templates/slides-2/public/icon-192.svg +1 -0
  985. package/corpus/templates/slides-2/public/icon-512.svg +1 -0
  986. package/corpus/templates/slides-2/public/manifest.json +20 -0
  987. package/corpus/templates/slides-2/public/placeholder.svg +1 -0
  988. package/corpus/templates/slides-2/public/robots.txt +14 -0
  989. package/corpus/templates/slides-2/public/visual-edit-structure-proof.html +99 -0
  990. package/corpus/templates/slides-2/react-router.config.ts +7 -0
  991. package/corpus/templates/slides-2/scripts/edit-fidelity/README.md +499 -0
  992. package/corpus/templates/slides-2/scripts/edit-fidelity/authoring-fuzz.ts +9517 -0
  993. package/corpus/templates/slides-2/scripts/edit-fidelity/baseline.json +16427 -0
  994. package/corpus/templates/slides-2/scripts/edit-fidelity/dev-server-url.ts +10 -0
  995. package/corpus/templates/slides-2/scripts/edit-fidelity/lib/in-page.ts +1975 -0
  996. package/corpus/templates/slides-2/scripts/edit-fidelity/lib/metrics.ts +1401 -0
  997. package/corpus/templates/slides-2/scripts/edit-fidelity/retry-infra.ts +7 -0
  998. package/corpus/templates/slides-2/scripts/edit-fidelity/run-options.ts +21 -0
  999. package/corpus/templates/slides-2/scripts/edit-fidelity/run-outcomes.ts +127 -0
  1000. package/corpus/templates/slides-2/scripts/edit-fidelity/run.ts +8451 -0
  1001. package/corpus/templates/slides-2/scripts/export-fidelity/README.md +116 -0
  1002. package/corpus/templates/slides-2/scripts/export-fidelity/compare-layout-lib.ts +241 -0
  1003. package/corpus/templates/slides-2/scripts/export-fidelity/compare-layout.ts +30 -0
  1004. package/corpus/templates/slides-2/scripts/export-fidelity/diff.ts +222 -0
  1005. package/corpus/templates/slides-2/scripts/export-fidelity/export.ts +765 -0
  1006. package/corpus/templates/slides-2/scripts/export-fidelity/fixtures/agent-dark-deck.json +48 -0
  1007. package/corpus/templates/slides-2/scripts/export-fidelity/generate-calibration.ts +765 -0
  1008. package/corpus/templates/slides-2/scripts/export-fidelity/google-export.ts +137 -0
  1009. package/corpus/templates/slides-2/scripts/export-fidelity/google-layout.ts +82 -0
  1010. package/corpus/templates/slides-2/scripts/export-fidelity/resolve-pkg.ts +29 -0
  1011. package/corpus/templates/slides-2/scripts/migrate-production.ts +22 -0
  1012. package/corpus/templates/slides-2/server/db/index.ts +73 -0
  1013. package/corpus/templates/slides-2/server/db/schema.ts +189 -0
  1014. package/corpus/templates/slides-2/server/handlers/assets-url.ts +13 -0
  1015. package/corpus/templates/slides-2/server/handlers/assets.ts +3217 -0
  1016. package/corpus/templates/slides-2/server/handlers/decks.ts +185 -0
  1017. package/corpus/templates/slides-2/server/handlers/demo.ts +9 -0
  1018. package/corpus/templates/slides-2/server/handlers/design-systems.ts +219 -0
  1019. package/corpus/templates/slides-2/server/handlers/google-docs-auth.ts +165 -0
  1020. package/corpus/templates/slides-2/server/handlers/image-gen.ts +153 -0
  1021. package/corpus/templates/slides-2/server/handlers/image-providers/gemini.ts +209 -0
  1022. package/corpus/templates/slides-2/server/handlers/image-providers/index.ts +61 -0
  1023. package/corpus/templates/slides-2/server/handlers/image-providers/openai.ts +288 -0
  1024. package/corpus/templates/slides-2/server/handlers/image-providers/types.ts +35 -0
  1025. package/corpus/templates/slides-2/server/handlers/import/docx-parser.ts +31 -0
  1026. package/corpus/templates/slides-2/server/handlers/import/html-converter.ts +1534 -0
  1027. package/corpus/templates/slides-2/server/handlers/import/pdf-fidelity-parser.ts +1039 -0
  1028. package/corpus/templates/slides-2/server/handlers/import/pdf-sidecar-reader.ts +81 -0
  1029. package/corpus/templates/slides-2/server/handlers/import/pdf-style-digest.ts +187 -0
  1030. package/corpus/templates/slides-2/server/handlers/import/pptx-assets.ts +155 -0
  1031. package/corpus/templates/slides-2/server/handlers/import/pptx-parser.ts +57 -0
  1032. package/corpus/templates/slides-2/server/handlers/request-auth-context.ts +108 -0
  1033. package/corpus/templates/slides-2/server/handlers/share.ts +52 -0
  1034. package/corpus/templates/slides-2/server/handlers/uploads-chunked.ts +1066 -0
  1035. package/corpus/templates/slides-2/server/handlers/uploads.ts +457 -0
  1036. package/corpus/templates/slides-2/server/lib/access-request-email.ts +66 -0
  1037. package/corpus/templates/slides-2/server/lib/assets-image-delegation.ts +331 -0
  1038. package/corpus/templates/slides-2/server/lib/bounded-multipart.ts +49 -0
  1039. package/corpus/templates/slides-2/server/lib/builder-design-system-proxy.ts +122 -0
  1040. package/corpus/templates/slides-2/server/lib/chat-attachments.ts +425 -0
  1041. package/corpus/templates/slides-2/server/lib/chatgpt-directory-tools.ts +246 -0
  1042. package/corpus/templates/slides-2/server/lib/chunked-upload-session.ts +195 -0
  1043. package/corpus/templates/slides-2/server/lib/comment-notifications.ts +184 -0
  1044. package/corpus/templates/slides-2/server/lib/deck-access-requests.ts +102 -0
  1045. package/corpus/templates/slides-2/server/lib/deck-asset-scope.ts +150 -0
  1046. package/corpus/templates/slides-2/server/lib/deck-export-tracking.ts +119 -0
  1047. package/corpus/templates/slides-2/server/lib/deck-templates/a-case-study.ts +154 -0
  1048. package/corpus/templates/slides-2/server/lib/deck-templates/a-company.ts +160 -0
  1049. package/corpus/templates/slides-2/server/lib/deck-templates/a-pitch.ts +151 -0
  1050. package/corpus/templates/slides-2/server/lib/deck-templates/a-quarterly.ts +264 -0
  1051. package/corpus/templates/slides-2/server/lib/deck-templates/a-shared.ts +35 -0
  1052. package/corpus/templates/slides-2/server/lib/deck-templates/a-update.ts +188 -0
  1053. package/corpus/templates/slides-2/server/lib/deck-templates/a-workshop.ts +196 -0
  1054. package/corpus/templates/slides-2/server/lib/deck-templates/b-keynote.ts +131 -0
  1055. package/corpus/templates/slides-2/server/lib/deck-templates/b-lesson.ts +209 -0
  1056. package/corpus/templates/slides-2/server/lib/deck-templates/b-manifesto.ts +149 -0
  1057. package/corpus/templates/slides-2/server/lib/deck-templates/b-portfolio.ts +144 -0
  1058. package/corpus/templates/slides-2/server/lib/deck-templates/b-research.ts +221 -0
  1059. package/corpus/templates/slides-2/server/lib/deck-templates/b-roadmap.ts +264 -0
  1060. package/corpus/templates/slides-2/server/lib/deck-templates/b-shared.ts +44 -0
  1061. package/corpus/templates/slides-2/server/lib/deck-templates/group-a.ts +16 -0
  1062. package/corpus/templates/slides-2/server/lib/deck-templates/group-b.ts +16 -0
  1063. package/corpus/templates/slides-2/server/lib/deck-templates/legacy.ts +567 -0
  1064. package/corpus/templates/slides-2/server/lib/deck-templates.ts +60 -0
  1065. package/corpus/templates/slides-2/server/lib/deck-versions.ts +233 -0
  1066. package/corpus/templates/slides-2/server/lib/design-system-workflows.ts +22 -0
  1067. package/corpus/templates/slides-2/server/lib/emails.ts +84 -0
  1068. package/corpus/templates/slides-2/server/lib/env-config.ts +15 -0
  1069. package/corpus/templates/slides-2/server/lib/fetch-remote-image.ts +245 -0
  1070. package/corpus/templates/slides-2/server/lib/generation-completion.ts +278 -0
  1071. package/corpus/templates/slides-2/server/lib/google-docs-access.ts +51 -0
  1072. package/corpus/templates/slides-2/server/lib/google-docs-error.ts +54 -0
  1073. package/corpus/templates/slides-2/server/lib/google-docs-oauth.ts +422 -0
  1074. package/corpus/templates/slides-2/server/lib/google-fonts.ts +509 -0
  1075. package/corpus/templates/slides-2/server/lib/google-oauth-preflight.ts +119 -0
  1076. package/corpus/templates/slides-2/server/lib/google-slides-export-availability.ts +55 -0
  1077. package/corpus/templates/slides-2/server/lib/image-proxy-response.ts +16 -0
  1078. package/corpus/templates/slides-2/server/lib/image-proxy-url.ts +155 -0
  1079. package/corpus/templates/slides-2/server/lib/import-asset-storage.ts +214 -0
  1080. package/corpus/templates/slides-2/server/lib/imported-asset-batch.ts +218 -0
  1081. package/corpus/templates/slides-2/server/lib/legacy-slide-source.ts +692 -0
  1082. package/corpus/templates/slides-2/server/lib/media-search-providers.ts +216 -0
  1083. package/corpus/templates/slides-2/server/lib/native-creative-context.ts +295 -0
  1084. package/corpus/templates/slides-2/server/lib/pdf-parse-setup.ts +115 -0
  1085. package/corpus/templates/slides-2/server/lib/provider-api.ts +57 -0
  1086. package/corpus/templates/slides-2/server/lib/request-wait-until.ts +30 -0
  1087. package/corpus/templates/slides-2/server/lib/shared-deck.ts +29 -0
  1088. package/corpus/templates/slides-2/server/lib/slide-asset-references.ts +309 -0
  1089. package/corpus/templates/slides-2/server/lib/slide-content-patch.ts +1004 -0
  1090. package/corpus/templates/slides-2/server/lib/slide-hygiene.ts +731 -0
  1091. package/corpus/templates/slides-2/server/lib/slide-image-insertion.ts +136 -0
  1092. package/corpus/templates/slides-2/server/lib/slide-reveal-issues.ts +41 -0
  1093. package/corpus/templates/slides-2/server/lib/slide-source-editing/babel-walk.ts +117 -0
  1094. package/corpus/templates/slides-2/server/lib/slide-source-editing/batch-edit.ts +152 -0
  1095. package/corpus/templates/slides-2/server/lib/slide-source-editing/edit-ops.ts +1478 -0
  1096. package/corpus/templates/slides-2/server/lib/slide-source-editing/image-target.ts +128 -0
  1097. package/corpus/templates/slides-2/server/lib/slide-source-editing/text-diff.ts +23 -0
  1098. package/corpus/templates/slides-2/server/lib/slide-source-limits.ts +9 -0
  1099. package/corpus/templates/slides-2/server/lib/slide-source.ts +1786 -0
  1100. package/corpus/templates/slides-2/server/lib/slide-theme-demo-sources.ts +2106 -0
  1101. package/corpus/templates/slides-2/server/lib/slide-themes.ts +2174 -0
  1102. package/corpus/templates/slides-2/server/lib/slide-worker-runtime.ts +1995 -0
  1103. package/corpus/templates/slides-2/server/lib/slides-tracking.ts +73 -0
  1104. package/corpus/templates/slides-2/server/lib/source-import.ts +213 -0
  1105. package/corpus/templates/slides-2/server/lib/svgl.ts +364 -0
  1106. package/corpus/templates/slides-2/server/lib/tenant-files.ts +179 -0
  1107. package/corpus/templates/slides-2/server/lib/upload-storage-status.ts +9 -0
  1108. package/corpus/templates/slides-2/server/lib/uploaded-asset-cleanup-outbox.ts +521 -0
  1109. package/corpus/templates/slides-2/server/lib/uploaded-asset-object-lifecycle.ts +404 -0
  1110. package/corpus/templates/slides-2/server/lib/uploaded-fonts.ts +696 -0
  1111. package/corpus/templates/slides-2/server/lib/uploaded-reference-storage.ts +77 -0
  1112. package/corpus/templates/slides-2/server/lib/validate-slide-animations.ts +341 -0
  1113. package/corpus/templates/slides-2/server/middleware/auth.ts +6 -0
  1114. package/corpus/templates/slides-2/server/plugins/_dev-upload-stub.ts +6 -0
  1115. package/corpus/templates/slides-2/server/plugins/agent-chat.ts +402 -0
  1116. package/corpus/templates/slides-2/server/plugins/auth.ts +31 -0
  1117. package/corpus/templates/slides-2/server/plugins/collab.ts +30 -0
  1118. package/corpus/templates/slides-2/server/plugins/config.ts +6 -0
  1119. package/corpus/templates/slides-2/server/plugins/core-routes.ts +30 -0
  1120. package/corpus/templates/slides-2/server/plugins/creative-context.ts +187 -0
  1121. package/corpus/templates/slides-2/server/plugins/db.ts +462 -0
  1122. package/corpus/templates/slides-2/server/plugins/feature-flags.ts +5 -0
  1123. package/corpus/templates/slides-2/server/plugins/labs.ts +8 -0
  1124. package/corpus/templates/slides-2/server/plugins/orphaned-asset-cleanup.ts +17 -0
  1125. package/corpus/templates/slides-2/server/plugins/otel.ts +6 -0
  1126. package/corpus/templates/slides-2/server/plugins/transactional-emails.ts +5 -0
  1127. package/corpus/templates/slides-2/server/register-secrets.ts +209 -0
  1128. package/corpus/templates/slides-2/server/routes/[...page].get.ts +5 -0
  1129. package/corpus/templates/slides-2/server/routes/[...page].head.ts +1 -0
  1130. package/corpus/templates/slides-2/server/routes/[base]/_agent-native/google-docs/auth-url.get.ts +1 -0
  1131. package/corpus/templates/slides-2/server/routes/[base]/_agent-native/google-docs/callback.get.ts +1 -0
  1132. package/corpus/templates/slides-2/server/routes/_agent-native/google-docs/auth-url.get.ts +1 -0
  1133. package/corpus/templates/slides-2/server/routes/_agent-native/google-docs/callback.get.ts +1 -0
  1134. package/corpus/templates/slides-2/server/routes/api/assets/upload-video.post.ts +1 -0
  1135. package/corpus/templates/slides-2/server/routes/api/assets/upload.post.ts +1 -0
  1136. package/corpus/templates/slides-2/server/routes/api/assets/video-uploads.delete.ts +1 -0
  1137. package/corpus/templates/slides-2/server/routes/api/deck-agent-context.json.get.ts +105 -0
  1138. package/corpus/templates/slides-2/server/routes/api/decks/events.get.ts +1 -0
  1139. package/corpus/templates/slides-2/server/routes/api/demo.get.ts +1 -0
  1140. package/corpus/templates/slides-2/server/routes/api/exports/[filename].get.ts +221 -0
  1141. package/corpus/templates/slides-2/server/routes/api/exports/google-slides.post.ts +240 -0
  1142. package/corpus/templates/slides-2/server/routes/api/exports/html.post.ts +67 -0
  1143. package/corpus/templates/slides-2/server/routes/api/exports/pptx.post.ts +100 -0
  1144. package/corpus/templates/slides-2/server/routes/api/gen-preview/[...].get.ts +24 -0
  1145. package/corpus/templates/slides-2/server/routes/api/generated/[...].get.ts +24 -0
  1146. package/corpus/templates/slides-2/server/routes/api/image-proxy.get.ts +518 -0
  1147. package/corpus/templates/slides-2/server/routes/api/import-assets/[token].get.ts +70 -0
  1148. package/corpus/templates/slides-2/server/routes/api/preview-images.get.ts +20 -0
  1149. package/corpus/templates/slides-2/server/routes/api/share/[token].get.ts +1 -0
  1150. package/corpus/templates/slides-2/server/routes/api/uploads-chunked/[sessionId]/chunk.post.ts +1 -0
  1151. package/corpus/templates/slides-2/server/routes/api/uploads-chunked/[sessionId]/status.get.ts +1 -0
  1152. package/corpus/templates/slides-2/server/routes/api/uploads-chunked/[sessionId].delete.ts +1 -0
  1153. package/corpus/templates/slides-2/server/routes/api/uploads-chunked/start.post.ts +1 -0
  1154. package/corpus/templates/slides-2/server/routes/api/uploads.delete.ts +1 -0
  1155. package/corpus/templates/slides-2/server/routes/api/uploads.post.ts +1 -0
  1156. package/corpus/templates/slides-2/server/workspace-defaults.ts +196 -0
  1157. package/corpus/templates/slides-2/shared/action-ui.ts +147 -0
  1158. package/corpus/templates/slides-2/shared/agent-readable.ts +2 -0
  1159. package/corpus/templates/slides-2/shared/api.ts +406 -0
  1160. package/corpus/templates/slides-2/shared/app-state-tabs.ts +30 -0
  1161. package/corpus/templates/slides-2/shared/app-storage.ts +9 -0
  1162. package/corpus/templates/slides-2/shared/aspect-ratios.ts +49 -0
  1163. package/corpus/templates/slides-2/shared/blank-slide.ts +179 -0
  1164. package/corpus/templates/slides-2/shared/client-compatibility.ts +2 -0
  1165. package/corpus/templates/slides-2/shared/contrast-audit.ts +255 -0
  1166. package/corpus/templates/slides-2/shared/deck-access.ts +26 -0
  1167. package/corpus/templates/slides-2/shared/deck-app-scope.ts +14 -0
  1168. package/corpus/templates/slides-2/shared/deck-content.ts +37 -0
  1169. package/corpus/templates/slides-2/shared/deck-history-control.ts +79 -0
  1170. package/corpus/templates/slides-2/shared/deck-scope.ts +5 -0
  1171. package/corpus/templates/slides-2/shared/deck-title.ts +199 -0
  1172. package/corpus/templates/slides-2/shared/deck-write.ts +61 -0
  1173. package/corpus/templates/slides-2/shared/design-flags.ts +7 -0
  1174. package/corpus/templates/slides-2/shared/design-system-validation.ts +88 -0
  1175. package/corpus/templates/slides-2/shared/editor-control.ts +120 -0
  1176. package/corpus/templates/slides-2/shared/generation-timing.ts +19 -0
  1177. package/corpus/templates/slides-2/shared/google-docs.ts +69 -0
  1178. package/corpus/templates/slides-2/shared/labs.ts +10 -0
  1179. package/corpus/templates/slides-2/shared/overview-navigation.ts +34 -0
  1180. package/corpus/templates/slides-2/shared/ownership.ts +6 -0
  1181. package/corpus/templates/slides-2/shared/pdf-sidecar.ts +130 -0
  1182. package/corpus/templates/slides-2/shared/presenter-control.ts +111 -0
  1183. package/corpus/templates/slides-2/shared/representative-slide.ts +89 -0
  1184. package/corpus/templates/slides-2/shared/slide-background.ts +30 -0
  1185. package/corpus/templates/slides-2/shared/slide-comment-anchor.ts +117 -0
  1186. package/corpus/templates/slides-2/shared/slide-comment-reactions.ts +47 -0
  1187. package/corpus/templates/slides-2/shared/slide-design-tokens.ts +92 -0
  1188. package/corpus/templates/slides-2/shared/slide-fit.ts +78 -0
  1189. package/corpus/templates/slides-2/shared/slide-ids.ts +133 -0
  1190. package/corpus/templates/slides-2/shared/slide-number.ts +124 -0
  1191. package/corpus/templates/slides-2/shared/slide-object-structure.ts +790 -0
  1192. package/corpus/templates/slides-2/shared/slide-text-range-selection.ts +50 -0
  1193. package/corpus/templates/slides-2/shared/slide-transitions.ts +70 -0
  1194. package/corpus/templates/slides-2/shared/slides-2-transition.ts +194 -0
  1195. package/corpus/templates/slides-2/shared/slides-user-prefs.ts +22 -0
  1196. package/corpus/templates/slides-2/shared/source-import-errors.ts +12 -0
  1197. package/corpus/templates/slides-2/shared/source-runtime-export.ts +76 -0
  1198. package/corpus/templates/slides-2/shared/upload-types.ts +41 -0
  1199. package/corpus/templates/slides-2/ssr-entry.ts +11 -0
  1200. package/corpus/templates/slides-2/tsconfig.json +27 -0
  1201. package/corpus/templates/slides-2/vite.config.ts +77 -0
  1202. package/corpus/templates/slides-2/vitest.config.ts +28 -0
  1203. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +1 -1
  1204. package/package.json +1 -1
  1205. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +0 -182
  1206. package/corpus/templates/design/actions/apply-shader-fill.ts +0 -450
  1207. package/corpus/templates/design/actions/apply-shader.ts +0 -257
  1208. package/corpus/templates/design/actions/preview-shader-fill.ts +0 -157
  1209. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +0 -1144
  1210. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +0 -450
  1211. package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +0 -71
  1212. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +0 -555
  1213. package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +0 -105
  1214. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +0 -433
  1215. package/corpus/templates/design/app/components/design/inspector/color-picker-shader-pane.tsx +0 -648
  1216. package/corpus/templates/design/shared/shader-fill.ts +0 -174
  1217. package/corpus/templates/design/shared/shader-presets.ts +0 -1523
@@ -0,0 +1,2378 @@
1
+ /**
2
+ * patch-deck — granular, server-side read-modify-write for deck fields,
3
+ * individual slides, slide ordering, slide deletion, and slide addition.
4
+ *
5
+ * All mutations run under the same per-deck lock used by `add-slide` so
6
+ * concurrent writers touching DIFFERENT slides of the same deck never
7
+ * silently overwrite each other's work (the last-full-PUT-wins race).
8
+ *
9
+ * This action is called by the client editor instead of the old full-deck PUT.
10
+ * Agent actions (update-slide, add-slide, etc.) continue to use their own
11
+ * dedicated actions which also use the same per-deck lock.
12
+ */
13
+ import { ActionContractError } from "@agent-native/core";
14
+ import { defineAction, fail } from "@agent-native/core/action";
15
+ import type { ActionRunContext } from "@agent-native/core/action";
16
+ import { assertAccess } from "@agent-native/core/sharing";
17
+ import {
18
+ getGenerationCreativeContext,
19
+ mergeCreativeContextReuseLabels,
20
+ recordGenerationCreativeContext,
21
+ replaceCreativeContextElementProvenance,
22
+ validateGenerationCreativeContext,
23
+ } from "@agent-native/creative-context/server";
24
+ import type { CreativeContextReuseLabel } from "@agent-native/creative-context/types";
25
+ import { eq } from "drizzle-orm";
26
+ import { z } from "zod";
27
+
28
+ import { getPreset, type TweakDefinition } from "../app/lib/design-systems.js";
29
+ import {
30
+ normalizeSlidePadding,
31
+ normalizeSlidePaddingForWrite,
32
+ } from "../app/lib/normalize-slide-padding.js";
33
+ import { getDb, schema } from "../server/db/index.js";
34
+ import { notifyClients } from "../server/handlers/decks.js";
35
+ import {
36
+ createDeckVersionSnapshot,
37
+ deckVersionChangeGroupFromAction,
38
+ deckVersionChatContextFromAction,
39
+ deckVersionContentSignature,
40
+ } from "../server/lib/deck-versions.js";
41
+ import { lockSlideAssetReferences } from "../server/lib/slide-asset-references.js";
42
+ import {
43
+ applySlideContentEdits,
44
+ SlideContentEditSchema,
45
+ type SlideContentEdit,
46
+ } from "../server/lib/slide-content-patch.js";
47
+ import {
48
+ HYGIENE_ACTION_DESCRIPTION,
49
+ slideHygieneResult,
50
+ } from "../server/lib/slide-hygiene.js";
51
+ import {
52
+ isSlideSourceWithinByteLimit,
53
+ MAX_SLIDE_SOURCE_BYTES,
54
+ SLIDE_SOURCE_BYTE_LIMIT_MESSAGE,
55
+ } from "../server/lib/slide-source-limits.js";
56
+ import {
57
+ assertSourceSlidePreserved,
58
+ sourceImportForDeck,
59
+ type SourceImportMetadata,
60
+ } from "../server/lib/source-import.js";
61
+ import {
62
+ assertSlideAnimationsResolve,
63
+ type SlideAnimationBaseline,
64
+ } from "../server/lib/validate-slide-animations.js";
65
+ import { ANIMATION_TRIGGERS } from "../shared/api.js";
66
+ import { ASPECT_RATIO_VALUES } from "../shared/aspect-ratios.js";
67
+ import { isRealSlide } from "../shared/blank-slide.js";
68
+ import { stableStringify } from "../shared/deck-content.js";
69
+ import {
70
+ assertHumanReadableDeckTitle,
71
+ repairGeneratedDeckTitle,
72
+ } from "../shared/deck-title.js";
73
+ import { isMergeSafeDeckPatchOperations } from "../shared/deck-write.js";
74
+ import {
75
+ createLayoutFitRevision,
76
+ deckFitRenderFieldsChanged,
77
+ hashSlideContent,
78
+ slideFitRenderFieldsChanged,
79
+ } from "../shared/slide-fit.js";
80
+ import {
81
+ clampTransitionSpeed,
82
+ normalizeSlideTransition,
83
+ SLIDE_TRANSITION_INPUTS,
84
+ SLIDE_TRANSITION_SPEED,
85
+ } from "../shared/slide-transitions.js";
86
+ import { assertSlides2DeckData } from "./_deck-app-scope.js";
87
+ import {
88
+ trackDeckCreationStarted,
89
+ trackSlideContentEdited,
90
+ } from "./_deck-tracking.js";
91
+ import {
92
+ assertDeckWriteApplied,
93
+ assertDeckClientWriteCurrent,
94
+ deckRevisionWhere,
95
+ deckClientWriteFields,
96
+ deckClientWriteSchema,
97
+ nextDeckRevision,
98
+ retryDeckWrite,
99
+ } from "./_deck-write.js";
100
+ import { assertSlideInlineImagesPersistable } from "./_inline-image-storage.js";
101
+ import {
102
+ assertNoRenderArtifacts,
103
+ assertNoRenderArtifactsInNewSlide,
104
+ } from "./_render-artifacts.js";
105
+ import {
106
+ assertSourceBackedSlidesUnchanged,
107
+ compileSlideSource,
108
+ renderBlankSlideSource,
109
+ } from "./_slide-source.js";
110
+
111
+ const LOCK_KEY = "__slidesDeckPatchLocks" as const;
112
+ type GlobalWithLocks = typeof globalThis & {
113
+ [LOCK_KEY]?: Map<string, Promise<unknown>>;
114
+ };
115
+ const globalRef = globalThis as GlobalWithLocks;
116
+ if (!globalRef[LOCK_KEY]) {
117
+ globalRef[LOCK_KEY] = new Map<string, Promise<unknown>>();
118
+ }
119
+ const deckLocks: Map<string, Promise<unknown>> = globalRef[LOCK_KEY]!;
120
+
121
+ export function withDeckLock<T>(
122
+ deckId: string,
123
+ fn: () => Promise<T>,
124
+ ): Promise<T> {
125
+ const prev = deckLocks.get(deckId) ?? Promise.resolve();
126
+ const next = prev.then(fn, fn);
127
+ deckLocks.set(deckId, next);
128
+ next
129
+ .finally(() => {
130
+ if (deckLocks.get(deckId) === next) deckLocks.delete(deckId);
131
+ })
132
+ .catch(() => {});
133
+ return next;
134
+ }
135
+
136
+ const SlideAnimationSchema = z.object({
137
+ id: z.string().min(1).describe("Stable ID for this ordered reveal step"),
138
+ elementIndex: z
139
+ .number()
140
+ .int()
141
+ .min(0)
142
+ .describe(
143
+ "0-based legacy child index. Resolution uses elementPath when present, so keep it paired with elementPath for compatibility.",
144
+ ),
145
+ elementPath: z
146
+ .array(z.number().int().min(0))
147
+ .min(1)
148
+ .optional()
149
+ .describe(
150
+ "Preferred 0-based child-index path from the outer .fmd-slide wrapper. Required when this patch sends an animations list; reveals that a content patch keeps do not need it. Re-read final HTML after content edits.",
151
+ ),
152
+ byParagraph: z
153
+ .boolean()
154
+ .optional()
155
+ .describe(
156
+ "Reveal each paragraph or list item inside this element as its own click step, in document order. A paragraph reveal starts at that paragraph and runs through the rest of its container. With nothing to split it is one step.",
157
+ ),
158
+ trigger: z
159
+ .enum(ANIMATION_TRIGGERS)
160
+ .optional()
161
+ .describe(
162
+ "When this step plays. on-click (default) waits for its own click. with-previous plays in the same click as the step before it, so the objects appear together. The first step always waits for a click.",
163
+ ),
164
+ type: z
165
+ .enum(["appear", "fade", "slide-up", "zoom"])
166
+ .describe(
167
+ "Animation used when this step is revealed. Supported semantics: appear (immediate reveal), fade (opacity), slide-up (subtle upward motion), zoom (subtle scale). Do not invent other types.",
168
+ ),
169
+ });
170
+
171
+ const SLIDE_TRANSITION_DESCRIPTION =
172
+ "Transition on the boundary before this slide. It plays in either direction, and this slide, the later of the pair, supplies the type and speed; backward plays it reversed. One of: none, dissolve, fade, slide-from-right, slide-from-left, flip, cube, gallery. Legacy instant, slide, and zoom are accepted and stored as their canonical equivalents. Null resets to none. On patch-slide, omitting it keeps the current transition; on add-slide, omitting it means none.";
173
+ const SLIDE_TRANSITION_SPEED_DESCRIPTION =
174
+ "Speed of this slide's transition, from 0 (slowest, 5.0 s) to 49 (fastest, 0.1 s). Null resets to the default 45 (0.5 s). On patch-slide, omitting it keeps the current speed; on add-slide, omitting it means the default 45 (0.5 s).";
175
+
176
+ const SlideFieldsSchema = z.object({
177
+ content: z.string().optional(),
178
+ notes: z.string().optional(),
179
+ background: z.string().nullable().optional(),
180
+ layout: z.string().optional(),
181
+ layoutWarningDismissed: z.boolean().nullable().optional(),
182
+ imageUrl: z.string().nullable().optional(),
183
+ imageLoading: z.boolean().nullable().optional(),
184
+ imagePrompt: z.string().nullable().optional(),
185
+ excalidrawData: z.string().nullable().optional(),
186
+ transition: z
187
+ .enum(SLIDE_TRANSITION_INPUTS)
188
+ .nullable()
189
+ .optional()
190
+ .describe(SLIDE_TRANSITION_DESCRIPTION),
191
+ transitionSpeed: z
192
+ .number()
193
+ .int()
194
+ .min(SLIDE_TRANSITION_SPEED.min)
195
+ .max(SLIDE_TRANSITION_SPEED.max)
196
+ .nullable()
197
+ .optional()
198
+ .describe(SLIDE_TRANSITION_SPEED_DESCRIPTION),
199
+ animations: z
200
+ .array(SlideAnimationSchema)
201
+ .nullable()
202
+ .optional()
203
+ .describe(
204
+ "Complete ordered on-click reveal list. Include every intended target in order; unlisted elements remain visible. Use elementPath from the final HTML and 0-based indexes. Omit it to keep the current reveals; a content change that strands one, or moves one onto a different element, fails with a slide_animation_* error (400) and writes nothing. [] or null clears every reveal.",
205
+ ),
206
+ splitByParagraph: z.boolean().nullable().optional(),
207
+ skipped: z
208
+ .boolean()
209
+ .nullable()
210
+ .optional()
211
+ .describe(
212
+ "Exclude this slide from Present/Presenter playback without deleting it.",
213
+ ),
214
+ });
215
+
216
+ const SlideFieldBaselineSchema = z.discriminatedUnion("present", [
217
+ z.object({ present: z.literal(false) }),
218
+ z.object({ present: z.literal(true), value: z.unknown() }),
219
+ ]);
220
+
221
+ const PatchSlideOp = z
222
+ .object({
223
+ op: z.literal("patch-slide"),
224
+ slideId: z.string(),
225
+ fields: SlideFieldsSchema.default({}),
226
+ edits: z
227
+ .array(SlideContentEditSchema)
228
+ .min(1)
229
+ .optional()
230
+ .describe(
231
+ "Ordered find/replace edits against this slide's current HTML, same shape as update-slide edits. Prefer this over fields.content for small changes such as a color swap, so unchanged markup is not regenerated. Do not combine with fields.content.",
232
+ ),
233
+ baseContentHash: z
234
+ .string()
235
+ .min(1)
236
+ .optional()
237
+ .describe(
238
+ "Content hash from this slide's exact get-deck source. Required whenever fields.content is replaced.",
239
+ ),
240
+ baseFields: z
241
+ .record(z.string(), SlideFieldBaselineSchema)
242
+ .optional()
243
+ .describe(
244
+ "Exact snapshots for every non-content field being replaced: use {present:false} when absent, otherwise {present:true,value}. Stale retries only apply when each field still matches its snapshot or the requested value.",
245
+ ),
246
+ preserveSource: z
247
+ .boolean()
248
+ .optional()
249
+ .default(true)
250
+ .describe(
251
+ "Keep source-imported images and factual copy (default true). Set false only for an explicit rewrite.",
252
+ ),
253
+ })
254
+ .superRefine((operation, context) => {
255
+ if (
256
+ operation.edits !== undefined &&
257
+ operation.fields.content !== undefined
258
+ ) {
259
+ context.addIssue({
260
+ code: z.ZodIssueCode.custom,
261
+ path: ["edits"],
262
+ message: "Use either edits or fields.content, not both",
263
+ });
264
+ }
265
+ if (
266
+ operation.fields.content !== undefined &&
267
+ operation.baseContentHash === undefined
268
+ ) {
269
+ context.addIssue({
270
+ code: z.ZodIssueCode.custom,
271
+ path: ["baseContentHash"],
272
+ message: "Content replacement requires the source slide contentHash",
273
+ });
274
+ }
275
+ if (operation.baseFields !== undefined) {
276
+ const touchedFields = Object.keys(operation.fields).filter(
277
+ (field) =>
278
+ field !== "content" &&
279
+ operation.fields[field as keyof typeof operation.fields] !==
280
+ undefined,
281
+ );
282
+ for (const field of touchedFields) {
283
+ if (!Object.hasOwn(operation.baseFields, field)) {
284
+ context.addIssue({
285
+ code: z.ZodIssueCode.custom,
286
+ path: ["baseFields", field],
287
+ message: "Snapshot every non-content field being replaced",
288
+ });
289
+ }
290
+ }
291
+ for (const field of Object.keys(operation.baseFields)) {
292
+ if (!touchedFields.includes(field)) {
293
+ context.addIssue({
294
+ code: z.ZodIssueCode.custom,
295
+ path: ["baseFields", field],
296
+ message: "Only snapshot fields included in fields",
297
+ });
298
+ }
299
+ }
300
+ }
301
+ });
302
+
303
+ const DeleteSlideOp = z.object({
304
+ op: z.literal("delete-slide"),
305
+ slideId: z.string(),
306
+ allowEmpty: z
307
+ .boolean()
308
+ .optional()
309
+ .describe("Keep the deck empty when deleting its last slide."),
310
+ });
311
+
312
+ const ReorderSlidesOp = z
313
+ .object({
314
+ op: z.literal("reorder-slides"),
315
+ orderedIds: z
316
+ .array(z.string())
317
+ .describe(
318
+ "Desired slide ID order; concurrent additions remain appended.",
319
+ ),
320
+ })
321
+ .superRefine(({ orderedIds }, context) => {
322
+ const duplicateId = firstDuplicate(orderedIds);
323
+ if (duplicateId === undefined) return;
324
+ context.addIssue({
325
+ code: z.ZodIssueCode.custom,
326
+ path: ["orderedIds"],
327
+ message: `Slide ID ${duplicateId} appears more than once`,
328
+ });
329
+ });
330
+
331
+ const AddSlideOp = z
332
+ .object({
333
+ op: z.literal("add-slide"),
334
+ slideId: z.string(),
335
+ afterSlideId: z.string().optional(), // insert after this slide; append if absent
336
+ duplicateSlideId: z.string().optional(),
337
+ fields: z
338
+ .object({
339
+ content: z.string().optional(),
340
+ sourceTsx: z
341
+ .string()
342
+ .max(MAX_SLIDE_SOURCE_BYTES)
343
+ .refine(isSlideSourceWithinByteLimit, {
344
+ message: SLIDE_SOURCE_BYTE_LIMIT_MESSAGE,
345
+ })
346
+ .optional(),
347
+ notes: z.string().optional(),
348
+ layout: z.string().optional(),
349
+ background: z.string().optional(),
350
+ imageUrl: z.string().optional(),
351
+ imagePrompt: z.string().optional(),
352
+ excalidrawData: z.string().optional(),
353
+ transition: z
354
+ .enum(SLIDE_TRANSITION_INPUTS)
355
+ .nullable()
356
+ .optional()
357
+ .describe(SLIDE_TRANSITION_DESCRIPTION),
358
+ transitionSpeed: z
359
+ .number()
360
+ .int()
361
+ .min(SLIDE_TRANSITION_SPEED.min)
362
+ .max(SLIDE_TRANSITION_SPEED.max)
363
+ .nullable()
364
+ .optional()
365
+ .describe(SLIDE_TRANSITION_SPEED_DESCRIPTION),
366
+ animations: z
367
+ .array(SlideAnimationSchema)
368
+ .nullable()
369
+ .optional()
370
+ .describe(
371
+ "Complete ordered on-click reveal list for the new slide. Omitted, null, or [] means no reveals. Use elementPath from the final HTML and 0-based indexes.",
372
+ ),
373
+ splitByParagraph: z.boolean().optional(),
374
+ skipped: z.boolean().optional(),
375
+ })
376
+ .passthrough(),
377
+ })
378
+ .superRefine(({ duplicateSlideId, fields }, context) => {
379
+ if (duplicateSlideId !== undefined) {
380
+ if (fields.sourceTsx !== undefined || fields.content !== undefined) {
381
+ context.addIssue({
382
+ code: z.ZodIssueCode.custom,
383
+ path: ["fields"],
384
+ message:
385
+ "Duplicate slides inherit source and preview from the selected slide.",
386
+ });
387
+ }
388
+ return;
389
+ }
390
+ if ((fields.content === undefined) === (fields.sourceTsx === undefined)) {
391
+ context.addIssue({
392
+ code: z.ZodIssueCode.custom,
393
+ path: ["fields", "sourceTsx"],
394
+ message:
395
+ "Pass exactly one of sourceTsx or legacy content when adding a slide.",
396
+ });
397
+ }
398
+ });
399
+
400
+ const PatchDeckFieldsOp = z.object({
401
+ op: z.literal("patch-deck-fields"),
402
+ fields: z
403
+ .object({
404
+ title: z.string().optional(),
405
+ designSystemId: z.string().nullable().optional(),
406
+ tweaks: z
407
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
408
+ .nullable()
409
+ .optional(),
410
+ aspectRatio: z.enum(ASPECT_RATIO_VALUES).nullable().optional(),
411
+ shareToken: z.string().optional(),
412
+ visibility: z.enum(["private", "org", "public"]).optional(),
413
+ starred: z.boolean().nullable().optional(),
414
+ generationContext: z.record(z.string(), z.unknown()).optional(),
415
+ })
416
+ .passthrough(),
417
+ });
418
+
419
+ export const OperationSchema = z.discriminatedUnion("op", [
420
+ PatchSlideOp,
421
+ DeleteSlideOp,
422
+ ReorderSlidesOp,
423
+ AddSlideOp,
424
+ PatchDeckFieldsOp,
425
+ ]);
426
+
427
+ export type Operation = z.infer<typeof OperationSchema>;
428
+ type PatchSlideOperation = Extract<Operation, { op: "patch-slide" }>;
429
+
430
+ const MCP_WIDGET_PATCH_SLIDE_FIELDS = new Set([
431
+ "content",
432
+ "notes",
433
+ "background",
434
+ "layout",
435
+ "layoutWarningDismissed",
436
+ "imageUrl",
437
+ "excalidrawData",
438
+ "transition",
439
+ "transitionSpeed",
440
+ "animations",
441
+ "splitByParagraph",
442
+ "skipped",
443
+ ]);
444
+
445
+ const MCP_WIDGET_ADD_SLIDE_FIELDS = new Set([
446
+ "content",
447
+ "notes",
448
+ "background",
449
+ "layout",
450
+ "layoutWarningDismissed",
451
+ "imageUrl",
452
+ "excalidrawData",
453
+ "transition",
454
+ "transitionSpeed",
455
+ "animations",
456
+ "splitByParagraph",
457
+ "skipped",
458
+ ]);
459
+
460
+ function hasOnlyMcpWidgetSlideFields(
461
+ fields: object,
462
+ allowedFields: Set<string>,
463
+ ): boolean {
464
+ const names = Object.keys(fields);
465
+ return (
466
+ names.length > 0 &&
467
+ names.every((name) => allowedFields.has(name)) &&
468
+ Object.values(fields).every((value) => value !== undefined)
469
+ );
470
+ }
471
+
472
+ function hasOnlyMcpWidgetOperationKeys(
473
+ operation: object,
474
+ allowedKeys: readonly string[],
475
+ requiredKeys: readonly string[],
476
+ ): boolean {
477
+ const keys = Object.keys(operation);
478
+ return (
479
+ keys.every((key) => allowedKeys.includes(key)) &&
480
+ requiredKeys.every((key) => Object.hasOwn(operation, key))
481
+ );
482
+ }
483
+
484
+ function hasExactMcpWidgetFieldBaselines(
485
+ fields: object,
486
+ baseFields: PatchSlideOperation["baseFields"],
487
+ ): boolean {
488
+ const fieldsByName = fields as Record<string, unknown>;
489
+ const changedFields = Object.keys(fields).filter(
490
+ (field) => field !== "content" && fieldsByName[field] !== undefined,
491
+ );
492
+ if (changedFields.length === 0) return baseFields === undefined;
493
+ if (!baseFields) return false;
494
+
495
+ const baselineFields = Object.keys(baseFields);
496
+ return (
497
+ baselineFields.length === changedFields.length &&
498
+ baselineFields.every((field) => {
499
+ const baseline = baseFields[field];
500
+ if (!changedFields.includes(field) || !baseline) return false;
501
+ const baselineKeys = Object.keys(baseline);
502
+ return baseline.present === false
503
+ ? baselineKeys.length === 1 && baselineKeys[0] === "present"
504
+ : baselineKeys.length === 2 &&
505
+ baselineKeys.includes("present") &&
506
+ baselineKeys.includes("value");
507
+ })
508
+ );
509
+ }
510
+
511
+ function hasOnlyMcpWidgetTweakSelections(
512
+ value: unknown,
513
+ tweakDefinitions: readonly TweakDefinition[],
514
+ ): boolean {
515
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
516
+ const tweaks = value as Record<string, unknown>;
517
+ const definitions = new Map(
518
+ tweakDefinitions.map((tweak) => [tweak.id, tweak]),
519
+ );
520
+ const entries = Object.entries(tweaks);
521
+ return (
522
+ entries.length > 0 &&
523
+ entries.every(([id, selectedValue]) => {
524
+ const definition = definitions.get(id);
525
+ return (
526
+ typeof selectedValue === "string" &&
527
+ (definition?.options?.some(
528
+ (option) => option.value === selectedValue,
529
+ ) ??
530
+ false)
531
+ );
532
+ })
533
+ );
534
+ }
535
+
536
+ function invalidTweakSelectionIds(
537
+ value: unknown,
538
+ tweakDefinitions: readonly TweakDefinition[],
539
+ ): string[] {
540
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
541
+ return ["(expected a tweak map)"];
542
+ }
543
+
544
+ const definitions = new Map(
545
+ tweakDefinitions.map((tweak) => [tweak.id, tweak]),
546
+ );
547
+ return Object.entries(value as Record<string, unknown>)
548
+ .filter(([id, selectedValue]) => {
549
+ const definition = definitions.get(id);
550
+ if (!definition) return true;
551
+ if (definition.type === "toggle")
552
+ return typeof selectedValue !== "boolean";
553
+ if (definition.type === "slider") {
554
+ return (
555
+ typeof selectedValue !== "number" || !Number.isFinite(selectedValue)
556
+ );
557
+ }
558
+ return (
559
+ typeof selectedValue !== "string" ||
560
+ !definition.options?.some((option) => option.value === selectedValue)
561
+ );
562
+ })
563
+ .map(([id]) => id);
564
+ }
565
+
566
+ export function isMcpWidgetPatchAllowed(
567
+ caller: string | undefined,
568
+ operations: Operation[],
569
+ options?: {
570
+ rewriteSource?: boolean;
571
+ hasCreativeContext?: boolean;
572
+ requireAllSourceSlides?: boolean;
573
+ tweakDefinitions?: readonly TweakDefinition[];
574
+ },
575
+ ): boolean {
576
+ if (caller !== "mcp-widget-write") return true;
577
+ if (
578
+ operations.length === 0 ||
579
+ options?.rewriteSource ||
580
+ options?.hasCreativeContext ||
581
+ options?.requireAllSourceSlides
582
+ ) {
583
+ return false;
584
+ }
585
+
586
+ return operations.every((operation) => {
587
+ if (operation.op === "patch-deck-fields") {
588
+ if (
589
+ !hasOnlyMcpWidgetOperationKeys(
590
+ operation,
591
+ ["op", "fields"],
592
+ ["op", "fields"],
593
+ ) ||
594
+ Object.keys(operation.fields).length !== 1
595
+ ) {
596
+ return false;
597
+ }
598
+ if (typeof operation.fields.title === "string") return true;
599
+
600
+ return (
601
+ options?.tweakDefinitions !== undefined &&
602
+ hasOnlyMcpWidgetTweakSelections(
603
+ operation.fields.tweaks,
604
+ options.tweakDefinitions,
605
+ )
606
+ );
607
+ }
608
+
609
+ if (operation.op === "patch-slide") {
610
+ const hasContent = operation.fields.content !== undefined;
611
+ return (
612
+ hasOnlyMcpWidgetOperationKeys(
613
+ operation,
614
+ [
615
+ "op",
616
+ "slideId",
617
+ "fields",
618
+ "baseContentHash",
619
+ "baseFields",
620
+ "preserveSource",
621
+ ],
622
+ ["op", "slideId", "fields"],
623
+ ) &&
624
+ hasOnlyMcpWidgetSlideFields(
625
+ operation.fields,
626
+ MCP_WIDGET_PATCH_SLIDE_FIELDS,
627
+ ) &&
628
+ Object.values(operation.fields).every((value) => value !== undefined) &&
629
+ hasExactMcpWidgetFieldBaselines(
630
+ operation.fields,
631
+ operation.baseFields,
632
+ ) &&
633
+ (operation.fields.layoutWarningDismissed === undefined ||
634
+ typeof operation.fields.layoutWarningDismissed === "boolean") &&
635
+ (hasContent
636
+ ? typeof operation.fields.content === "string" &&
637
+ typeof operation.baseContentHash === "string" &&
638
+ operation.baseContentHash.length > 0
639
+ : operation.baseContentHash === undefined) &&
640
+ operation.preserveSource !== false
641
+ );
642
+ }
643
+
644
+ if (operation.op === "add-slide") {
645
+ return (
646
+ hasOnlyMcpWidgetOperationKeys(
647
+ operation,
648
+ ["op", "slideId", "afterSlideId", "fields"],
649
+ ["op", "slideId", "fields"],
650
+ ) &&
651
+ typeof operation.fields.content === "string" &&
652
+ hasOnlyMcpWidgetSlideFields(
653
+ operation.fields,
654
+ MCP_WIDGET_ADD_SLIDE_FIELDS,
655
+ ) &&
656
+ (operation.fields.layoutWarningDismissed === undefined ||
657
+ typeof operation.fields.layoutWarningDismissed === "boolean")
658
+ );
659
+ }
660
+
661
+ if (operation.op === "delete-slide") {
662
+ return (
663
+ hasOnlyMcpWidgetOperationKeys(
664
+ operation,
665
+ ["op", "slideId", "allowEmpty"],
666
+ ["op", "slideId"],
667
+ ) && operation.allowEmpty !== true
668
+ );
669
+ }
670
+
671
+ if (operation.op === "reorder-slides") {
672
+ return (
673
+ hasOnlyMcpWidgetOperationKeys(
674
+ operation,
675
+ ["op", "orderedIds"],
676
+ ["op", "orderedIds"],
677
+ ) && operation.orderedIds.length > 0
678
+ );
679
+ }
680
+
681
+ return false;
682
+ });
683
+ }
684
+
685
+ function persistedTargetSlideCount(deck: unknown): number | null {
686
+ if (!deck || typeof deck !== "object" || Array.isArray(deck)) return null;
687
+ const generationContext = (deck as Record<string, unknown>).generationContext;
688
+ if (
689
+ !generationContext ||
690
+ typeof generationContext !== "object" ||
691
+ Array.isArray(generationContext)
692
+ ) {
693
+ return null;
694
+ }
695
+ const targetSlideCount = (generationContext as Record<string, unknown>)
696
+ .targetSlideCount;
697
+ return typeof targetSlideCount === "number" &&
698
+ Number.isInteger(targetSlideCount) &&
699
+ targetSlideCount > 0
700
+ ? targetSlideCount
701
+ : null;
702
+ }
703
+
704
+ interface ProjectedSlide {
705
+ id: unknown;
706
+ content?: unknown;
707
+ excalidrawData?: unknown;
708
+ real: boolean;
709
+ }
710
+
711
+ function projectedRealSlideCount(
712
+ slides: unknown[],
713
+ operations: Operation[],
714
+ ): { initial: number; count: number; added: boolean } {
715
+ const projected = slides.map((slide): ProjectedSlide => {
716
+ const { id, content, excalidrawData } = (
717
+ slide && typeof slide === "object" && !Array.isArray(slide) ? slide : {}
718
+ ) as { id?: unknown; content?: unknown; excalidrawData?: unknown };
719
+ return { id, content, excalidrawData, real: isRealSlide(slide) };
720
+ });
721
+ const initial = projected.filter(({ real }) => real).length;
722
+ let added = false;
723
+
724
+ for (const operation of operations) {
725
+ if (operation.op === "add-slide") {
726
+ if (projected.some(({ id }) => id === operation.slideId)) continue;
727
+ const { content, excalidrawData } = operation.fields;
728
+ projected.push({
729
+ id: operation.slideId,
730
+ content,
731
+ excalidrawData,
732
+ real: isRealSlide(operation.fields),
733
+ });
734
+ added = true;
735
+ continue;
736
+ }
737
+ if (operation.op === "patch-slide") {
738
+ const slide = projected.find(({ id }) => id === operation.slideId);
739
+ const { content, excalidrawData } = operation.fields;
740
+ if (slide && (content !== undefined || excalidrawData !== undefined)) {
741
+ if (content !== undefined) slide.content = content;
742
+ if (excalidrawData !== undefined) slide.excalidrawData = excalidrawData;
743
+ slide.real = isRealSlide(slide);
744
+ }
745
+ continue;
746
+ }
747
+ if (operation.op !== "delete-slide") continue;
748
+
749
+ const index = projected.findIndex(({ id }) => id === operation.slideId);
750
+ if (index !== -1) projected.splice(index, 1);
751
+ if (projected.length === 0 && !operation.allowEmpty) {
752
+ projected.push({ id: undefined, real: true });
753
+ }
754
+ }
755
+
756
+ return { initial, count: projected.filter(({ real }) => real).length, added };
757
+ }
758
+
759
+ function firstDuplicate(values: readonly string[]): string | undefined {
760
+ const seen = new Set<string>();
761
+ for (const value of values) {
762
+ if (seen.has(value)) return value;
763
+ seen.add(value);
764
+ }
765
+ return undefined;
766
+ }
767
+
768
+ export function assertSlidesWidgetWriteScope(
769
+ deckId: string,
770
+ context: ActionRunContext | undefined,
771
+ ): void {
772
+ if (context?.caller !== "mcp-widget-write") return;
773
+
774
+ const grant = context.mcpDirectoryWidgetWrite;
775
+ if (
776
+ !grant ||
777
+ grant.appId !== "slides-2" ||
778
+ grant.resourceIds.deckId !== deckId ||
779
+ !grant.actionNames.includes("patch-deck")
780
+ ) {
781
+ throw new ActionContractError(
782
+ "This Slides widget write capability is missing or scoped to a different deck or action.",
783
+ {
784
+ errorCode: "mcp_widget_write_scope_mismatch",
785
+ statusCode: 403,
786
+ },
787
+ );
788
+ }
789
+ }
790
+
791
+ export function assertSourceImportSlidesCovered(
792
+ metadata: SourceImportMetadata | null,
793
+ operations: Operation[],
794
+ requireAllSourceSlides: boolean,
795
+ ): void {
796
+ if (!metadata || !requireAllSourceSlides) return;
797
+
798
+ const sourceSlideIds =
799
+ Array.isArray(metadata.slideIds) && metadata.slideIds.length > 0
800
+ ? metadata.slideIds
801
+ : metadata.slides.map((slide) => slide.id);
802
+ const patchedContentSlideIds = new Set(
803
+ operations.flatMap((operation) =>
804
+ operation.op === "patch-slide" && operation.fields.content !== undefined
805
+ ? [operation.slideId]
806
+ : [],
807
+ ),
808
+ );
809
+ const missingSlideIds = sourceSlideIds.filter(
810
+ (slideId) => !patchedContentSlideIds.has(slideId),
811
+ );
812
+ if (missingSlideIds.length === 0) return;
813
+
814
+ fail(
815
+ `Deck-wide source restyle requires one content patch per imported slide. Missing ${missingSlideIds.length} slide(s): ${missingSlideIds.slice(0, 12).join(", ")}${missingSlideIds.length > 12 ? ", …" : ""}. Continue with every source slide ID in one patch-deck call before verifying the changed slides with get-deck slideIds and compact=false.`,
816
+ { errorCode: "incomplete_source_restyle", statusCode: 422 },
817
+ );
818
+ }
819
+
820
+ // Edit results keep the wrapper exactly as written: re-normalizing would insert
821
+ // default padding when a color or --deck-* edit touches an unpadded wrapper.
822
+ const editResolvedOperations = new WeakSet<Operation>();
823
+
824
+ async function resolvePatchSlideEdits(
825
+ operations: Operation[],
826
+ slides: Array<{ id?: unknown; content?: unknown }>,
827
+ ): Promise<Operation[]> {
828
+ // Resolved in order so a later op on the same slide builds on the content
829
+ // an earlier op produced instead of clobbering it. baseContentHash still
830
+ // names the pre-batch content, which is what the stale check compares.
831
+ const pendingContent = new Map<string, string>();
832
+ const resolved: Operation[] = [];
833
+ for (const operation of operations) {
834
+ if (operation.op !== "patch-slide") {
835
+ resolved.push(operation);
836
+ continue;
837
+ }
838
+ if (!operation.edits) {
839
+ if (operation.fields.content !== undefined) {
840
+ const source = slides.find((entry) => entry?.id === operation.slideId);
841
+ pendingContent.set(
842
+ operation.slideId,
843
+ normalizeSlidePaddingForWrite(
844
+ typeof source?.content === "string" ? source.content : undefined,
845
+ operation.fields.content,
846
+ ),
847
+ );
848
+ }
849
+ resolved.push(operation);
850
+ continue;
851
+ }
852
+ const slide = slides.find((entry) => entry?.id === operation.slideId);
853
+ if (!slide) {
854
+ fail(
855
+ `patch-slide edits target slide "${operation.slideId}", which does not exist in the current deck. Check the slideId; if this slide is added by add-slide in the same call, edits cannot target it until a later call. No slides were changed.`,
856
+ { errorCode: "slide_not_found", statusCode: 404 },
857
+ );
858
+ }
859
+ const originalContent = String(slide.content ?? "");
860
+ const currentContent =
861
+ pendingContent.get(operation.slideId) ?? originalContent;
862
+ const { edits, ...rest } = operation;
863
+ const patched = await applySlideContentEdits(
864
+ currentContent,
865
+ edits as SlideContentEdit[],
866
+ );
867
+ pendingContent.set(operation.slideId, patched.content);
868
+ const next: Operation = {
869
+ ...rest,
870
+ fields: { ...rest.fields, content: patched.content },
871
+ baseContentHash:
872
+ rest.baseContentHash ?? hashSlideContent(originalContent),
873
+ };
874
+ editResolvedOperations.add(next);
875
+ resolved.push(next);
876
+ }
877
+ return resolved;
878
+ }
879
+
880
+ const AgentPatchDeckInputSchema = z.object({
881
+ deckId: z.string().describe("Deck ID"),
882
+ rewriteSource: z
883
+ .boolean()
884
+ .optional()
885
+ .default(false)
886
+ .describe(
887
+ "Legacy compatibility flag. When true on a source-imported deck, clear its source-import metadata; structural edits no longer require this flag.",
888
+ ),
889
+ requireAllSourceSlides: z
890
+ .boolean()
891
+ .optional()
892
+ .default(false)
893
+ .describe(
894
+ "For a deck-wide source-import restyle, require one content patch for every imported slide before any write is committed.",
895
+ ),
896
+ operations: z
897
+ .array(
898
+ z.union([
899
+ PatchSlideOp,
900
+ DeleteSlideOp,
901
+ ReorderSlidesOp,
902
+ AddSlideOp,
903
+ z.object({
904
+ op: z.literal("patch-deck-fields"),
905
+ fields: z.object({
906
+ title: z
907
+ .string()
908
+ .optional()
909
+ .describe("The concise, specific title to apply to the deck"),
910
+ designSystemId: z
911
+ .null()
912
+ .optional()
913
+ .describe(
914
+ "Set to null to unlink a deleted or inaccessible design system. Use apply-design-system to link a design system.",
915
+ ),
916
+ tweaks: z
917
+ .record(
918
+ z.string(),
919
+ z.union([z.string(), z.number(), z.boolean()]),
920
+ )
921
+ .optional()
922
+ .describe(
923
+ "Complete stored tweak map for this deck's current design-system controls. This replaces the stored map; copy storedTweaks from view-screen, then change requested keys using the listed choices.",
924
+ ),
925
+ starred: z
926
+ .boolean()
927
+ .optional()
928
+ .describe("Whether the deck should be starred"),
929
+ }),
930
+ }),
931
+ ]),
932
+ )
933
+ .min(1)
934
+ .describe(
935
+ "Use patch-slide for content or slide fields, add-slide to append a slide, delete-slide to remove a slide, reorder-slides to set slide order, and patch-deck-fields for top-level fields such as title, starred, design-system tweaks, or clearing a design-system link with designSystemId: null. Use apply-design-system to link a design system. Call view-screen for the current tweak map and allowed choices before changing tweaks. For a small change to existing slide HTML (a color, a word, one declaration), send patch-slide edits instead of fields.content; edits apply to the current HTML and need no baseContentHash. For every patch-slide that replaces fields.content, read that target with get-deck compact=false and include its contentHash as baseContentHash. For a deck-wide source restyle, include one patch-slide operation with content and its matching hash for every existing source slide.",
936
+ ),
937
+ });
938
+
939
+ const CreativeContextReuseLabelSchema = z.object({
940
+ itemId: z.string().min(1).optional(),
941
+ itemVersionId: z.string().min(1).optional(),
942
+ kind: z.string().min(1),
943
+ label: z.string().min(1),
944
+ dataRole: z.literal("untrusted-reference").default("untrusted-reference"),
945
+ elementId: z.string().min(1).optional(),
946
+ influence: z
947
+ .enum(["reused", "adapted", "reference-conditioned", "generated"])
948
+ .optional(),
949
+ });
950
+
951
+ function storedCreativeContext(value: unknown): {
952
+ contextMode: "off" | "auto" | "pinned";
953
+ contextPackId: string | null;
954
+ reuseLabels: CreativeContextReuseLabel[];
955
+ } | null {
956
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
957
+ const record = value as Record<string, unknown>;
958
+ if (
959
+ record.contextMode !== "off" &&
960
+ record.contextMode !== "auto" &&
961
+ record.contextMode !== "pinned"
962
+ ) {
963
+ return null;
964
+ }
965
+ return {
966
+ contextMode: record.contextMode,
967
+ contextPackId:
968
+ typeof record.contextPackId === "string" ? record.contextPackId : null,
969
+ reuseLabels: Array.isArray(record.reuseLabels)
970
+ ? (record.reuseLabels as CreativeContextReuseLabel[])
971
+ : [],
972
+ };
973
+ }
974
+
975
+ // ---------------------------------------------------------------------------
976
+ // Core merge logic (exported for unit tests)
977
+ // ---------------------------------------------------------------------------
978
+
979
+ // Transition fields are stored canonical so every reader sees a value from
980
+ // SLIDE_TRANSITION_TYPES. null and undefined pass through: null is an explicit
981
+ // clear that the caller applies by deleting the key.
982
+ function canonicalSlideFieldValue(field: string, value: unknown): unknown {
983
+ if (value === null || value === undefined) return value;
984
+ if (field === "transition") return normalizeSlideTransition(value);
985
+ if (field === "transitionSpeed") return clampTransitionSpeed(value as number);
986
+ return value;
987
+ }
988
+
989
+ // get-deck reads an absent transition or speed as null, so a snapshot of null
990
+ // and a missing key name the same slide state. Unrecognized values are not
991
+ // coerced: they stay distinct and fail the comparison.
992
+ function readerSlideFieldValue(
993
+ field: "transition" | "transitionSpeed",
994
+ value: unknown,
995
+ ): unknown {
996
+ if (field === "transition") {
997
+ if (value === null || value === undefined) return "none";
998
+ return (SLIDE_TRANSITION_INPUTS as readonly unknown[]).includes(value)
999
+ ? normalizeSlideTransition(value)
1000
+ : value;
1001
+ }
1002
+ return value === null || value === undefined
1003
+ ? SLIDE_TRANSITION_SPEED.default
1004
+ : value;
1005
+ }
1006
+
1007
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1008
+ export function applyOperation(
1009
+ deck: any,
1010
+ op: Operation,
1011
+ options?: {
1012
+ clearLayoutWarningDismissal?: boolean;
1013
+ sourceContentHashes?: ReadonlyMap<string, string>;
1014
+ compiledNewSources?: ReadonlyMap<
1015
+ string,
1016
+ { sourceTsx: string; sourceHash: string; content: string }
1017
+ >;
1018
+ validatedNewSlideIds?: Set<string>;
1019
+ },
1020
+ ): boolean {
1021
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1022
+ const slides: any[] = Array.isArray(deck.slides) ? deck.slides : [];
1023
+
1024
+ switch (op.op) {
1025
+ case "patch-slide": {
1026
+ const idx = slides.findIndex((s: { id: string }) => s.id === op.slideId);
1027
+ if (idx === -1) return false;
1028
+ const slide = slides[idx];
1029
+ const previousSlide = { ...slide };
1030
+ const fields = op.fields;
1031
+ if (op.baseContentHash !== undefined) {
1032
+ const sourceContentHash =
1033
+ options?.sourceContentHashes?.get(op.slideId) ??
1034
+ hashSlideContent(String(slide.content ?? ""));
1035
+ if (sourceContentHash !== op.baseContentHash) {
1036
+ fail(
1037
+ "Slide content changed since it was read. Call get-deck with this slideId again and rebase the patch.",
1038
+ {
1039
+ errorCode: "slide_content_stale",
1040
+ statusCode: 409,
1041
+ details: { slideId: op.slideId },
1042
+ },
1043
+ );
1044
+ }
1045
+ }
1046
+ const alreadyDesiredFields = new Set<string>();
1047
+ for (const field of Object.keys(fields)) {
1048
+ if (
1049
+ field === "content" ||
1050
+ fields[field as keyof typeof fields] === undefined
1051
+ ) {
1052
+ continue;
1053
+ }
1054
+ const baseline = op.baseFields?.[field];
1055
+ if (!baseline) continue;
1056
+ const currentValue = slide[field];
1057
+ const desiredValue = canonicalSlideFieldValue(
1058
+ field,
1059
+ fields[field as keyof typeof fields],
1060
+ );
1061
+ const alreadyDesired =
1062
+ desiredValue === null
1063
+ ? currentValue === undefined
1064
+ : currentValue !== undefined &&
1065
+ stableStringify(currentValue) === stableStringify(desiredValue);
1066
+ if (alreadyDesired) {
1067
+ alreadyDesiredFields.add(field);
1068
+ continue;
1069
+ }
1070
+ const matchesBaseline =
1071
+ field === "notes"
1072
+ ? stableStringify(currentValue ?? "") ===
1073
+ stableStringify(baseline.present ? (baseline.value ?? "") : "")
1074
+ : field === "transition" || field === "transitionSpeed"
1075
+ ? stableStringify(readerSlideFieldValue(field, currentValue)) ===
1076
+ stableStringify(
1077
+ readerSlideFieldValue(
1078
+ field,
1079
+ baseline.present ? baseline.value : undefined,
1080
+ ),
1081
+ )
1082
+ : baseline.present
1083
+ ? currentValue !== undefined &&
1084
+ stableStringify(currentValue) ===
1085
+ stableStringify(baseline.value)
1086
+ : currentValue === undefined;
1087
+ if (!matchesBaseline) {
1088
+ fail(
1089
+ `Slide field ${field} changed since it was read. Re-read the slide and reconcile this field before retrying.`,
1090
+ {
1091
+ errorCode: "slide_field_stale",
1092
+ statusCode: 409,
1093
+ details: { slideId: op.slideId, field },
1094
+ },
1095
+ );
1096
+ }
1097
+ }
1098
+ const previousFitFields = {
1099
+ content: slide.content,
1100
+ layout: slide.layout,
1101
+ excalidrawData: slide.excalidrawData,
1102
+ };
1103
+ if (fields.content !== undefined) {
1104
+ const previousContent =
1105
+ typeof slide.content === "string" ? slide.content : undefined;
1106
+ const nextContent = editResolvedOperations.has(op)
1107
+ ? fields.content
1108
+ : normalizeSlidePaddingForWrite(previousContent, fields.content);
1109
+ assertNoRenderArtifacts(previousContent ?? "", nextContent, op.slideId);
1110
+ slide.content = nextContent;
1111
+ }
1112
+ if (fields.notes !== undefined && !alreadyDesiredFields.has("notes")) {
1113
+ slide.notes = fields.notes;
1114
+ }
1115
+ for (const key of [
1116
+ "background",
1117
+ "layout",
1118
+ "layoutWarningDismissed",
1119
+ "imageUrl",
1120
+ "imageLoading",
1121
+ "imagePrompt",
1122
+ "excalidrawData",
1123
+ "transition",
1124
+ "transitionSpeed",
1125
+ "animations",
1126
+ "splitByParagraph",
1127
+ "skipped",
1128
+ ] as const) {
1129
+ if (alreadyDesiredFields.has(key)) continue;
1130
+ const value = canonicalSlideFieldValue(key, fields[key]);
1131
+ if (value === null) delete slide[key];
1132
+ else if (value !== undefined) slide[key] = value;
1133
+ }
1134
+ assertSlideInlineImagesPersistable(previousSlide, slide, op.slideId);
1135
+ const layoutChanged = slideFitRenderFieldsChanged(
1136
+ previousFitFields,
1137
+ slide,
1138
+ );
1139
+ if (layoutChanged) {
1140
+ slide.layoutFitRevision = createLayoutFitRevision();
1141
+ if (
1142
+ options?.clearLayoutWarningDismissal &&
1143
+ fields.layoutWarningDismissed === undefined
1144
+ ) {
1145
+ delete slide.layoutWarningDismissed;
1146
+ }
1147
+ }
1148
+ return false;
1149
+ }
1150
+
1151
+ case "delete-slide": {
1152
+ const idx = slides.findIndex((s: { id: string }) => s.id === op.slideId);
1153
+ const removed = idx !== -1;
1154
+ if (removed) slides.splice(idx, 1);
1155
+ const addedFallback = slides.length === 0 && !op.allowEmpty;
1156
+ if (addedFallback) {
1157
+ const fallbackId = `slide-${Date.now()}-fallback`;
1158
+ if (deck.appId === "slides-2") {
1159
+ slides.push({
1160
+ id: fallbackId,
1161
+ ...renderBlankSlideSource(),
1162
+ notes: "",
1163
+ layout: "blank",
1164
+ });
1165
+ options?.validatedNewSlideIds?.add(fallbackId);
1166
+ } else {
1167
+ slides.push({
1168
+ id: fallbackId,
1169
+ content: `<div class="fmd-slide" style="box-sizing: border-box; width: 100%; height: 100%; padding: 80px 110px; display: flex; flex-direction: column; justify-content: center; align-items: center; text-align: center;"><div style="font-size: 28px; font-weight: 600; color: hsl(var(--muted-foreground) / 0.4);">Double-click to edit</div></div>`,
1170
+ notes: "",
1171
+ layout: "blank",
1172
+ });
1173
+ }
1174
+ }
1175
+ if (!removed && !addedFallback) return false;
1176
+ deck.slides = slides;
1177
+ delete deck.sourceImport;
1178
+ return true;
1179
+ }
1180
+
1181
+ case "reorder-slides": {
1182
+ const { orderedIds } = op;
1183
+ const duplicateId = firstDuplicate(orderedIds);
1184
+ if (duplicateId !== undefined) {
1185
+ fail(`Cannot reorder slides with duplicate ID ${duplicateId}`, {
1186
+ errorCode: "duplicate_slide_id",
1187
+ statusCode: 400,
1188
+ });
1189
+ }
1190
+ const byId = new Map(slides.map((s: { id: string }) => [s.id, s]));
1191
+ // Build the new order from the client's desired order, keeping only
1192
+ // slides that actually exist in the server copy.
1193
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1194
+ const reordered: any[] = orderedIds
1195
+ .map((id) => byId.get(id))
1196
+ .filter(Boolean);
1197
+ const orderedSet = new Set(orderedIds);
1198
+ for (const s of slides) {
1199
+ if (!orderedSet.has(s.id)) reordered.push(s);
1200
+ }
1201
+ if (
1202
+ reordered.length === slides.length &&
1203
+ reordered.every((slide, index) => slide?.id === slides[index]?.id)
1204
+ ) {
1205
+ return false;
1206
+ }
1207
+ deck.slides = reordered;
1208
+ delete deck.sourceImport;
1209
+ return true;
1210
+ }
1211
+
1212
+ case "add-slide": {
1213
+ const { slideId, afterSlideId, duplicateSlideId, fields } = op;
1214
+ if (slides.some((s: { id: string }) => s.id === slideId)) return false;
1215
+ if (
1216
+ ["sourceHash", "revision"].some((key) => Object.hasOwn(fields, key))
1217
+ ) {
1218
+ fail(
1219
+ "Slide source metadata can only be written by create-deck or add-slide.",
1220
+ {
1221
+ errorCode: "slide_source_action_required",
1222
+ statusCode: 409,
1223
+ },
1224
+ );
1225
+ }
1226
+ const duplicate = duplicateSlideId
1227
+ ? slides.find((slide: { id?: string }) => slide.id === duplicateSlideId)
1228
+ : undefined;
1229
+ if (duplicateSlideId && !duplicate) {
1230
+ fail("Source slide to duplicate was not found in this deck.", {
1231
+ errorCode: "slide_not_found",
1232
+ statusCode: 404,
1233
+ });
1234
+ }
1235
+ if (
1236
+ duplicate &&
1237
+ typeof duplicate.sourceTsx === "string" &&
1238
+ (fields.sourceTsx !== undefined ||
1239
+ (fields.content !== undefined &&
1240
+ fields.content !== duplicate.content))
1241
+ ) {
1242
+ fail(
1243
+ "Duplicate source slides must keep their derived preview; pass duplicateSlideId without changing content.",
1244
+ {
1245
+ errorCode: "slide_source_action_required",
1246
+ statusCode: 409,
1247
+ },
1248
+ );
1249
+ }
1250
+ if (typeof fields.content === "string") {
1251
+ assertNoRenderArtifactsInNewSlide(
1252
+ fields.content,
1253
+ slideId,
1254
+ slides.map((s: { content?: unknown }) => String(s.content ?? "")),
1255
+ );
1256
+ }
1257
+ const compiledSource = options?.compiledNewSources?.get(slideId);
1258
+ if (fields.sourceTsx !== undefined && !compiledSource) {
1259
+ fail("New React slide source must be validated before storage.", {
1260
+ errorCode: "slide_source_action_required",
1261
+ statusCode: 409,
1262
+ });
1263
+ }
1264
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1265
+ // Copy every provided field: a duplicated or undo-restored slide has to
1266
+ // keep its transition, animations, and image data, not just its text.
1267
+ const newSlide: any = {
1268
+ ...(duplicate ?? {}),
1269
+ ...fields,
1270
+ id: slideId,
1271
+ ...(compiledSource
1272
+ ? {
1273
+ sourceTsx: compiledSource.sourceTsx,
1274
+ revision: 1,
1275
+ sourceHash: compiledSource.sourceHash,
1276
+ }
1277
+ : {}),
1278
+ content: compiledSource
1279
+ ? compiledSource.content
1280
+ : typeof fields.content === "string"
1281
+ ? normalizeSlidePadding(fields.content)
1282
+ : duplicate && typeof duplicate.content === "string"
1283
+ ? duplicate.content
1284
+ : "",
1285
+ layoutFitRevision: createLayoutFitRevision(),
1286
+ notes: fields.notes ?? "",
1287
+ layout: fields.layout ?? "content",
1288
+ };
1289
+ delete newSlide.imageLoading;
1290
+ for (const key of [
1291
+ "transition",
1292
+ "transitionSpeed",
1293
+ "animations",
1294
+ ] as const) {
1295
+ const value = canonicalSlideFieldValue(key, newSlide[key]);
1296
+ if (value === null || value === undefined) delete newSlide[key];
1297
+ else newSlide[key] = value;
1298
+ }
1299
+ assertSlideInlineImagesPersistable({}, newSlide, slideId);
1300
+ const insertAfterIdx = afterSlideId
1301
+ ? slides.findIndex((s: { id: string }) => s.id === afterSlideId)
1302
+ : -1;
1303
+ if (insertAfterIdx !== -1) {
1304
+ slides.splice(insertAfterIdx + 1, 0, newSlide);
1305
+ } else {
1306
+ slides.push(newSlide);
1307
+ }
1308
+ if (compiledSource) options?.validatedNewSlideIds?.add(slideId);
1309
+ deck.slides = slides;
1310
+ delete deck.sourceImport;
1311
+ return true;
1312
+ }
1313
+
1314
+ case "patch-deck-fields": {
1315
+ const { fields } = op;
1316
+ if (fields.title !== undefined) {
1317
+ const repairedTitle = repairGeneratedDeckTitle(
1318
+ fields.title,
1319
+ slides[0]?.content,
1320
+ deck.title,
1321
+ );
1322
+ if (repairedTitle) {
1323
+ deck.title = repairedTitle;
1324
+ } else {
1325
+ assertHumanReadableDeckTitle(fields.title);
1326
+ deck.title = fields.title;
1327
+ }
1328
+ }
1329
+ if ("designSystemId" in fields)
1330
+ deck.designSystemId = fields.designSystemId;
1331
+ if (fields.tweaks === null) delete deck.tweaks;
1332
+ else if (fields.tweaks !== undefined) deck.tweaks = fields.tweaks;
1333
+ if (fields.aspectRatio === null) delete deck.aspectRatio;
1334
+ else if (fields.aspectRatio !== undefined)
1335
+ deck.aspectRatio = fields.aspectRatio;
1336
+ if (fields.shareToken !== undefined) deck.shareToken = fields.shareToken;
1337
+ if (fields.visibility !== undefined) deck.visibility = fields.visibility;
1338
+ if (fields.starred === null) delete deck.starred;
1339
+ else if (fields.starred !== undefined) deck.starred = fields.starred;
1340
+ if (fields.generationContext !== undefined)
1341
+ deck.generationContext = fields.generationContext;
1342
+ return false;
1343
+ }
1344
+ }
1345
+ }
1346
+
1347
+ export function slideSignatures(deck: any): Map<string, string> {
1348
+ const signatures = new Map<string, string>();
1349
+ for (const slide of Array.isArray(deck?.slides) ? deck.slides : []) {
1350
+ if (typeof slide?.id === "string") {
1351
+ const {
1352
+ layoutFitRevision: _layoutFitRevision,
1353
+ layoutWarningDismissed: _layoutWarningDismissed,
1354
+ ...material
1355
+ } = slide;
1356
+ signatures.set(slide.id, deckVersionContentSignature(material));
1357
+ }
1358
+ }
1359
+ return signatures;
1360
+ }
1361
+
1362
+ export function slideContents(deck: any): Map<string, string> {
1363
+ const contents = new Map<string, string>();
1364
+ for (const slide of Array.isArray(deck?.slides) ? deck.slides : []) {
1365
+ if (typeof slide?.id === "string") {
1366
+ contents.set(
1367
+ slide.id,
1368
+ typeof slide.content === "string" ? slide.content : "",
1369
+ );
1370
+ }
1371
+ }
1372
+ return contents;
1373
+ }
1374
+
1375
+ // The stored content and reveals of every slide, read before any operation runs.
1376
+ // Reveal objects are replaced, never mutated, so a shallow copy is a true baseline.
1377
+ export function slideAnimationBaselines(
1378
+ deck: any,
1379
+ ): Map<string, SlideAnimationBaseline> {
1380
+ const baselines = new Map<string, SlideAnimationBaseline>();
1381
+ for (const slide of Array.isArray(deck?.slides) ? deck.slides : []) {
1382
+ if (typeof slide?.id !== "string") continue;
1383
+ baselines.set(slide.id, {
1384
+ content: typeof slide.content === "string" ? slide.content : "",
1385
+ animations: Array.isArray(slide.animations) ? [...slide.animations] : [],
1386
+ });
1387
+ }
1388
+ return baselines;
1389
+ }
1390
+
1391
+ export function assertPatchedSlideAnimationsResolve(
1392
+ deck: any,
1393
+ operations: readonly Operation[],
1394
+ options?: {
1395
+ requireElementPaths?: boolean;
1396
+ contentChangedSlideIds?: ReadonlySet<string>;
1397
+ baselines?: ReadonlyMap<string, SlideAnimationBaseline>;
1398
+ },
1399
+ ): void {
1400
+ const slideIdsToValidate = new Set(
1401
+ operations.flatMap((operation) => {
1402
+ if (operation.op !== "patch-slide" && operation.op !== "add-slide") {
1403
+ return [];
1404
+ }
1405
+ const contentChanged =
1406
+ operation.fields.content !== undefined &&
1407
+ (options?.contentChangedSlideIds?.has(operation.slideId) ?? true);
1408
+ return operation.fields.animations !== undefined || contentChanged
1409
+ ? [operation.slideId]
1410
+ : [];
1411
+ }),
1412
+ );
1413
+ if (slideIdsToValidate.size === 0) return;
1414
+
1415
+ // Only a reveal list the caller sent must carry element paths. Reveals that a
1416
+ // content patch keeps are checked the same way update-slide checks them.
1417
+ const explicitAnimationSlideIds = new Set(
1418
+ operations.flatMap((operation) =>
1419
+ (operation.op === "patch-slide" || operation.op === "add-slide") &&
1420
+ operation.fields.animations !== undefined
1421
+ ? [operation.slideId]
1422
+ : [],
1423
+ ),
1424
+ );
1425
+
1426
+ const slides: any[] = Array.isArray(deck.slides) ? deck.slides : [];
1427
+ for (const slideId of slideIdsToValidate) {
1428
+ const slide = slides.find((candidate) => candidate?.id === slideId);
1429
+ if (!slide || !Array.isArray(slide.animations) || !slide.animations.length)
1430
+ continue;
1431
+
1432
+ assertSlideAnimationsResolve({
1433
+ slideId,
1434
+ content: typeof slide.content === "string" ? slide.content : "",
1435
+ animations: slide.animations,
1436
+ requireElementPaths:
1437
+ options?.requireElementPaths === true &&
1438
+ explicitAnimationSlideIds.has(slideId),
1439
+ baseline: options?.baselines?.get(slideId),
1440
+ });
1441
+ }
1442
+ }
1443
+
1444
+ export function resolveDeckColumnUpdates(
1445
+ current: { title: string; designSystemId: string | null },
1446
+ operations: Operation[],
1447
+ resolvedTitle?: string,
1448
+ ): { title: string; designSystemId: string | null } {
1449
+ const fieldOps = operations
1450
+ .filter(
1451
+ (op): op is z.infer<typeof PatchDeckFieldsOp> =>
1452
+ op.op === "patch-deck-fields",
1453
+ )
1454
+ .reverse();
1455
+ const titleOp = fieldOps.find((op) => typeof op.fields.title === "string");
1456
+ const dsOp = fieldOps.find((op) => "designSystemId" in op.fields);
1457
+ return {
1458
+ title: resolvedTitle ?? titleOp?.fields.title ?? current.title,
1459
+ designSystemId: dsOp
1460
+ ? (dsOp.fields.designSystemId ?? null)
1461
+ : current.designSystemId,
1462
+ };
1463
+ }
1464
+
1465
+ export function isAgentPatchCaller(caller: string | undefined): boolean {
1466
+ return (
1467
+ caller === "tool" ||
1468
+ caller === "mcp" ||
1469
+ caller === "a2a" ||
1470
+ caller === "webmcp"
1471
+ );
1472
+ }
1473
+
1474
+ // Replay is safe only where an op sets end state by id or is CAS-guarded. A
1475
+ // content-replacing patch-slide qualifies only with its own baseContentHash:
1476
+ // the agent path sends no revision, and edits resolve against stored content,
1477
+ // so an unhashed occurrence-based edit would re-apply to another match on replay.
1478
+ function patchDeckRetrySafe(input: {
1479
+ operations?: readonly unknown[];
1480
+ }): boolean {
1481
+ if (!Array.isArray(input.operations)) return false;
1482
+ return input.operations.every((raw) => {
1483
+ const operation = raw as {
1484
+ op?: unknown;
1485
+ slideId?: unknown;
1486
+ edits?: unknown;
1487
+ baseContentHash?: unknown;
1488
+ fields?: { content?: unknown };
1489
+ };
1490
+ switch (operation.op) {
1491
+ case "patch-slide":
1492
+ return (
1493
+ (operation.edits === undefined &&
1494
+ operation.fields?.content === undefined) ||
1495
+ typeof operation.baseContentHash === "string"
1496
+ );
1497
+ // applyOperation skips an add-slide whose slideId already exists.
1498
+ case "add-slide":
1499
+ return typeof operation.slideId === "string";
1500
+ case "delete-slide":
1501
+ case "reorder-slides":
1502
+ case "patch-deck-fields":
1503
+ return true;
1504
+ default:
1505
+ return false;
1506
+ }
1507
+ });
1508
+ }
1509
+
1510
+ export default defineAction({
1511
+ title: "Patch Slides deck",
1512
+ description:
1513
+ "Granular deck patch used by the browser editor for concurrent-safe writes. Every patch-slide operation that replaces fields.content must include that slide's exact contentHash as baseContentHash; read targets with get-deck compact=false first. Stale revision rebases support patch-slide operations with content hashes and, for each replaced non-content field, a baseFields snapshot; this also allows a combined content-and-metadata patch to rebase safely. Rebase only if each current field still matches its snapshot or the requested value. Same-field divergence returns slide_field_stale. Stale delete-slide, reorder-slides, and patch-deck-fields operations return deck_revision_conflict; re-read and reconcile the deck before retrying those operations. Call get-design-system once for the full linked context. For a short, completed deck, pass all slides to create-deck in one call; use sequential add-slide calls only for long or live in-app generation. Reserve patch-deck for existing-slide edits, deck fields, ordering, or intentional source-preserving batches. Never issue parallel writes to the same deck. " +
1514
+ "Each operation touches only the target slide or field — concurrent writers " +
1515
+ "on different slides never overwrite each other's work. For a deck-wide " +
1516
+ "source restyle, set requireAllSourceSlides=true and send one patch-slide " +
1517
+ "operation with content for every imported slide in one call; the action " +
1518
+ "rejects partial coverage. For animations, inspect the final slide HTML, " +
1519
+ "then patch content and the complete ordered animations list together. " +
1520
+ "A content patch that omits animations keeps the current reveals; one its " +
1521
+ "change strands or moves onto a different element fails with a slide_animation_* error (400) and writes nothing. " +
1522
+ "Send animations: [] to clear every reveal. " +
1523
+ "Use delete-slide to remove a slide and reorder-slides to set the order; " +
1524
+ "validate every 0-based elementPath and do not invent one-based indexes. " +
1525
+ "Then call get-deck once with slideIds and compact=false to verify the " +
1526
+ "persisted full source, notes, IDs, and animation metadata before reporting " +
1527
+ "success. Only the slide " +
1528
+ "IDs in updatedSlideIds actually changed: a slide echoed back in " +
1529
+ "unchangedSlideIds came out identical to the stored slide, was not " +
1530
+ "written, and must never be described as edited. A batch in which every " +
1531
+ "patched slide is unchanged " +
1532
+ "is rejected, so re-read those slides and send content that differs " +
1533
+ "instead of retrying the same HTML. Content writes " +
1534
+ "return immediately with contentHash plus layoutFitRevision-keyed layoutFit.status=pending; call " +
1535
+ "Do not check fit after each write: finish all slide edits, then call get-layout-overflows once, and once more only after a repair. If measurements are unknown, report the unmeasured slides and do not recheck this turn unless the editor has produced a new measurement. " +
1536
+ "Agents can add, delete, and reorder slides through operations in this action. " +
1537
+ "Structural edits to an imported deck clear its source-import metadata automatically; the legacy rewriteSource flag is not required." +
1538
+ HYGIENE_ACTION_DESCRIPTION,
1539
+ schema: z.object({
1540
+ deckId: z.string().describe("Deck ID"),
1541
+ clientWrite: deckClientWriteSchema.optional(),
1542
+ rewriteSource: z
1543
+ .boolean()
1544
+ .optional()
1545
+ .default(false)
1546
+ .describe(
1547
+ "Legacy compatibility flag. When true on a source-imported deck, clear its source-import metadata; structural edits no longer require this flag.",
1548
+ ),
1549
+ requireAllSourceSlides: z
1550
+ .boolean()
1551
+ .optional()
1552
+ .default(false)
1553
+ .describe(
1554
+ "For a deck-wide source-import restyle, require one content patch for every imported slide before any write is committed.",
1555
+ ),
1556
+ operations: z
1557
+ .array(OperationSchema)
1558
+ .min(1)
1559
+ .describe("Ordered list of granular operations to apply"),
1560
+ creativeContext: z
1561
+ .object({
1562
+ contextPackId: z.string().optional(),
1563
+ contextModeOverride: z.literal("off").optional(),
1564
+ reuseLabels: z
1565
+ .array(CreativeContextReuseLabelSchema)
1566
+ .optional()
1567
+ .default([]),
1568
+ })
1569
+ .optional()
1570
+ .describe(
1571
+ "Optional exact Creative Context provenance for context-backed slide patch operations.",
1572
+ ),
1573
+ }),
1574
+ agentInputSchema: AgentPatchDeckInputSchema,
1575
+ http: { method: "POST" },
1576
+ mcpAnnotations: {
1577
+ readOnlyHint: false,
1578
+ destructiveHint: true,
1579
+ openWorldHint: false,
1580
+ },
1581
+ retrySafe: patchDeckRetrySafe,
1582
+ run: async (
1583
+ {
1584
+ deckId,
1585
+ clientWrite,
1586
+ operations: requestedOperations,
1587
+ requireAllSourceSlides,
1588
+ rewriteSource,
1589
+ creativeContext,
1590
+ },
1591
+ ctx,
1592
+ ) => {
1593
+ assertSlidesWidgetWriteScope(deckId, ctx);
1594
+ await assertAccess("deck", deckId, "editor");
1595
+ const isAgentCaller = isAgentPatchCaller(ctx?.caller);
1596
+
1597
+ const applyPatch = async () => {
1598
+ const db = getDb();
1599
+ const [row] = await db
1600
+ .select()
1601
+ .from(schema.decks)
1602
+ .where(eq(schema.decks.id, deckId))
1603
+ .limit(1);
1604
+
1605
+ if (!row)
1606
+ fail(`Deck ${deckId} not found`, {
1607
+ errorCode: "deck_not_found",
1608
+ statusCode: 404,
1609
+ });
1610
+
1611
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1612
+ const deck: any = JSON.parse(row.data);
1613
+ assertSlides2DeckData(deck);
1614
+ const tweakDefinitions = getPreset(
1615
+ typeof deck.designSystemId === "string"
1616
+ ? deck.designSystemId
1617
+ : "default",
1618
+ ).tweaks;
1619
+ if (isAgentCaller) {
1620
+ const invalidTweakIds = requestedOperations.flatMap((operation) =>
1621
+ operation.op === "patch-deck-fields" &&
1622
+ operation.fields.tweaks !== undefined
1623
+ ? invalidTweakSelectionIds(
1624
+ operation.fields.tweaks,
1625
+ tweakDefinitions,
1626
+ )
1627
+ : [],
1628
+ );
1629
+ if (invalidTweakIds.length > 0) {
1630
+ fail(
1631
+ `Unsupported design-system tweak selection(s): ${[...new Set(invalidTweakIds)].join(", ")}. Call view-screen to see this deck's available controls and choices.`,
1632
+ {
1633
+ errorCode: "invalid_design_system_tweak",
1634
+ statusCode: 400,
1635
+ },
1636
+ );
1637
+ }
1638
+ }
1639
+ if (
1640
+ !isMcpWidgetPatchAllowed(ctx?.caller, requestedOperations, {
1641
+ rewriteSource,
1642
+ hasCreativeContext: creativeContext !== undefined,
1643
+ requireAllSourceSlides,
1644
+ tweakDefinitions,
1645
+ })
1646
+ ) {
1647
+ fail(
1648
+ "The Slides widget can edit slide content, structure, title, and available design-system tweaks, not deck access or linked resources.",
1649
+ {
1650
+ errorCode: "mcp_widget_write_outside_editor_scope",
1651
+ statusCode: 403,
1652
+ },
1653
+ );
1654
+ }
1655
+ if (
1656
+ ctx?.caller === "mcp-widget-write" &&
1657
+ clientWrite?.expectedUpdatedAt === undefined
1658
+ ) {
1659
+ fail(
1660
+ "The Slides widget needs the current deck revision before saving.",
1661
+ {
1662
+ errorCode: "mcp_widget_write_revision_required",
1663
+ statusCode: 409,
1664
+ },
1665
+ );
1666
+ }
1667
+
1668
+ // Resolved against this attempt's row so a stale-revision retry
1669
+ // re-applies the edits to the fresh slide content.
1670
+ const operations = await resolvePatchSlideEdits(
1671
+ requestedOperations,
1672
+ Array.isArray(deck.slides) ? deck.slides : [],
1673
+ );
1674
+ const compiledNewSources = new Map<
1675
+ string,
1676
+ { sourceTsx: string; sourceHash: string; content: string }
1677
+ >();
1678
+ for (const operation of operations) {
1679
+ if (
1680
+ operation.op !== "add-slide" ||
1681
+ operation.fields.sourceTsx === undefined
1682
+ ) {
1683
+ continue;
1684
+ }
1685
+ if (!row.ownerEmail) {
1686
+ fail("A signed-in deck owner is required to save slide source.", {
1687
+ errorCode: "slide_source_owner_required",
1688
+ statusCode: 401,
1689
+ });
1690
+ }
1691
+ const compiled = await compileSlideSource(operation.fields.sourceTsx, {
1692
+ ownerEmail: row.ownerEmail,
1693
+ orgId: row.orgId,
1694
+ });
1695
+ compiledNewSources.set(operation.slideId, {
1696
+ sourceTsx: operation.fields.sourceTsx,
1697
+ sourceHash: compiled.sourceHash,
1698
+ content: compiled.content,
1699
+ });
1700
+ }
1701
+ const validatedNewSlideIds = new Set(compiledNewSources.keys());
1702
+
1703
+ const writeDisposition = assertDeckClientWriteCurrent(
1704
+ row,
1705
+ deckId,
1706
+ clientWrite,
1707
+ {
1708
+ allowRevisionMismatch: isMergeSafeDeckPatchOperations(operations),
1709
+ },
1710
+ );
1711
+ if (writeDisposition === "already-applied") {
1712
+ return {
1713
+ ok: true,
1714
+ deckId,
1715
+ updatedAt: row.updatedAt,
1716
+ applied: false,
1717
+ updatedSlideIds: [],
1718
+ deletedSlideIds: [],
1719
+ };
1720
+ }
1721
+
1722
+ const previousSlides = Array.isArray(deck.slides)
1723
+ ? deck.slides.map((slide: Record<string, unknown>) => ({ ...slide }))
1724
+ : [];
1725
+ const previousGenerationContext = deck.generationContext;
1726
+ const existingContext = storedCreativeContext(deck.creativeContext);
1727
+ const previousDeckFitFields = {
1728
+ aspectRatio: deck.aspectRatio,
1729
+ designSystemId: deck.designSystemId,
1730
+ };
1731
+
1732
+ const currentSlides = Array.isArray(deck.slides) ? deck.slides : [];
1733
+ if (ctx?.caller === "mcp-widget-write") {
1734
+ const storedSlideIds = currentSlides
1735
+ .map((slide: { id?: unknown }) => slide?.id)
1736
+ .filter((id: unknown): id is string => typeof id === "string");
1737
+ const duplicateSlideId = firstDuplicate(storedSlideIds);
1738
+ if (duplicateSlideId !== undefined) {
1739
+ fail(
1740
+ "The Slides 2 widget cannot edit a deck with duplicate slide IDs. Repair the deck in the Slides 2 editor first.",
1741
+ {
1742
+ errorCode: "duplicate_deck_slide_ids",
1743
+ statusCode: 409,
1744
+ details: { slideId: duplicateSlideId },
1745
+ },
1746
+ );
1747
+ }
1748
+ }
1749
+ const sourceContentHashes = new Map<string, string>(
1750
+ currentSlides.map(
1751
+ (slide: { id: string; content?: unknown }) =>
1752
+ [slide.id, hashSlideContent(String(slide.content ?? ""))] as const,
1753
+ ),
1754
+ );
1755
+ const existingSlideIds = new Set(
1756
+ currentSlides.map((slide: { id?: unknown }) => slide.id),
1757
+ );
1758
+ const missingSlideIds = operations
1759
+ .filter((operation) => operation.op === "patch-slide")
1760
+ .map((operation) => operation.slideId)
1761
+ .filter((slideId) => !existingSlideIds.has(slideId));
1762
+ if (missingSlideIds.length > 0) {
1763
+ fail(
1764
+ `Cannot patch missing slide(s): ${[...new Set(missingSlideIds)].join(", ")}`,
1765
+ { errorCode: "slide_not_found", statusCode: 404 },
1766
+ );
1767
+ }
1768
+
1769
+ const contentPatchOperations = operations.filter(
1770
+ (operation): operation is Extract<Operation, { op: "patch-slide" }> =>
1771
+ operation.op === "patch-slide" &&
1772
+ operation.fields.content !== undefined,
1773
+ );
1774
+ if (
1775
+ (isAgentCaller || clientWrite !== undefined) &&
1776
+ contentPatchOperations.some(
1777
+ (operation) => operation.baseContentHash === undefined,
1778
+ )
1779
+ ) {
1780
+ fail(
1781
+ "Every slide content replacement requires its source contentHash. Read each target with get-deck, then retry the patch.",
1782
+ {
1783
+ errorCode: "slide_content_hash_required",
1784
+ statusCode: 400,
1785
+ },
1786
+ );
1787
+ }
1788
+
1789
+ const sourceImport = sourceImportForDeck(deck.sourceImport);
1790
+ const sourceRewriteRequested =
1791
+ isAgentCaller && rewriteSource && sourceImport !== null;
1792
+ if (isAgentCaller && rewriteSource && sourceImport === null) {
1793
+ fail(
1794
+ "rewriteSource=true only applies to a source-preserving deck; omit it for a regular deck",
1795
+ { errorCode: "source_rewrite_unsupported", statusCode: 400 },
1796
+ );
1797
+ }
1798
+
1799
+ const targetSlideCount = persistedTargetSlideCount(deck);
1800
+ // Counting parses slide HTML and this runs on every editor save, so only
1801
+ // an agent batch that appends against a persisted target pays for it.
1802
+ if (
1803
+ isAgentCaller &&
1804
+ targetSlideCount !== null &&
1805
+ operations.some((operation) => operation.op === "add-slide")
1806
+ ) {
1807
+ const projected = projectedRealSlideCount(currentSlides, operations);
1808
+ if (projected.added && projected.count > targetSlideCount) {
1809
+ const realSlideCount = projected.initial;
1810
+ // The patch's own fills and deletes move the count before it adds.
1811
+ const settledCount = projectedRealSlideCount(
1812
+ currentSlides,
1813
+ operations.filter((operation) => operation.op !== "add-slide"),
1814
+ ).count;
1815
+ const headroom = targetSlideCount - settledCount;
1816
+ fail(
1817
+ realSlideCount < targetSlideCount
1818
+ ? `Not applied: this deck has ${realSlideCount} real slides of its persisted target of ${targetSlideCount}, and this patch would make it ${projected.count}. The deck is not complete: resend the patch ${headroom > 0 ? `adding at most ${headroom} more real slides in this patch (target ${targetSlideCount})` : `without its add-slide operations, since its other operations already bring the deck to ${settledCount} of ${targetSlideCount} real slides`}.`
1819
+ : `Not applied: this deck has ${realSlideCount} real slides and this patch would make it ${projected.count}, exceeding its persisted target of ${targetSlideCount}. If the deck is complete, stop adding slides and summarize what was built. Only if the user explicitly asked for more slides than ${targetSlideCount}, call add-slide with targetSlideCountOverride set to the new total of real slides, then continue.`,
1820
+ {
1821
+ errorCode: "target_slide_count_reached",
1822
+ statusCode: 409,
1823
+ details: {
1824
+ deckId,
1825
+ currentSlideCount: currentSlides.length,
1826
+ realSlideCount,
1827
+ projectedSlideCount: projected.count,
1828
+ targetSlideCount,
1829
+ },
1830
+ },
1831
+ );
1832
+ }
1833
+ }
1834
+
1835
+ const layoutFitSlideIds = new Set<string>();
1836
+ const deletedSlideIds = new Set<string>();
1837
+ const signaturesBeforeOperations = slideSignatures(deck);
1838
+ const contentsBeforeOperations = slideContents(deck);
1839
+ const animationBaselinesBeforeOperations = slideAnimationBaselines(deck);
1840
+ const fitFieldsBeforeOperations = new Map<
1841
+ string,
1842
+ { content: unknown; layout: unknown; excalidrawData: unknown }
1843
+ >(
1844
+ (Array.isArray(deck.slides) ? deck.slides : [])
1845
+ .filter((slide: { id?: unknown }) => typeof slide.id === "string")
1846
+ .map(
1847
+ (slide: Record<string, unknown>) =>
1848
+ [
1849
+ slide.id as string,
1850
+ {
1851
+ content: slide.content,
1852
+ layout: slide.layout,
1853
+ excalidrawData: slide.excalidrawData,
1854
+ },
1855
+ ] as const,
1856
+ ),
1857
+ );
1858
+ const derivedBeforeOperations = new Map<
1859
+ string,
1860
+ { layoutFitRevision: unknown; layoutWarningDismissed: unknown }
1861
+ >(
1862
+ (Array.isArray(deck.slides) ? deck.slides : [])
1863
+ .filter((slide: { id?: unknown }) => typeof slide.id === "string")
1864
+ .map(
1865
+ (slide: Record<string, unknown>) =>
1866
+ [
1867
+ slide.id as string,
1868
+ {
1869
+ layoutFitRevision: slide.layoutFitRevision,
1870
+ layoutWarningDismissed: slide.layoutWarningDismissed,
1871
+ },
1872
+ ] as const,
1873
+ ),
1874
+ );
1875
+ let structuralOperationApplied = false;
1876
+ for (const op of operations) {
1877
+ const existedBeforeDelete =
1878
+ op.op === "delete-slide" &&
1879
+ (deck.slides as Array<{ id?: string }>).some(
1880
+ (slide) => slide.id === op.slideId,
1881
+ );
1882
+ const operationChangedStructure = applyOperation(deck, op, {
1883
+ clearLayoutWarningDismissal: isAgentCaller,
1884
+ sourceContentHashes,
1885
+ compiledNewSources,
1886
+ validatedNewSlideIds,
1887
+ });
1888
+ structuralOperationApplied ||= operationChangedStructure;
1889
+ if (
1890
+ existedBeforeDelete &&
1891
+ !(deck.slides as Array<{ id?: string }>).some(
1892
+ (slide) => slide.id === op.slideId,
1893
+ )
1894
+ ) {
1895
+ deletedSlideIds.add(op.slideId);
1896
+ }
1897
+ }
1898
+ const sourceImportCleared =
1899
+ sourceImport !== null &&
1900
+ (sourceRewriteRequested || structuralOperationApplied);
1901
+ if (isAgentCaller) {
1902
+ assertSourceImportSlidesCovered(
1903
+ sourceImport,
1904
+ operations,
1905
+ sourceImportCleared ? false : requireAllSourceSlides,
1906
+ );
1907
+ for (const op of operations) {
1908
+ if (
1909
+ sourceImportCleared ||
1910
+ op.op !== "patch-slide" ||
1911
+ (op.fields.content === undefined && op.fields.notes === undefined)
1912
+ ) {
1913
+ continue;
1914
+ }
1915
+ assertSourceSlidePreserved({
1916
+ metadata: sourceImport,
1917
+ slideId: op.slideId,
1918
+ nextContent:
1919
+ op.fields.content === undefined
1920
+ ? undefined
1921
+ : editResolvedOperations.has(op)
1922
+ ? op.fields.content
1923
+ : normalizeSlidePaddingForWrite(
1924
+ contentsBeforeOperations.get(op.slideId),
1925
+ op.fields.content,
1926
+ ),
1927
+ nextNotes: op.fields.notes,
1928
+ preserveSource: op.preserveSource,
1929
+ });
1930
+ }
1931
+ }
1932
+ const signaturesAfterOperations = slideSignatures(deck);
1933
+ const contentsAfterOperations = slideContents(deck);
1934
+ const requestedSlideIds = [
1935
+ ...new Set(
1936
+ operations.flatMap((operation) =>
1937
+ operation.op === "patch-slide" || operation.op === "add-slide"
1938
+ ? [operation.slideId]
1939
+ : [],
1940
+ ),
1941
+ ),
1942
+ ].filter((slideId) => signaturesAfterOperations.has(slideId));
1943
+ const changedSlideIds = new Set(
1944
+ requestedSlideIds.filter(
1945
+ (slideId) =>
1946
+ signaturesBeforeOperations.get(slideId) !==
1947
+ signaturesAfterOperations.get(slideId),
1948
+ ),
1949
+ );
1950
+ const contentChangedSlideIds = new Set(
1951
+ requestedSlideIds.filter(
1952
+ (slideId) =>
1953
+ contentsBeforeOperations.get(slideId) !==
1954
+ contentsAfterOperations.get(slideId),
1955
+ ),
1956
+ );
1957
+ for (const slideId of requestedSlideIds) {
1958
+ if (deletedSlideIds.has(slideId)) changedSlideIds.add(slideId);
1959
+ }
1960
+
1961
+ const explicitWarningSlideIds = new Set(
1962
+ operations.flatMap((operation) =>
1963
+ (operation.op === "patch-slide" || operation.op === "add-slide") &&
1964
+ (operation.fields as { layoutWarningDismissed?: unknown })
1965
+ .layoutWarningDismissed !== undefined
1966
+ ? [operation.slideId]
1967
+ : [],
1968
+ ),
1969
+ );
1970
+ for (const slide of Array.isArray(deck.slides) ? deck.slides : []) {
1971
+ if (typeof slide.id !== "string") continue;
1972
+ if (!explicitWarningSlideIds.has(slide.id)) continue;
1973
+ if (
1974
+ slide.layoutWarningDismissed !==
1975
+ derivedBeforeOperations.get(slide.id)?.layoutWarningDismissed
1976
+ ) {
1977
+ changedSlideIds.add(slide.id);
1978
+ }
1979
+ }
1980
+
1981
+ for (const slide of Array.isArray(deck.slides) ? deck.slides : []) {
1982
+ if (typeof slide.id !== "string") continue;
1983
+ const previousFitFields = fitFieldsBeforeOperations.get(slide.id);
1984
+ if (!previousFitFields) {
1985
+ layoutFitSlideIds.add(slide.id);
1986
+ continue;
1987
+ }
1988
+ if (slideFitRenderFieldsChanged(previousFitFields, slide)) {
1989
+ layoutFitSlideIds.add(slide.id);
1990
+ continue;
1991
+ }
1992
+ layoutFitSlideIds.delete(slide.id);
1993
+ const derived = derivedBeforeOperations.get(slide.id);
1994
+ if (!derived) continue;
1995
+ if (derived.layoutFitRevision === undefined) {
1996
+ delete slide.layoutFitRevision;
1997
+ } else {
1998
+ slide.layoutFitRevision = derived.layoutFitRevision;
1999
+ }
2000
+ if (!explicitWarningSlideIds.has(slide.id)) {
2001
+ if (derived.layoutWarningDismissed === undefined) {
2002
+ delete slide.layoutWarningDismissed;
2003
+ } else {
2004
+ slide.layoutWarningDismissed = derived.layoutWarningDismissed;
2005
+ }
2006
+ }
2007
+ }
2008
+
2009
+ const unchangedSlideIds = requestedSlideIds.filter(
2010
+ (slideId) => !changedSlideIds.has(slideId),
2011
+ );
2012
+
2013
+ if (deckFitRenderFieldsChanged(previousDeckFitFields, deck)) {
2014
+ for (const slide of Array.isArray(deck.slides) ? deck.slides : []) {
2015
+ if (typeof slide.id !== "string") continue;
2016
+ if (!layoutFitSlideIds.has(slide.id)) {
2017
+ slide.layoutFitRevision = createLayoutFitRevision();
2018
+ }
2019
+ if (isAgentCaller) delete slide.layoutWarningDismissed;
2020
+ layoutFitSlideIds.add(slide.id);
2021
+ }
2022
+ }
2023
+
2024
+ if (sourceImportCleared) delete deck.sourceImport;
2025
+ deck.appId = "slides-2";
2026
+ assertSourceBackedSlidesUnchanged(row.data, deck, validatedNewSlideIds);
2027
+ assertPatchedSlideAnimationsResolve(deck, operations, {
2028
+ requireElementPaths: isAgentCaller,
2029
+ contentChangedSlideIds,
2030
+ baselines: animationBaselinesBeforeOperations,
2031
+ });
2032
+
2033
+ const { title: sqlTitle, designSystemId: sqlDesignSystemId } =
2034
+ resolveDeckColumnUpdates(
2035
+ { title: row.title, designSystemId: row.designSystemId },
2036
+ operations,
2037
+ operations.some(
2038
+ (operation) =>
2039
+ operation.op === "patch-deck-fields" &&
2040
+ operation.fields.title !== undefined,
2041
+ ) && typeof deck.title === "string"
2042
+ ? deck.title
2043
+ : undefined,
2044
+ );
2045
+
2046
+ let generationRecord:
2047
+ | {
2048
+ contextMode: "off" | "auto" | "pinned";
2049
+ contextPackId: string | null;
2050
+ reuseLabels: CreativeContextReuseLabel[];
2051
+ elementProvenance: Array<{
2052
+ elementId: string;
2053
+ influence:
2054
+ | "reused"
2055
+ | "adapted"
2056
+ | "reference-conditioned"
2057
+ | "generated";
2058
+ itemId?: string;
2059
+ itemVersionId?: string;
2060
+ label?: string;
2061
+ }>;
2062
+ }
2063
+ | undefined;
2064
+ if (creativeContext) {
2065
+ const affectedSlideIds = [
2066
+ ...new Set(
2067
+ operations.flatMap((operation) =>
2068
+ operation.op === "patch-slide" || operation.op === "add-slide"
2069
+ ? [operation.slideId]
2070
+ : [],
2071
+ ),
2072
+ ),
2073
+ ];
2074
+ if (!affectedSlideIds.length) {
2075
+ fail(
2076
+ "Creative Context provenance requires a patch-slide or add-slide operation",
2077
+ {
2078
+ errorCode: "creative_context_provenance_target_required",
2079
+ statusCode: 400,
2080
+ },
2081
+ );
2082
+ }
2083
+ if (
2084
+ existingContext &&
2085
+ creativeContext.contextPackId !== undefined &&
2086
+ creativeContext.contextPackId !== existingContext.contextPackId
2087
+ ) {
2088
+ fail(
2089
+ "The deck patch must use the deck's existing creative-context pack",
2090
+ {
2091
+ errorCode: "creative_context_pack_mismatch",
2092
+ statusCode: 409,
2093
+ },
2094
+ );
2095
+ }
2096
+ const effectivePackId =
2097
+ creativeContext.contextPackId ?? existingContext?.contextPackId;
2098
+ const requestedLabels = affectedSlideIds.flatMap((slideId) => {
2099
+ const labels = creativeContext.reuseLabels.filter(
2100
+ (label) => !label.elementId || label.elementId === slideId,
2101
+ );
2102
+ return labels.length
2103
+ ? labels.map((label) => ({ ...label, elementId: slideId }))
2104
+ : [
2105
+ {
2106
+ kind: "slide",
2107
+ label: "Net-new deck patch",
2108
+ dataRole: "untrusted-reference" as const,
2109
+ elementId: slideId,
2110
+ influence: "generated" as const,
2111
+ },
2112
+ ];
2113
+ });
2114
+ const validated = await validateGenerationCreativeContext({
2115
+ contextPackId: effectivePackId,
2116
+ contextPackSource:
2117
+ creativeContext.contextPackId === undefined
2118
+ ? "inherited"
2119
+ : "explicit",
2120
+ contextModeOverride: creativeContext.contextModeOverride,
2121
+ reuseLabels: requestedLabels,
2122
+ reuseLabelsSource: creativeContext.reuseLabels.length
2123
+ ? "explicit"
2124
+ : "inherited",
2125
+ });
2126
+ const contextMode =
2127
+ validated.contextMode === "off"
2128
+ ? "off"
2129
+ : (existingContext?.contextMode ?? validated.contextMode);
2130
+ const previous =
2131
+ contextMode === "off"
2132
+ ? null
2133
+ : await getGenerationCreativeContext({
2134
+ appId: "slides-2",
2135
+ artifactType: "deck",
2136
+ artifactId: deckId,
2137
+ });
2138
+ const nextElementProvenance = validated.reuseLabels.map((label) => ({
2139
+ elementId: label.elementId!,
2140
+ influence: label.influence ?? ("reference-conditioned" as const),
2141
+ ...(label.itemId ? { itemId: label.itemId } : {}),
2142
+ ...(label.itemVersionId
2143
+ ? { itemVersionId: label.itemVersionId }
2144
+ : {}),
2145
+ label: label.label,
2146
+ }));
2147
+ const mergedReuseLabels = mergeCreativeContextReuseLabels(
2148
+ existingContext?.reuseLabels ?? [],
2149
+ validated.reuseLabels,
2150
+ );
2151
+ generationRecord = {
2152
+ contextMode,
2153
+ contextPackId: validated.contextPackId,
2154
+ reuseLabels:
2155
+ contextMode === "off" ? validated.reuseLabels : mergedReuseLabels,
2156
+ elementProvenance:
2157
+ contextMode === "off"
2158
+ ? nextElementProvenance
2159
+ : replaceCreativeContextElementProvenance(
2160
+ previous?.elementProvenance ?? [],
2161
+ nextElementProvenance,
2162
+ ),
2163
+ };
2164
+ if (!(contextMode === "off" && existingContext)) {
2165
+ deck.creativeContext = {
2166
+ contextMode,
2167
+ contextPackId: validated.contextPackId,
2168
+ reuseLabels: mergedReuseLabels,
2169
+ };
2170
+ }
2171
+ }
2172
+
2173
+ const hasNonSlidePatchWork =
2174
+ operations.some((operation) => operation.op !== "patch-slide") ||
2175
+ sourceImportCleared ||
2176
+ generationRecord !== undefined;
2177
+ if (
2178
+ isAgentCaller &&
2179
+ !hasNonSlidePatchWork &&
2180
+ requestedSlideIds.length > 0 &&
2181
+ changedSlideIds.size === 0
2182
+ ) {
2183
+ fail(
2184
+ `Nothing was written: every patched slide (${requestedSlideIds.join(", ")}) is identical to the deck's current content, so the deck is unchanged. Do not report these slides as edited. Re-read them with get-deck and send content that actually differs.`,
2185
+ { errorCode: "deck_patch_no_change", statusCode: 409 },
2186
+ );
2187
+ }
2188
+
2189
+ const meaningfulChange =
2190
+ deckVersionContentSignature(row.data) !==
2191
+ deckVersionContentSignature(deck) ||
2192
+ row.title !== sqlTitle ||
2193
+ row.designSystemId !== sqlDesignSystemId ||
2194
+ generationRecord !== undefined;
2195
+ if (!meaningfulChange) {
2196
+ if (isAgentCaller) {
2197
+ fail(
2198
+ "Nothing was written: the requested deck patch is identical to the current deck. Re-read with get-deck before retrying.",
2199
+ { errorCode: "deck_patch_no_change", statusCode: 409 },
2200
+ );
2201
+ }
2202
+ if (clientWrite) {
2203
+ const updateResult = await db
2204
+ .update(schema.decks)
2205
+ .set(deckClientWriteFields(clientWrite, row.updatedAt))
2206
+ .where(deckRevisionWhere(schema.decks, deckId, row.updatedAt));
2207
+ assertDeckWriteApplied(updateResult, deckId, "deck patch replay");
2208
+ }
2209
+ return {
2210
+ ok: true,
2211
+ deckId,
2212
+ updatedAt: row.updatedAt,
2213
+ applied: false,
2214
+ updatedSlideIds: [],
2215
+ deletedSlideIds: [],
2216
+ };
2217
+ }
2218
+
2219
+ const now = nextDeckRevision(row.updatedAt);
2220
+ deck.updatedAt = now;
2221
+
2222
+ await db.transaction(async (tx: any) => {
2223
+ await lockSlideAssetReferences(
2224
+ tx,
2225
+ deck,
2226
+ {
2227
+ ownerEmail: row.ownerEmail ?? "",
2228
+ orgId: row.orgId,
2229
+ },
2230
+ deckId,
2231
+ );
2232
+ await createDeckVersionSnapshot(
2233
+ {
2234
+ id: row.id,
2235
+ title: row.title ?? "Untitled",
2236
+ data: row.data ?? "",
2237
+ ownerEmail: row.ownerEmail ?? "",
2238
+ },
2239
+ {
2240
+ force: isAgentCaller,
2241
+ chatContext: deckVersionChatContextFromAction(ctx),
2242
+ label: "Before deck patch",
2243
+ db: tx,
2244
+ },
2245
+ );
2246
+ const updateResult = await tx
2247
+ .update(schema.decks)
2248
+ .set({
2249
+ title: sqlTitle,
2250
+ data: JSON.stringify(deck),
2251
+ designSystemId: sqlDesignSystemId,
2252
+ updatedAt: now,
2253
+ ...deckClientWriteFields(clientWrite, now),
2254
+ })
2255
+ .where(deckRevisionWhere(schema.decks, deckId, row.updatedAt));
2256
+ assertDeckWriteApplied(updateResult, deckId, "deck patch");
2257
+ if (generationRecord) {
2258
+ await recordGenerationCreativeContext(
2259
+ {
2260
+ appId: "slides-2",
2261
+ artifactType: "deck",
2262
+ artifactId: deckId,
2263
+ ...generationRecord,
2264
+ },
2265
+ { db: tx },
2266
+ );
2267
+ }
2268
+ });
2269
+
2270
+ if (
2271
+ operations.some((operation) => operation.op !== "patch-deck-fields")
2272
+ ) {
2273
+ trackSlideContentEdited(
2274
+ "patch_deck",
2275
+ deckId,
2276
+ previousSlides,
2277
+ deck,
2278
+ ctx,
2279
+ );
2280
+ }
2281
+ trackDeckCreationStarted(
2282
+ deckId,
2283
+ previousGenerationContext,
2284
+ deck.generationContext,
2285
+ ctx,
2286
+ );
2287
+
2288
+ const updatedSlideIds = requestedSlideIds.filter((slideId) =>
2289
+ changedSlideIds.has(slideId),
2290
+ );
2291
+ const hasMixedStructuralOperation = operations.some(
2292
+ (operation) =>
2293
+ operation.op === "delete-slide" ||
2294
+ operation.op === "reorder-slides" ||
2295
+ operation.op === "patch-deck-fields",
2296
+ );
2297
+ const agentChangeId = deckVersionChangeGroupFromAction(ctx);
2298
+ if (updatedSlideIds.length === 1 && !hasMixedStructuralOperation) {
2299
+ await notifyClients(deckId, {
2300
+ slideId: updatedSlideIds[0],
2301
+ actor: isAgentCaller ? "agent" : "human",
2302
+ ...(agentChangeId ? { agentChangeId } : {}),
2303
+ });
2304
+ } else if (agentChangeId) {
2305
+ await notifyClients(deckId, { agentChangeId });
2306
+ } else {
2307
+ await notifyClients(deckId);
2308
+ }
2309
+
2310
+ const layoutFitSlideIdList = [...layoutFitSlideIds].filter((slideId) =>
2311
+ signaturesAfterOperations.has(slideId),
2312
+ );
2313
+ const finalSlides: Array<{
2314
+ id?: unknown;
2315
+ content?: unknown;
2316
+ layoutFitRevision?: unknown;
2317
+ }> = Array.isArray(deck.slides) ? deck.slides : [];
2318
+ const base = {
2319
+ ok: true,
2320
+ deckId,
2321
+ updatedAt: now,
2322
+ updatedSlideIds,
2323
+ deletedSlideIds: [...deletedSlideIds].filter(
2324
+ (slideId) =>
2325
+ signaturesBeforeOperations.has(slideId) &&
2326
+ !signaturesAfterOperations.has(slideId),
2327
+ ),
2328
+ ...(unchangedSlideIds.length
2329
+ ? {
2330
+ unchangedSlideIds,
2331
+ partial: true,
2332
+ message: `Applied, but ${unchangedSlideIds.join(", ")} came out identical to the stored slide and were not written — do not report those slides as edited.`,
2333
+ }
2334
+ : {}),
2335
+ ...(isAgentCaller
2336
+ ? slideHygieneResult(
2337
+ [...contentChangedSlideIds].map((slideId) => ({
2338
+ slideId,
2339
+ html: contentsAfterOperations.get(slideId) ?? "",
2340
+ previousHtml: contentsBeforeOperations.get(slideId),
2341
+ })),
2342
+ )
2343
+ : {}),
2344
+ ...(sourceRewriteRequested ? { sourceRewritten: true } : {}),
2345
+ ...(sourceImportCleared && !sourceRewriteRequested
2346
+ ? { sourceImportCleared: true }
2347
+ : {}),
2348
+ ...(layoutFitSlideIdList.length
2349
+ ? {
2350
+ layoutFit: {
2351
+ status: "pending" as const,
2352
+ slides: layoutFitSlideIdList.map((slideId) => {
2353
+ const slide = finalSlides.find((s) => s.id === slideId);
2354
+ return {
2355
+ slideId,
2356
+ contentHash: hashSlideContent(
2357
+ typeof slide?.content === "string" ? slide.content : "",
2358
+ ),
2359
+ layoutFitRevision:
2360
+ typeof slide?.layoutFitRevision === "string"
2361
+ ? slide.layoutFitRevision
2362
+ : undefined,
2363
+ };
2364
+ }),
2365
+ },
2366
+ }
2367
+ : {}),
2368
+ };
2369
+ return base;
2370
+ };
2371
+ return withDeckLock(deckId, () =>
2372
+ retryDeckWrite(
2373
+ applyPatch,
2374
+ isMergeSafeDeckPatchOperations(requestedOperations),
2375
+ ),
2376
+ );
2377
+ },
2378
+ });