@agent-native/core 0.141.7 → 0.144.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/corpus/README.md +1 -1
  3. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +19 -0
  4. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +296 -0
  5. package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  6. package/corpus/templates/analytics/.agents/skills/turn-into-skill/SKILL.md +112 -0
  7. package/corpus/templates/analytics/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  8. package/corpus/templates/analytics/AGENTS.md +35 -38
  9. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +1 -1
  10. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +17 -22
  11. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +1 -1
  12. package/corpus/templates/analytics/actions/navigate.ts +4 -5
  13. package/corpus/templates/analytics/actions/provider-api-register.ts +50 -0
  14. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +44 -11
  15. package/corpus/templates/analytics/actions/test-custom-api-connection.ts +106 -0
  16. package/corpus/templates/analytics/app/components/CustomApiCard.tsx +430 -0
  17. package/corpus/templates/analytics/app/i18n-data.ts +680 -0
  18. package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +6 -0
  19. package/corpus/templates/analytics/app/lib/sql-query.ts +33 -6
  20. package/corpus/templates/analytics/app/pages/DataSources.tsx +4 -0
  21. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-load-reliably-during-background-data-sw.md +6 -0
  22. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-remain-responsive-while-historical-data.md +6 -0
  23. package/corpus/templates/analytics/changelog/2026-08-06-analytics-query-results-now-render-as-a-table-with-csv-downl.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-08-06-connect-public-https-custom-apis-and-hand-tested-endpoints-t.md +6 -0
  25. package/corpus/templates/analytics/changelog/2026-08-06-durable-agent-worker-memory.md +6 -0
  26. package/corpus/templates/analytics/changelog/2026-08-06-live-dashboard-panels-use-the-shared-query-timeout.md +6 -0
  27. package/corpus/templates/analytics/changelog/2026-08-06-on-demand-billing-is-available-as-a-native-dashboard-with-ta.md +6 -0
  28. package/corpus/templates/analytics/changelog/2026-08-06-visible-dashboard-names-are-unique.md +6 -0
  29. package/corpus/templates/analytics/netlify.toml +7 -0
  30. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +4 -0
  31. package/corpus/templates/analytics/server/db/index.ts +27 -0
  32. package/corpus/templates/analytics/server/db/schema.ts +10 -0
  33. package/corpus/templates/analytics/server/lib/dashboards-store.ts +227 -60
  34. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +156 -45
  35. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +227 -1
  36. package/corpus/templates/analytics/server/plugins/agent-chat.ts +5 -0
  37. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +1 -0
  38. package/corpus/templates/analytics/server/plugins/db.ts +52 -18
  39. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +28 -2
  40. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +16 -1
  41. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +296 -0
  42. package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  43. package/corpus/templates/assets/.agents/skills/turn-into-skill/SKILL.md +112 -0
  44. package/corpus/templates/assets/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  45. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +296 -0
  46. package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  47. package/corpus/templates/brain/.agents/skills/turn-into-skill/SKILL.md +112 -0
  48. package/corpus/templates/brain/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  49. package/corpus/templates/brain/actions/list-connection-providers.ts +24 -3
  50. package/corpus/templates/brain/app/lib/dispatch-links.ts +44 -0
  51. package/corpus/templates/brain/app/routes/sources.tsx +15 -7
  52. package/corpus/templates/brain/changelog/2026-08-06-brain-provider-setup-links-open-dispatch-correctly-in-standa.md +6 -0
  53. package/corpus/templates/brain/server/plugins/agent-chat.ts +1 -0
  54. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +26 -0
  55. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +296 -0
  56. package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  57. package/corpus/templates/calendar/.agents/skills/turn-into-skill/SKILL.md +112 -0
  58. package/corpus/templates/calendar/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  59. package/corpus/templates/calendar/AGENTS.md +11 -32
  60. package/corpus/templates/calendar/actions/update-event.ts +107 -1
  61. package/corpus/templates/calendar/app/components/calendar/EventCalendarSelect.tsx +83 -0
  62. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +57 -0
  63. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +59 -73
  64. package/corpus/templates/calendar/app/hooks/use-events.ts +4 -1
  65. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  66. package/corpus/templates/calendar/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  67. package/corpus/templates/calendar/changelog/2026-08-06-existing-events-can-move-between-connected-google-account-ca.md +6 -0
  68. package/corpus/templates/calendar/server/lib/google-calendar.ts +101 -0
  69. package/corpus/templates/calendar/server/plugins/agent-chat.ts +7 -1
  70. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  71. package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  72. package/corpus/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  73. package/corpus/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  74. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +296 -0
  75. package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  76. package/corpus/templates/clips/.agents/skills/turn-into-skill/SKILL.md +112 -0
  77. package/corpus/templates/clips/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  78. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +9 -24
  79. package/corpus/templates/clips/app/components/editor/timeline-geometry.ts +15 -0
  80. package/corpus/templates/clips/app/components/editor/waveform.tsx +4 -17
  81. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +14 -3
  82. package/corpus/templates/clips/app/components/player/player-controls.tsx +0 -3
  83. package/corpus/templates/clips/app/components/player/scrubber.tsx +2 -33
  84. package/corpus/templates/clips/app/components/player/video-player.tsx +58 -11
  85. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +28 -38
  86. package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -27
  87. package/corpus/templates/clips/changelog/2026-08-06-clips-recovers-from-stalled-recording-setup-without-requirin.md +6 -0
  88. package/corpus/templates/clips/changelog/2026-08-06-clips-starts-faster-after-a-deploy-or-idle-period-startup-no.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-08-06-deleting-a-recording-returns-cleanly-to-the-library-and-fit-.md +6 -0
  90. package/corpus/templates/clips/changelog/2026-08-06-hide-clip-insights-from-viewers-without-analytics-access.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-08-06-trimmed-clips-play-on-a-clean-timeline-without-cut-markers.md +6 -0
  92. package/corpus/templates/clips/desktop/src/app.tsx +48 -20
  93. package/corpus/templates/clips/desktop/src/lib/recorder.ts +275 -74
  94. package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +84 -0
  95. package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +14 -4
  96. package/corpus/templates/clips/server/plugins/db.ts +214 -26
  97. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +296 -0
  98. package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  99. package/corpus/templates/content/.agents/skills/turn-into-skill/SKILL.md +112 -0
  100. package/corpus/templates/content/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  101. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +296 -0
  102. package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  103. package/corpus/templates/crm/.agents/skills/turn-into-skill/SKILL.md +112 -0
  104. package/corpus/templates/crm/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  105. package/corpus/templates/crm/changelog/2026-08-06-crm-starts-faster-after-a-deploy-or-idle-period-one-time-dat.md +6 -0
  106. package/corpus/templates/crm/server/plugins/db.ts +11 -8
  107. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +36 -1
  108. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +296 -0
  109. package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  110. package/corpus/templates/design/.agents/skills/turn-into-skill/SKILL.md +112 -0
  111. package/corpus/templates/design/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  112. package/corpus/templates/design/actions/import-from-url.ts +1 -1
  113. package/corpus/templates/design/app/components/design/LayersPanel.tsx +27 -18
  114. package/corpus/templates/design/app/pages/Index.tsx +6 -2
  115. package/corpus/templates/design/changelog/2026-08-05-deleting-a-design-from-the-home-page-no-longer-leaves-the-ap.md +6 -0
  116. package/corpus/templates/design/changelog/2026-08-05-renaming-a-layer-now-focuses-its-name-field-automatically.md +6 -0
  117. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  118. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +296 -0
  119. package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  120. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/SKILL.md +112 -0
  121. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  122. package/corpus/templates/dispatch/AGENTS.md +8 -1
  123. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -0
  124. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +30 -15
  125. package/corpus/templates/dispatch/app/i18n-data.ts +131 -0
  126. package/corpus/templates/dispatch/app/routes/admin._index.tsx +4 -0
  127. package/corpus/templates/dispatch/app/routes/admin.agents.tsx +1 -0
  128. package/corpus/templates/dispatch/app/routes/admin.approvals.tsx +1 -0
  129. package/corpus/templates/dispatch/app/routes/admin.apps.$appId.tsx +1 -0
  130. package/corpus/templates/dispatch/app/routes/admin.apps.tsx +1 -0
  131. package/corpus/templates/dispatch/app/routes/admin.audit.tsx +1 -0
  132. package/corpus/templates/dispatch/app/routes/admin.automations.tsx +1 -0
  133. package/corpus/templates/dispatch/app/routes/admin.destinations.tsx +4 -0
  134. package/corpus/templates/dispatch/app/routes/admin.dreams.tsx +1 -0
  135. package/corpus/templates/dispatch/app/routes/admin.identities.tsx +1 -0
  136. package/corpus/templates/dispatch/app/routes/admin.integrations.tsx +4 -0
  137. package/corpus/templates/dispatch/app/routes/admin.messaging.tsx +1 -0
  138. package/corpus/templates/dispatch/app/routes/admin.metrics.tsx +1 -0
  139. package/corpus/templates/dispatch/app/routes/admin.new-app.tsx +1 -0
  140. package/corpus/templates/dispatch/app/routes/admin.operations.tsx +1 -0
  141. package/corpus/templates/dispatch/app/routes/admin.thread-debug.tsx +4 -0
  142. package/corpus/templates/dispatch/app/routes/admin.transactional-email.$appId.$id.tsx +4 -0
  143. package/corpus/templates/dispatch/app/routes/admin.transactional-email.tsx +4 -0
  144. package/corpus/templates/dispatch/app/routes/admin.tsx +1 -0
  145. package/corpus/templates/dispatch/app/routes/admin.vault.tsx +1 -0
  146. package/corpus/templates/dispatch/app/routes/admin.workspace.tsx +1 -0
  147. package/corpus/templates/dispatch/changelog/2026-08-06-dispatch-keeps-everyday-navigation-focused-while-management-.md +6 -0
  148. package/corpus/templates/dispatch/changelog/2026-08-06-ready-workspace-apps-stay-one-click-away-in-a-compact-dispat.md +6 -0
  149. package/corpus/templates/dispatch/changelog/2026-08-06-shared-vault-values-and-management-are-now-restricted-to-wor.md +6 -0
  150. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +296 -0
  151. package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  152. package/corpus/templates/factory/.agents/skills/turn-into-skill/SKILL.md +112 -0
  153. package/corpus/templates/factory/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  154. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +296 -0
  155. package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  156. package/corpus/templates/forms/.agents/skills/turn-into-skill/SKILL.md +112 -0
  157. package/corpus/templates/forms/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  158. package/corpus/templates/forms/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  159. package/corpus/templates/forms/server/plugins/agent-chat.ts +4 -0
  160. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +296 -0
  161. package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  162. package/corpus/templates/macros/.agents/skills/turn-into-skill/SKILL.md +112 -0
  163. package/corpus/templates/macros/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  164. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +296 -0
  165. package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  166. package/corpus/templates/mail/.agents/skills/turn-into-skill/SKILL.md +112 -0
  167. package/corpus/templates/mail/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  168. package/corpus/templates/mail/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  169. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  170. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +296 -0
  171. package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  172. package/corpus/templates/plan/.agents/skills/turn-into-skill/SKILL.md +112 -0
  173. package/corpus/templates/plan/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  174. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +48 -5
  175. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +37 -1
  176. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +6 -0
  177. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +15 -2
  178. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +29 -24
  179. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +296 -0
  180. package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  181. package/corpus/templates/slides/.agents/skills/turn-into-skill/SKILL.md +112 -0
  182. package/corpus/templates/slides/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  183. package/corpus/templates/slides/AGENTS.md +17 -21
  184. package/corpus/templates/slides/_gitignore +1 -0
  185. package/corpus/templates/slides/actions/export-pptx.ts +470 -36
  186. package/corpus/templates/slides/actions/get-deck.ts +12 -0
  187. package/corpus/templates/slides/actions/import-file.ts +175 -139
  188. package/corpus/templates/slides/actions/import-google-slides-reference.ts +271 -12
  189. package/corpus/templates/slides/actions/import-pptx.ts +193 -68
  190. package/corpus/templates/slides/actions/patch-deck.ts +51 -0
  191. package/corpus/templates/slides/actions/update-slide.ts +29 -3
  192. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -13
  193. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +5 -1
  194. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +25 -12
  195. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +112 -57
  196. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +202 -0
  197. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +26 -14
  198. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +173 -0
  199. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +182 -141
  200. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  201. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +18 -0
  202. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +85 -0
  203. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -0
  204. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +15 -0
  205. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +27 -1
  206. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  207. package/corpus/templates/slides/app/lib/create-deck-generation.ts +72 -11
  208. package/corpus/templates/slides/app/lib/deck-preview-frame.ts +15 -0
  209. package/corpus/templates/slides/app/lib/deck-share-links.ts +9 -0
  210. package/corpus/templates/slides/app/lib/export-pptx-client.ts +89 -0
  211. package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +89 -0
  212. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +3 -0
  213. package/corpus/templates/slides/app/pages/Index.tsx +172 -15
  214. package/corpus/templates/slides/changelog/2026-08-06-clicking-the-gray-canvas-around-a-slide-clears-its-selection.md +6 -0
  215. package/corpus/templates/slides/changelog/2026-08-06-deck-thumbnails-keep-a-consistent-listing-frame-across-aspec.md +6 -0
  216. package/corpus/templates/slides/changelog/2026-08-06-deep-linked-decks-open-directly-instead-of-being-interrupted.md +6 -0
  217. package/corpus/templates/slides/changelog/2026-08-06-google-slides-exports-now-show-clear-import-steps-when-direc.md +6 -0
  218. package/corpus/templates/slides/changelog/2026-08-06-import-pdf-google-slides-and-powerpoint-decks-directly-from-.md +6 -0
  219. package/corpus/templates/slides/changelog/2026-08-06-imported-powerpoint-exports-keep-text-and-images-editable-an.md +6 -0
  220. package/corpus/templates/slides/changelog/2026-08-06-presentation-prompts-continue-adding-rendered-slides-until-t.md +6 -0
  221. package/corpus/templates/slides/changelog/2026-08-06-public-decks-now-share-read-only-presentation-links-by-defau.md +6 -0
  222. package/corpus/templates/slides/changelog/2026-08-06-slides-offers-a-direct-connect-google-button-when-a-drive-co.md +6 -0
  223. package/corpus/templates/slides/changelog/2026-08-06-slides-preserve-uploaded-pdf-layouts-accept-larger-reference.md +6 -0
  224. package/corpus/templates/slides/changelog/2026-08-06-styled-words-stay-part-of-their-text-box-instead-of-becoming.md +6 -0
  225. package/corpus/templates/slides/changelog/2026-08-06-uploaded-decks-keep-their-slides-and-visuals-when-you-improv.md +6 -0
  226. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +7 -20
  227. package/corpus/templates/slides/server/handlers/import/html-converter.ts +247 -13
  228. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +67 -0
  229. package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +2 -0
  230. package/corpus/templates/slides/server/handlers/uploads.ts +7 -3
  231. package/corpus/templates/slides/server/lib/chat-attachments.ts +7 -4
  232. package/corpus/templates/slides/server/lib/emails.ts +7 -2
  233. package/corpus/templates/slides/server/lib/google-docs-error.ts +50 -0
  234. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +1 -0
  235. package/corpus/templates/slides/server/lib/import-asset-storage.ts +127 -0
  236. package/corpus/templates/slides/server/lib/source-import.ts +167 -0
  237. package/corpus/templates/slides/server/plugins/agent-chat.ts +12 -3
  238. package/corpus/templates/slides/server/routes/api/import-assets/[token].get.ts +70 -0
  239. package/corpus/templates/slides/shared/upload-types.ts +3 -0
  240. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +296 -0
  241. package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  242. package/corpus/templates/tasks/.agents/skills/turn-into-skill/SKILL.md +112 -0
  243. package/corpus/templates/tasks/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  244. package/dist/agent/production-agent.d.ts +17 -0
  245. package/dist/agent/production-agent.js +72 -1
  246. package/dist/cli/agent-plugin.d.ts +112 -0
  247. package/dist/cli/agent-plugin.js +1081 -0
  248. package/dist/cli/app-skill.js +39 -1
  249. package/dist/cli/code-agent-executor.d.ts +3 -0
  250. package/dist/cli/code-agent-executor.js +51 -13
  251. package/dist/cli/index.js +13 -0
  252. package/dist/cli/templates-meta.js +1 -1
  253. package/dist/client/AgentPanel.d.ts +3 -1
  254. package/dist/client/AgentPanel.js +9 -2
  255. package/dist/client/AssistantChat.d.ts +8 -0
  256. package/dist/client/AssistantChat.js +23 -8
  257. package/dist/client/NewWorkspaceAppFlow.js +1 -0
  258. package/dist/client/agent-chat-adapter.js +123 -4
  259. package/dist/client/assistant-ui-recovery.d.ts +2 -1
  260. package/dist/client/assistant-ui-recovery.js +7 -3
  261. package/dist/client/onboarding/FirstRunOnboarding.js +11 -4
  262. package/dist/client/org/TeamPage.js +1 -1
  263. package/dist/client/require-session.js +1 -1
  264. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  265. package/dist/client/resources/McpConnectionSuggestion.js +27 -10
  266. package/dist/client/resources/McpIntegrationDialog.js +77 -45
  267. package/dist/client/resources/mcp-integration-catalog.d.ts +13 -1
  268. package/dist/client/resources/mcp-integration-catalog.js +255 -2
  269. package/dist/client/resources/mcp-integration-logos.js +18 -2
  270. package/dist/client/settings/AccountSettingsCard.js +2 -1
  271. package/dist/collab/struct-routes.d.ts +1 -1
  272. package/dist/db/client.d.ts +9 -0
  273. package/dist/db/client.js +51 -16
  274. package/dist/db/create-get-db.js +2 -2
  275. package/dist/db/ddl-guard.d.ts +11 -10
  276. package/dist/db/ddl-guard.js +40 -12
  277. package/dist/deploy/build.d.ts +2 -0
  278. package/dist/deploy/build.js +97 -4
  279. package/dist/deploy/workspace-deploy.js +0 -2
  280. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  281. package/dist/guards/no-env-credentials.js +9 -0
  282. package/dist/ingestion/index.d.ts +1 -1
  283. package/dist/ingestion/pptx.d.ts +68 -0
  284. package/dist/ingestion/pptx.js +660 -213
  285. package/dist/integrations/plugin.js +9 -6
  286. package/dist/jobs/scheduler.js +79 -23
  287. package/dist/localization/default-messages.d.ts +54 -0
  288. package/dist/localization/default-messages.js +57 -2
  289. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  290. package/dist/mcp-client/oauth-routes.d.ts +2 -0
  291. package/dist/mcp-client/oauth-routes.js +73 -6
  292. package/dist/observability/routes.d.ts +5 -5
  293. package/dist/onboarding/app-profile.js +11 -0
  294. package/dist/progress/routes.d.ts +1 -1
  295. package/dist/resources/handlers.d.ts +1 -1
  296. package/dist/resources/store.js +15 -1
  297. package/dist/scripts/call-agent.js +34 -4
  298. package/dist/secrets/routes.d.ts +3 -3
  299. package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
  300. package/dist/server/agent-chat/action-filters-a2a.js +36 -1
  301. package/dist/server/agent-chat-plugin.js +29 -8
  302. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  303. package/dist/server/auth.js +47 -11
  304. package/dist/server/better-auth-instance.js +5 -3
  305. package/dist/server/core-routes-plugin.d.ts +8 -1
  306. package/dist/server/core-routes-plugin.js +28 -3
  307. package/dist/server/credential-provider.d.ts +3 -3
  308. package/dist/server/credential-provider.js +90 -4
  309. package/dist/server/onboarding-html.d.ts +2 -0
  310. package/dist/server/onboarding-html.js +115 -29
  311. package/dist/server/realtime-token.d.ts +1 -1
  312. package/dist/server/self-dispatch.d.ts +5 -4
  313. package/dist/server/self-dispatch.js +25 -14
  314. package/dist/server/ssr-handler.d.ts +1 -1
  315. package/dist/server/ssr-handler.js +1 -1
  316. package/dist/server/transcribe-voice.d.ts +1 -1
  317. package/dist/shared/cache-control.d.ts +26 -2
  318. package/dist/shared/cache-control.js +43 -2
  319. package/dist/shared/password-policy.d.ts +3 -0
  320. package/dist/shared/password-policy.js +3 -0
  321. package/dist/sharing/actions/set-resource-visibility.js +16 -4
  322. package/dist/sharing/registry.d.ts +14 -0
  323. package/dist/styles/agent-native.css +6 -0
  324. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  325. package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  326. package/dist/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  327. package/dist/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  328. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  329. package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  330. package/dist/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  331. package/dist/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  332. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  333. package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  334. package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  335. package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  336. package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  337. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  338. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  339. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  340. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  341. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  342. package/dist/templates/workspace-root/AGENTS.md +7 -0
  343. package/dist/user-profile/actions/change-password.js +8 -3
  344. package/dist/user-profile/actions/set-password.js +5 -1
  345. package/dist/vite/action-types-plugin.js +59 -0
  346. package/docs/content/creating-templates.mdx +1 -1
  347. package/docs/content/getting-started-actions.mdx +38 -189
  348. package/docs/content/getting-started-database.mdx +80 -164
  349. package/docs/content/getting-started-pages.mdx +44 -155
  350. package/docs/content/getting-started.mdx +26 -106
  351. package/docs/content/skills-guide.mdx +13 -0
  352. package/package.json +6 -2
  353. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  354. package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  355. package/src/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  356. package/src/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  357. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  358. package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  359. package/src/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  360. package/src/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  361. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  362. package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  363. package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  364. package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  365. package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  366. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  367. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  368. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  369. package/src/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  370. package/src/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  371. package/src/templates/workspace-root/AGENTS.md +7 -0
@@ -15,8 +15,15 @@ import { z } from "zod";
15
15
 
16
16
  import { getDb, schema } from "../server/db/index.js";
17
17
  import { notifyClients } from "../server/handlers/decks.js";
18
+ import { uploadPptxSlideImages } from "../server/handlers/import/pptx-assets.js";
18
19
  import { upsertBuilderProxyDesignSystem } from "../server/lib/builder-design-system-proxy.js";
19
20
  import { setupPdfParse } from "../server/lib/pdf-parse-setup.js";
21
+ import {
22
+ buildSourceImportMetadata,
23
+ mergeSourceImportMetadata,
24
+ sourceImportForDeck,
25
+ type SourceImportSlideSnapshot,
26
+ } from "../server/lib/source-import.js";
20
27
  import {
21
28
  ASPECT_RATIOS,
22
29
  DEFAULT_ASPECT_RATIO,
@@ -30,9 +37,9 @@ const DEFAULT_MAX_SOURCE_CHARS = 60_000;
30
37
  export default defineAction({
31
38
  description:
32
39
  "Import a file (PPTX, DOCX, PDF, FIG) and extract content for creating slides or slide design systems. " +
33
- "For PPTX files, returns parsed slides with text and layout info ready for conversion. " +
40
+ "For PPTX files, returns parsed slides with text and layout info ready for conversion, or writes positioned source-preserving slides when importIntoDeck is true. " +
34
41
  "For DOCX files, returns structured sections extracted from the document. " +
35
- "For PDF files, returns extracted text organized by page. " +
42
+ "For PDF files, returns extracted text organized by page, or source-faithful page-image slides when importIntoDeck is true. " +
36
43
  "For Figma .fig files, requires Builder.io (free tier available) and starts Builder design-system indexing; the returned Builder job/design-system ids are the source of truth. " +
37
44
  "The agent can then use the extracted content to create a deck via create-deck or add-slide, or tell the user where Builder is indexing the design system.",
38
45
  schema: z.object({
@@ -53,7 +60,7 @@ export default defineAction({
53
60
  .optional()
54
61
  .default(false)
55
62
  .describe(
56
- "If true, append slides converted from the file to the end of deckId's existing slides.",
63
+ "If true, append slides to deckId. PDF pages are imported as source-faithful images that preserve their original layout and aspect ratio; use the default false when editable extracted source text is needed.",
57
64
  ),
58
65
  maxChars: z.coerce
59
66
  .number()
@@ -152,7 +159,37 @@ export default defineAction({
152
159
  (total, r) => total + r.imageSkippedCount,
153
160
  0,
154
161
  );
155
- await appendDeckSlides(deckId, title, slides, "import-file:pptx");
162
+ const sourceImport = buildSourceImportMetadata({
163
+ format: "pptx",
164
+ slides: pptxResults.map((result) => ({
165
+ id: result.slide.id,
166
+ text: result.sourceText,
167
+ notes: result.slide.notes ?? "",
168
+ imageUrls: result.imageUrls,
169
+ editableText: true,
170
+ })),
171
+ imagesSkipped,
172
+ });
173
+ if (imagesSkipped > 0) {
174
+ throw new Error(
175
+ `Source-faithful PPTX import could not preserve ${imagesSkipped} image(s). No slides were written. Retry with browser-renderable images or use a PDF export for page-faithful preservation.`,
176
+ );
177
+ }
178
+ const aspectRatio =
179
+ presentation.slides[0]?.widthEmu && presentation.slides[0]?.heightEmu
180
+ ? nearestAspectRatio(
181
+ presentation.slides[0].widthEmu,
182
+ presentation.slides[0].heightEmu,
183
+ )
184
+ : undefined;
185
+ await appendDeckSlides(
186
+ deckId,
187
+ title,
188
+ slides,
189
+ "import-file:pptx",
190
+ aspectRatio,
191
+ sourceImport,
192
+ );
156
193
  return {
157
194
  format: "pptx",
158
195
  title,
@@ -176,6 +213,8 @@ export default defineAction({
176
213
  imageNames: slide.images.map((img) => img.name),
177
214
  notes: slide.notes,
178
215
  layoutHint: slide.layoutHint,
216
+ transition: slide.transition,
217
+ splitByParagraph: slide.splitByParagraph,
179
218
  })),
180
219
  theme: presentation.theme,
181
220
  deckId,
@@ -238,9 +277,15 @@ export default defineAction({
238
277
  // typography) bake their visuals into vector/image page content with
239
278
  // no reliable text/shape structure to reconstruct, so importing into a
240
279
  // deck rasterizes each page instead of flattening it to generic bullet
241
- // text. Falls through to the text-only path below when the optional
242
- // canvas renderer isn't available in this runtime.
243
- if (importIntoDeck && canvasFactory) {
280
+ // text. Do not silently fall back to text reconstruction when the
281
+ // renderer is unavailable: that is how a visually complete source deck
282
+ // turns into a misleading, content-lossy import.
283
+ if (importIntoDeck) {
284
+ if (!canvasFactory) {
285
+ throw new Error(
286
+ "Source-faithful PDF import is unavailable in this runtime because the page renderer is not configured. Retry after connecting file storage or use a PDF export with a supported Slides deployment.",
287
+ );
288
+ }
244
289
  if (!deckId) throw new Error("deckId is required to import into deck");
245
290
  return importPdfPagesAsFullBleedSlides({
246
291
  fileBuffer,
@@ -251,8 +296,6 @@ export default defineAction({
251
296
  });
252
297
  }
253
298
 
254
- const { convertSectionsToSlides } =
255
- await import("../server/handlers/import/html-converter.js");
256
299
  const pdf = new PDFParse({
257
300
  data: new Uint8Array(fileBuffer),
258
301
  CanvasFactory: canvasFactory,
@@ -267,29 +310,6 @@ export default defineAction({
267
310
  );
268
311
  }
269
312
 
270
- if (importIntoDeck) {
271
- if (!deckId) throw new Error("deckId is required to import into deck");
272
- const sections = textPages.map((p) => ({
273
- heading: `Page ${p.num}`,
274
- content: p.text,
275
- }));
276
- const slideHtmlArray = convertSectionsToSlides(sections);
277
- const slides = slideHtmlArray.map((content) => ({
278
- id: newSlideId(),
279
- content,
280
- layout: "content",
281
- notes: "",
282
- }));
283
- await appendDeckSlides(deckId, title, slides, "import-file:pdf");
284
- return {
285
- format: "pdf",
286
- title,
287
- pageCount: pages.length,
288
- slideCount: slides.length,
289
- deckId,
290
- imported: true,
291
- };
292
- }
293
313
  const totalTextLength = textPages.reduce(
294
314
  (sum, p) => sum + p.text.length,
295
315
  0,
@@ -339,7 +359,7 @@ async function importPdfPagesAsFullBleedSlides(args: {
339
359
  canvasFactory: object;
340
360
  }) {
341
361
  const { fileBuffer, title, deckId, PDFParse, canvasFactory } = args;
342
- const { buildFullBleedImageSlideHtml, convertSectionsToSlides } =
362
+ const { buildFullPageImageSlideHtml } =
343
363
  await import("../server/handlers/import/html-converter.js");
344
364
 
345
365
  const pdf = new PDFParse({
@@ -347,69 +367,75 @@ async function importPdfPagesAsFullBleedSlides(args: {
347
367
  CanvasFactory: canvasFactory,
348
368
  });
349
369
  let pages: { num: number; text: string }[];
350
- let imagesByPage: Map<
370
+ let screenshotsByPage: Map<
351
371
  number,
352
372
  { data: Uint8Array; width: number; height: number }
353
373
  >;
354
374
  try {
355
- pages = normalizePdfPages(await pdf.getText());
356
- // Reuse the page's own embedded photo rather than rasterizing the whole
357
- // page: headless canvas text rendering depends on the PDF's embedded
358
- // fonts resolving in this runtime, which is unreliable, so real
359
- // extracted text is drawn as HTML below instead of trusting the render.
360
- const imageResult = await pdf.getImage({
375
+ let textPages: { num: number; text: string }[] = [];
376
+ try {
377
+ textPages = normalizePdfPages(await pdf.getText());
378
+ } catch (error) {
379
+ console.warn(
380
+ "[import-file] PDF text extraction failed; preserving rendered pages and leaving notes empty:",
381
+ error,
382
+ );
383
+ }
384
+ // Rasterize the complete page so positioned text, vector shapes, logos,
385
+ // photos, and the source page proportions survive the import together.
386
+ const screenshotResult = await pdf.getScreenshot({
387
+ desiredWidth: 1600,
361
388
  imageBuffer: true,
362
389
  imageDataUrl: false,
363
390
  });
364
- imagesByPage = new Map();
365
- for (const page of imageResult.pages) {
366
- const largest = page.images.reduce<
367
- { data: Uint8Array; width: number; height: number } | undefined
368
- >((best, image) => {
369
- if (!best || image.width * image.height > best.width * best.height) {
370
- return { data: image.data, width: image.width, height: image.height };
371
- }
372
- return best;
373
- }, undefined);
374
- if (largest) imagesByPage.set(page.pageNumber, largest);
391
+ screenshotsByPage = new Map();
392
+ for (const page of screenshotResult.pages) {
393
+ screenshotsByPage.set(page.pageNumber, {
394
+ data: page.data,
395
+ width: page.width,
396
+ height: page.height,
397
+ });
375
398
  }
399
+ if (screenshotsByPage.size === 0) {
400
+ throw new Error("The PDF renderer returned no pages.");
401
+ }
402
+ pages = screenshotResult.pages.map((page) => ({
403
+ num: page.pageNumber,
404
+ text:
405
+ textPages.find((textPage) => textPage.num === page.pageNumber)?.text ??
406
+ "",
407
+ }));
376
408
  } finally {
377
409
  await pdf.destroy();
378
410
  }
379
411
 
412
+ if (pages.length === 0) {
413
+ throw new Error("The PDF renderer returned no importable pages.");
414
+ }
415
+
380
416
  // Source decks (e.g. Instagram carousel exports) are commonly portrait or
381
417
  // square, not the deck editor's 16:9 default — match the canvas to the
382
- // first embedded photo's own proportions instead of stretching/cropping it.
383
- const firstImage = pages
384
- .map((page) => imagesByPage.get(page.num))
418
+ // first rendered page's own proportions instead of stretching/cropping it.
419
+ const firstScreenshot = pages
420
+ .map((page) => screenshotsByPage.get(page.num))
385
421
  .find((image): image is NonNullable<typeof image> => Boolean(image));
386
- const aspectRatio = firstImage
387
- ? nearestAspectRatio(firstImage.width, firstImage.height)
422
+ const aspectRatio = firstScreenshot
423
+ ? nearestAspectRatio(firstScreenshot.width, firstScreenshot.height)
388
424
  : undefined;
389
425
 
390
426
  const ownerEmail = getRequestUserEmail();
391
- const slides = await Promise.all(
427
+ const imported = await Promise.all(
392
428
  pages.map(async (page) => {
393
- const lines = page.text
394
- .split(/\r?\n/)
395
- .map((line) => line.trim())
396
- .filter(Boolean);
397
- const backgroundImage = imagesByPage.get(page.num);
398
-
399
- if (!backgroundImage) {
400
- const [content] = convertSectionsToSlides([
401
- { heading: lines[0] ?? `Page ${page.num}`, content: page.text },
402
- ]);
403
- return {
404
- id: newSlideId(),
405
- content: content ?? '<div class="fmd-slide"></div>',
406
- layout: "content",
407
- notes: "",
408
- };
429
+ const screenshot = screenshotsByPage.get(page.num);
430
+
431
+ if (!screenshot) {
432
+ throw new Error(
433
+ `The PDF renderer did not return page ${page.num}; refusing a partial source import.`,
434
+ );
409
435
  }
410
436
 
411
437
  const uploadResult = await uploadFile({
412
- data: Buffer.from(backgroundImage.data),
438
+ data: Buffer.from(screenshot.data),
413
439
  filename: `slide-import-${Date.now()}-p${page.num}.png`,
414
440
  mimeType: "image/png",
415
441
  ownerEmail: ownerEmail ?? undefined,
@@ -420,29 +446,42 @@ async function importPdfPagesAsFullBleedSlides(args: {
420
446
  "File storage is not configured. Connect Builder.io (free tier available) or another upload provider before importing PDF slides.",
421
447
  );
422
448
  }
423
- // pdf-parse breaks wrapped text across lines with no way to tell a
424
- // wrapped title from a heading followed by a body paragraph. Treat a
425
- // couple of short lines as one wrapped title (join them so the
426
- // sentence isn't cut off); three or more lines as a heading plus body
427
- // text, rendered with distinct sizes so they don't collapse into one
428
- // flat paragraph.
429
- const heading =
430
- lines.length > 2 ? lines[0] : lines.join(" ") || undefined;
431
- const subtitle = lines.length > 2 ? lines.slice(1).join(" ") : undefined;
449
+ const id = newSlideId();
432
450
  return {
433
- id: newSlideId(),
434
- content: buildFullBleedImageSlideHtml(
435
- uploadResult.url,
436
- heading,
437
- subtitle,
438
- ),
439
- layout: "full-image",
440
- notes: page.text,
451
+ slide: {
452
+ id,
453
+ content: buildFullPageImageSlideHtml(
454
+ uploadResult.url,
455
+ screenshot.width,
456
+ screenshot.height,
457
+ ),
458
+ layout: "full-image",
459
+ notes: page.text,
460
+ },
461
+ snapshot: {
462
+ id,
463
+ text: page.text,
464
+ notes: page.text,
465
+ imageUrls: [uploadResult.url],
466
+ editableText: false,
467
+ } satisfies SourceImportSlideSnapshot,
441
468
  };
442
469
  }),
443
470
  );
471
+ const slides = imported.map((entry) => entry.slide);
472
+ const sourceImport = buildSourceImportMetadata({
473
+ format: "pdf",
474
+ slides: imported.map((entry) => entry.snapshot),
475
+ });
444
476
 
445
- await appendDeckSlides(deckId, title, slides, "import-file:pdf", aspectRatio);
477
+ await appendDeckSlides(
478
+ deckId,
479
+ title,
480
+ slides,
481
+ "import-file:pdf",
482
+ aspectRatio,
483
+ sourceImport,
484
+ );
446
485
 
447
486
  return {
448
487
  format: "pdf",
@@ -459,65 +498,44 @@ function newSlideId(): string {
459
498
  return `slide-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
460
499
  }
461
500
 
462
- // EMF/WMF (Windows metafiles) and TIFF are valid PPTX embed formats but
463
- // browsers can't render them in an <img> tag — uploading and linking one
464
- // would just produce a broken image icon.
465
- const PPTX_BROWSER_RENDERABLE_IMAGE_MIME_TYPES = new Set([
466
- "image/png",
467
- "image/jpeg",
468
- "image/gif",
469
- "image/webp",
470
- "image/svg+xml",
471
- "image/bmp",
472
- ]);
473
-
474
501
  async function buildPptxSlide(
475
502
  slide: import("../server/handlers/import/pptx-parser.js").ParsedSlide,
476
503
  slideIndex: number,
477
504
  ownerEmail: string,
478
505
  themeFont: string | undefined,
479
506
  ): Promise<{
480
- slide: { id: string; content: string; layout: string; notes?: string };
507
+ slide: {
508
+ id: string;
509
+ content: string;
510
+ layout: string;
511
+ notes?: string;
512
+ transition?: "instant" | "none" | "fade" | "slide" | "zoom";
513
+ splitByParagraph?: boolean;
514
+ };
481
515
  imageSkippedCount: number;
516
+ sourceText: string;
517
+ imageUrls: string[];
482
518
  }> {
483
519
  const { convertToSlideHtml } =
484
520
  await import("../server/handlers/import/html-converter.js");
485
- const image = slide.images[0];
486
- const uploadable =
487
- image && PPTX_BROWSER_RENDERABLE_IMAGE_MIME_TYPES.has(image.mimeType)
488
- ? image
489
- : undefined;
490
- const imageUrl = uploadable
491
- ? await uploadFile({
492
- data: Buffer.from(uploadable.data),
493
- filename:
494
- "pptx-import-" +
495
- Date.now() +
496
- "-s" +
497
- slideIndex +
498
- "-" +
499
- uploadable.name,
500
- mimeType: uploadable.mimeType,
501
- ownerEmail,
502
- recordAsset: false,
503
- })
504
- .then((result) => result?.url)
505
- // A single slide's upload failing (network/API/rate-limit)
506
- // shouldn't abort the whole deck replacement — fall back to a
507
- // placeholder like an unsupported format would.
508
- .catch(() => undefined)
509
- : undefined;
521
+ const uploadedImages = await uploadPptxSlideImages({
522
+ slide,
523
+ slideIndex,
524
+ ownerEmail,
525
+ });
526
+ const id = newSlideId();
510
527
  return {
511
528
  slide: {
512
- id: newSlideId(),
513
- content: convertToSlideHtml(slide, imageUrl, themeFont),
529
+ id,
530
+ content: convertToSlideHtml(slide, uploadedImages.urls, themeFont),
514
531
  layout: slide.layoutHint ?? "content",
515
532
  notes: slide.notes,
533
+ ...(slide.transition ? { transition: slide.transition } : {}),
534
+ ...(slide.splitByParagraph ? { splitByParagraph: true } : {}),
516
535
  },
517
- // Only the first image on a slide is ever uploaded, so every other
518
- // image on that slide is unconditionally dropped too — not just the
519
- // first one when it's unsupported.
520
- imageSkippedCount: Math.max(0, slide.images.length - (imageUrl ? 1 : 0)),
536
+ imageSkippedCount: uploadedImages.imageSkippedCount,
537
+ sourceText: slide.texts.map((text) => text.content).join("\n"),
538
+ imageUrls: Object.values(uploadedImages.urls),
521
539
  };
522
540
  }
523
541
 
@@ -604,9 +622,12 @@ async function appendDeckSlides(
604
622
  content: string;
605
623
  layout: string;
606
624
  notes?: string;
625
+ transition?: "instant" | "none" | "fade" | "slide" | "zoom";
626
+ splitByParagraph?: boolean;
607
627
  }>,
608
628
  source: string,
609
629
  aspectRatio?: AspectRatio,
630
+ sourceImport?: ReturnType<typeof buildSourceImportMetadata>,
610
631
  ) {
611
632
  await assertAccess("deck", deckId, "editor");
612
633
 
@@ -640,11 +661,18 @@ async function appendDeckSlides(
640
661
  // every slide already on it.
641
662
  const hadExistingSlides = previousSlides.length > 0;
642
663
  const nextTitle = hadExistingSlides ? (existing[0].title ?? title) : title;
664
+ const nextSourceImport = sourceImport
665
+ ? mergeSourceImportMetadata(
666
+ sourceImportForDeck(previousData.sourceImport),
667
+ sourceImport,
668
+ )
669
+ : sourceImportForDeck(previousData.sourceImport);
643
670
  const data = {
644
671
  ...previousData,
645
672
  title: nextTitle,
646
673
  slides: [...previousSlides, ...slides],
647
674
  ...(!hadExistingSlides && aspectRatio ? { aspectRatio } : {}),
675
+ ...(nextSourceImport ? { sourceImport: nextSourceImport } : {}),
648
676
  updatedAt: writeNow,
649
677
  };
650
678
 
@@ -665,12 +693,20 @@ async function appendDeckSlides(
665
693
  }
666
694
 
667
695
  function safeParseDeckData(raw: string): Record<string, unknown> {
696
+ let parsed: unknown;
668
697
  try {
669
- const parsed = JSON.parse(raw);
670
- return parsed && typeof parsed === "object" ? parsed : {};
698
+ parsed = JSON.parse(raw);
671
699
  } catch {
672
- return {};
700
+ throw new Error(
701
+ "The target deck contains invalid JSON; refusing to overwrite it.",
702
+ );
703
+ }
704
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
705
+ throw new Error(
706
+ "The target deck data is invalid; refusing to overwrite it.",
707
+ );
673
708
  }
709
+ return parsed as Record<string, unknown>;
674
710
  }
675
711
 
676
712
  function stripTags(html: string): string {