@agent-native/core 0.141.6 → 0.144.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 (501) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/corpus/README.md +1 -1
  3. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
  4. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +11 -6
  5. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
  6. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +19 -0
  7. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +296 -0
  8. package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  9. package/corpus/templates/analytics/.agents/skills/turn-into-skill/SKILL.md +112 -0
  10. package/corpus/templates/analytics/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  11. package/corpus/templates/analytics/AGENTS.md +37 -40
  12. package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
  13. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +1 -1
  14. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +17 -22
  15. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
  16. package/corpus/templates/analytics/actions/navigate.ts +5 -6
  17. package/corpus/templates/analytics/actions/provider-api-register.ts +50 -0
  18. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +46 -13
  19. package/corpus/templates/analytics/actions/test-custom-api-connection.ts +106 -0
  20. package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
  21. package/corpus/templates/analytics/actions/view-screen.ts +0 -20
  22. package/corpus/templates/analytics/app/components/CustomApiCard.tsx +430 -0
  23. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  24. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
  25. package/corpus/templates/analytics/app/i18n-data.ts +682 -90
  26. package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +6 -0
  27. package/corpus/templates/analytics/app/lib/sql-query.ts +33 -6
  28. package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
  29. package/corpus/templates/analytics/app/pages/DataSources.tsx +4 -0
  30. package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
  31. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
  32. package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
  33. package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
  34. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  35. package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-load-reliably-during-background-data-sw.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-remain-responsive-while-historical-data.md +6 -0
  38. package/corpus/templates/analytics/changelog/2026-08-06-analytics-query-results-now-render-as-a-table-with-csv-downl.md +6 -0
  39. package/corpus/templates/analytics/changelog/2026-08-06-connect-public-https-custom-apis-and-hand-tested-endpoints-t.md +6 -0
  40. package/corpus/templates/analytics/changelog/2026-08-06-durable-agent-worker-memory.md +6 -0
  41. package/corpus/templates/analytics/changelog/2026-08-06-live-dashboard-panels-use-the-shared-query-timeout.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-08-06-on-demand-billing-is-available-as-a-native-dashboard-with-ta.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-08-06-visible-dashboard-names-are-unique.md +6 -0
  44. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
  45. package/corpus/templates/analytics/netlify.toml +7 -0
  46. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +4 -0
  47. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
  48. package/corpus/templates/analytics/server/db/index.ts +27 -0
  49. package/corpus/templates/analytics/server/db/schema.ts +10 -0
  50. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
  51. package/corpus/templates/analytics/server/lib/dashboards-store.ts +227 -60
  52. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1147 -0
  53. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +34 -15
  54. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +57 -4
  55. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +227 -1
  56. package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
  57. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -4
  58. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +1 -0
  59. package/corpus/templates/analytics/server/plugins/db.ts +52 -18
  60. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +36 -8
  61. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +16 -1
  62. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
  63. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +296 -0
  64. package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  65. package/corpus/templates/assets/.agents/skills/turn-into-skill/SKILL.md +112 -0
  66. package/corpus/templates/assets/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  67. package/corpus/templates/assets/AGENTS.md +1 -0
  68. package/corpus/templates/assets/actions/_helpers.ts +12 -0
  69. package/corpus/templates/assets/actions/generate-image.ts +9 -3
  70. package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
  71. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
  72. package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
  73. package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
  74. package/corpus/templates/assets/app/routes/settings.tsx +5 -0
  75. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  76. package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
  77. package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
  78. package/corpus/templates/assets/server/lib/generation.ts +58 -0
  79. package/corpus/templates/assets/shared/api.ts +12 -0
  80. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +296 -0
  81. package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  82. package/corpus/templates/brain/.agents/skills/turn-into-skill/SKILL.md +112 -0
  83. package/corpus/templates/brain/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  84. package/corpus/templates/brain/actions/list-connection-providers.ts +24 -3
  85. package/corpus/templates/brain/app/lib/dispatch-links.ts +44 -0
  86. package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
  87. package/corpus/templates/brain/app/routes/sources.tsx +15 -7
  88. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  89. package/corpus/templates/brain/app/routes.ts +1 -1
  90. package/corpus/templates/brain/changelog/2026-08-06-brain-provider-setup-links-open-dispatch-correctly-in-standa.md +6 -0
  91. package/corpus/templates/brain/server/plugins/agent-chat.ts +1 -0
  92. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +26 -0
  93. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +296 -0
  94. package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  95. package/corpus/templates/calendar/.agents/skills/turn-into-skill/SKILL.md +112 -0
  96. package/corpus/templates/calendar/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  97. package/corpus/templates/calendar/AGENTS.md +11 -32
  98. package/corpus/templates/calendar/actions/update-event.ts +107 -1
  99. package/corpus/templates/calendar/app/components/calendar/EventCalendarSelect.tsx +83 -0
  100. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +57 -0
  101. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +59 -73
  102. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
  103. package/corpus/templates/calendar/app/hooks/use-events.ts +4 -1
  104. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  105. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
  106. package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
  107. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  108. package/corpus/templates/calendar/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  109. package/corpus/templates/calendar/changelog/2026-08-06-existing-events-can-move-between-connected-google-account-ca.md +6 -0
  110. package/corpus/templates/calendar/server/lib/google-calendar.ts +101 -0
  111. package/corpus/templates/calendar/server/plugins/agent-chat.ts +7 -1
  112. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  113. package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  114. package/corpus/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  115. package/corpus/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  116. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  117. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  118. package/corpus/templates/chat/app/root.tsx +1 -1
  119. package/corpus/templates/chat/app/routes/agent.tsx +7 -7
  120. package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
  121. package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
  122. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  123. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +296 -0
  124. package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  125. package/corpus/templates/clips/.agents/skills/turn-into-skill/SKILL.md +112 -0
  126. package/corpus/templates/clips/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  127. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +9 -24
  128. package/corpus/templates/clips/app/components/editor/timeline-geometry.ts +15 -0
  129. package/corpus/templates/clips/app/components/editor/waveform.tsx +4 -17
  130. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  131. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +14 -3
  132. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
  133. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
  134. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
  135. package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
  136. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +28 -38
  137. package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -27
  138. package/corpus/templates/clips/changelog/2026-08-06-clips-recovers-from-stalled-recording-setup-without-requirin.md +6 -0
  139. package/corpus/templates/clips/changelog/2026-08-06-clips-starts-faster-after-a-deploy-or-idle-period-startup-no.md +6 -0
  140. package/corpus/templates/clips/changelog/2026-08-06-deleting-a-recording-returns-cleanly-to-the-library-and-fit-.md +6 -0
  141. package/corpus/templates/clips/changelog/2026-08-06-hide-clip-insights-from-viewers-without-analytics-access.md +6 -0
  142. package/corpus/templates/clips/desktop/src/app.tsx +48 -20
  143. package/corpus/templates/clips/desktop/src/lib/recorder.ts +275 -74
  144. package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +84 -0
  145. package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +14 -4
  146. package/corpus/templates/clips/server/plugins/db.ts +214 -26
  147. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +296 -0
  148. package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  149. package/corpus/templates/content/.agents/skills/turn-into-skill/SKILL.md +112 -0
  150. package/corpus/templates/content/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  151. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
  152. package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
  153. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  154. package/corpus/templates/content/e2e/global-setup.ts +1 -1
  155. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +296 -0
  156. package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  157. package/corpus/templates/crm/.agents/skills/turn-into-skill/SKILL.md +112 -0
  158. package/corpus/templates/crm/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  159. package/corpus/templates/crm/changelog/2026-08-06-crm-starts-faster-after-a-deploy-or-idle-period-one-time-dat.md +6 -0
  160. package/corpus/templates/crm/server/plugins/db.ts +11 -8
  161. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +36 -1
  162. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
  163. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +296 -0
  164. package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  165. package/corpus/templates/design/.agents/skills/turn-into-skill/SKILL.md +112 -0
  166. package/corpus/templates/design/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  167. package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
  168. package/corpus/templates/design/actions/import-from-url.ts +6 -5
  169. package/corpus/templates/design/app/components/design/LayersPanel.tsx +27 -18
  170. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
  171. package/corpus/templates/design/app/pages/Index.tsx +6 -2
  172. package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
  173. package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
  174. package/corpus/templates/design/app/routes/team.tsx +1 -1
  175. package/corpus/templates/design/changelog/2026-08-05-deleting-a-design-from-the-home-page-no-longer-leaves-the-ap.md +6 -0
  176. package/corpus/templates/design/changelog/2026-08-05-renaming-a-layer-now-focuses-its-name-field-automatically.md +6 -0
  177. package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
  178. package/corpus/templates/design/e2e/global-setup.ts +1 -1
  179. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  180. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +296 -0
  181. package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  182. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/SKILL.md +112 -0
  183. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  184. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  185. package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
  186. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  187. package/corpus/templates/dispatch/changelog/2026-08-06-ready-workspace-apps-stay-one-click-away-in-a-compact-dispat.md +6 -0
  188. package/corpus/templates/dispatch/changelog/2026-08-06-shared-vault-values-and-management-are-now-restricted-to-wor.md +6 -0
  189. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +296 -0
  190. package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  191. package/corpus/templates/factory/.agents/skills/turn-into-skill/SKILL.md +112 -0
  192. package/corpus/templates/factory/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  193. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
  194. package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
  195. package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
  196. package/corpus/templates/factory/app/routes/team.tsx +1 -1
  197. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +296 -0
  198. package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  199. package/corpus/templates/forms/.agents/skills/turn-into-skill/SKILL.md +112 -0
  200. package/corpus/templates/forms/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  201. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
  202. package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
  203. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  204. package/corpus/templates/forms/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  205. package/corpus/templates/forms/server/plugins/agent-chat.ts +4 -0
  206. package/corpus/templates/forms/shared/navigation.ts +1 -1
  207. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +296 -0
  208. package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  209. package/corpus/templates/macros/.agents/skills/turn-into-skill/SKILL.md +112 -0
  210. package/corpus/templates/macros/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  211. package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
  212. package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
  213. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  214. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +296 -0
  215. package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  216. package/corpus/templates/mail/.agents/skills/turn-into-skill/SKILL.md +112 -0
  217. package/corpus/templates/mail/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  218. package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
  219. package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
  220. package/corpus/templates/mail/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  221. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  222. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +296 -0
  223. package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  224. package/corpus/templates/plan/.agents/skills/turn-into-skill/SKILL.md +112 -0
  225. package/corpus/templates/plan/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  226. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  227. package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
  228. package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
  229. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  230. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +48 -5
  231. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +40 -2
  232. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +6 -0
  233. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +15 -2
  234. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +29 -24
  235. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +296 -0
  236. package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  237. package/corpus/templates/slides/.agents/skills/turn-into-skill/SKILL.md +112 -0
  238. package/corpus/templates/slides/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  239. package/corpus/templates/slides/AGENTS.md +7 -9
  240. package/corpus/templates/slides/_gitignore +1 -0
  241. package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
  242. package/corpus/templates/slides/actions/export-pptx.ts +402 -36
  243. package/corpus/templates/slides/actions/import-file.ts +46 -85
  244. package/corpus/templates/slides/actions/import-from-url.ts +6 -6
  245. package/corpus/templates/slides/actions/import-google-slides-reference.ts +271 -12
  246. package/corpus/templates/slides/actions/import-pptx.ts +85 -60
  247. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -13
  248. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
  249. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +5 -1
  250. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +25 -12
  251. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +112 -57
  252. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +202 -0
  253. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +26 -14
  254. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +176 -0
  255. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +182 -141
  256. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  257. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +18 -0
  258. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +85 -0
  259. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -0
  260. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +13 -3
  261. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +27 -1
  262. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
  263. package/corpus/templates/slides/app/global.css +6 -0
  264. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  265. package/corpus/templates/slides/app/lib/create-deck-generation.ts +10 -6
  266. package/corpus/templates/slides/app/lib/deck-preview-frame.ts +15 -0
  267. package/corpus/templates/slides/app/lib/deck-share-links.ts +9 -0
  268. package/corpus/templates/slides/app/lib/export-pptx-client.ts +89 -0
  269. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +3 -0
  270. package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
  271. package/corpus/templates/slides/app/pages/Index.tsx +107 -6
  272. package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
  273. package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
  274. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  275. package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
  276. package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
  277. package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
  278. package/corpus/templates/slides/changelog/2026-08-06-clicking-the-gray-canvas-around-a-slide-clears-its-selection.md +6 -0
  279. package/corpus/templates/slides/changelog/2026-08-06-deck-thumbnails-keep-a-consistent-listing-frame-across-aspec.md +6 -0
  280. package/corpus/templates/slides/changelog/2026-08-06-deep-linked-decks-open-directly-instead-of-being-interrupted.md +6 -0
  281. package/corpus/templates/slides/changelog/2026-08-06-google-slides-exports-now-show-clear-import-steps-when-direc.md +6 -0
  282. package/corpus/templates/slides/changelog/2026-08-06-import-pdf-google-slides-and-powerpoint-decks-directly-from-.md +6 -0
  283. package/corpus/templates/slides/changelog/2026-08-06-imported-powerpoint-exports-keep-text-and-images-editable-an.md +6 -0
  284. package/corpus/templates/slides/changelog/2026-08-06-presentation-prompts-continue-adding-rendered-slides-until-t.md +6 -0
  285. package/corpus/templates/slides/changelog/2026-08-06-public-decks-now-share-read-only-presentation-links-by-defau.md +6 -0
  286. package/corpus/templates/slides/changelog/2026-08-06-slides-offers-a-direct-connect-google-button-when-a-drive-co.md +6 -0
  287. package/corpus/templates/slides/changelog/2026-08-06-slides-preserve-uploaded-pdf-layouts-accept-larger-reference.md +6 -0
  288. package/corpus/templates/slides/changelog/2026-08-06-styled-words-stay-part-of-their-text-box-instead-of-becoming.md +6 -0
  289. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +7 -20
  290. package/corpus/templates/slides/server/handlers/import/html-converter.ts +236 -13
  291. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +67 -0
  292. package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +2 -0
  293. package/corpus/templates/slides/server/handlers/uploads.ts +7 -3
  294. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -3
  295. package/corpus/templates/slides/server/lib/emails.ts +7 -2
  296. package/corpus/templates/slides/server/lib/google-docs-error.ts +50 -0
  297. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +1 -0
  298. package/corpus/templates/slides/server/lib/import-asset-storage.ts +127 -0
  299. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -3
  300. package/corpus/templates/slides/server/routes/api/import-assets/[token].get.ts +70 -0
  301. package/corpus/templates/slides/shared/upload-types.ts +3 -0
  302. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +296 -0
  303. package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  304. package/corpus/templates/tasks/.agents/skills/turn-into-skill/SKILL.md +112 -0
  305. package/corpus/templates/tasks/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  306. package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
  307. package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
  308. package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
  309. package/dist/agent/engine/ai-sdk-engine.js +49 -3
  310. package/dist/agent/production-agent.d.ts +18 -0
  311. package/dist/agent/production-agent.js +80 -1
  312. package/dist/agent/types.d.ts +7 -0
  313. package/dist/cli/agent-plugin.d.ts +112 -0
  314. package/dist/cli/agent-plugin.js +1081 -0
  315. package/dist/cli/app-skill.js +39 -1
  316. package/dist/cli/code-agent-executor.d.ts +3 -0
  317. package/dist/cli/code-agent-executor.js +51 -13
  318. package/dist/cli/index.js +13 -0
  319. package/dist/cli/templates-meta.js +1 -1
  320. package/dist/client/AgentPanel.d.ts +3 -1
  321. package/dist/client/AgentPanel.js +9 -2
  322. package/dist/client/AssistantChat.d.ts +8 -0
  323. package/dist/client/AssistantChat.js +23 -8
  324. package/dist/client/NewWorkspaceAppFlow.js +1 -0
  325. package/dist/client/agent-chat-adapter.d.ts +1 -1
  326. package/dist/client/agent-chat-adapter.js +153 -7
  327. package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
  328. package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
  329. package/dist/client/assistant-ui-recovery.d.ts +2 -1
  330. package/dist/client/assistant-ui-recovery.js +7 -3
  331. package/dist/client/chat/run-recovery.js +5 -1
  332. package/dist/client/embed-auth.js +6 -2
  333. package/dist/client/error-format.js +12 -0
  334. package/dist/client/integrations/IntegrationsPanel.js +40 -6
  335. package/dist/client/onboarding/FirstRunOnboarding.js +11 -4
  336. package/dist/client/org/OrgSwitcher.js +2 -2
  337. package/dist/client/org/TeamPage.js +92 -72
  338. package/dist/client/require-session.d.ts +1 -1
  339. package/dist/client/require-session.js +4 -4
  340. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  341. package/dist/client/resources/McpConnectionSuggestion.js +27 -10
  342. package/dist/client/resources/McpIntegrationDialog.js +77 -45
  343. package/dist/client/resources/mcp-integration-catalog.d.ts +13 -1
  344. package/dist/client/resources/mcp-integration-catalog.js +255 -2
  345. package/dist/client/resources/mcp-integration-logos.js +18 -2
  346. package/dist/client/resources/use-mcp-servers.d.ts +5 -0
  347. package/dist/client/resources/use-mcp-servers.js +36 -0
  348. package/dist/client/settings/AccountSettingsCard.js +42 -36
  349. package/dist/client/settings/AutomationsSection.js +1 -1
  350. package/dist/client/settings/DemoModeSection.d.ts +3 -1
  351. package/dist/client/settings/DemoModeSection.js +4 -1
  352. package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
  353. package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
  354. package/dist/client/settings/SchedulingTimezoneField.js +9 -5
  355. package/dist/client/settings/SettingsPanel.d.ts +4 -1
  356. package/dist/client/settings/SettingsPanel.js +21 -21
  357. package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
  358. package/dist/client/settings/SettingsTabsPage.js +98 -45
  359. package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
  360. package/dist/client/settings/useSettingsPanelController.js +47 -8
  361. package/dist/collab/routes.d.ts +1 -1
  362. package/dist/db/client.d.ts +9 -0
  363. package/dist/db/client.js +51 -16
  364. package/dist/db/create-get-db.js +2 -2
  365. package/dist/db/ddl-guard.d.ts +11 -10
  366. package/dist/db/ddl-guard.js +40 -12
  367. package/dist/deploy/build.d.ts +2 -0
  368. package/dist/deploy/build.js +97 -4
  369. package/dist/deploy/workspace-deploy.js +0 -2
  370. package/dist/guards/no-env-credentials.js +9 -0
  371. package/dist/ingestion/index.d.ts +1 -1
  372. package/dist/ingestion/pptx.d.ts +68 -0
  373. package/dist/ingestion/pptx.js +660 -213
  374. package/dist/ingestion/website.d.ts +30 -0
  375. package/dist/integrations/plugin.js +9 -6
  376. package/dist/localization/default-messages.d.ts +59 -0
  377. package/dist/localization/default-messages.js +62 -2
  378. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  379. package/dist/mcp-client/oauth-routes.d.ts +2 -0
  380. package/dist/mcp-client/oauth-routes.js +74 -7
  381. package/dist/mcp-client/routes.d.ts +1 -0
  382. package/dist/mcp-client/routes.js +49 -0
  383. package/dist/navigation/index.js +16 -5
  384. package/dist/notifications/routes.d.ts +1 -1
  385. package/dist/observability/routes.d.ts +3 -3
  386. package/dist/onboarding/app-profile.js +11 -0
  387. package/dist/progress/routes.d.ts +1 -1
  388. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  389. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  390. package/dist/resources/store.js +15 -1
  391. package/dist/scripts/call-agent.js +45 -4
  392. package/dist/secrets/routes.d.ts +6 -6
  393. package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
  394. package/dist/server/agent-chat/action-filters-a2a.js +36 -1
  395. package/dist/server/agent-chat-plugin.js +29 -8
  396. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  397. package/dist/server/auth.js +55 -17
  398. package/dist/server/better-auth-instance.js +5 -3
  399. package/dist/server/core-routes-plugin.d.ts +8 -1
  400. package/dist/server/core-routes-plugin.js +28 -3
  401. package/dist/server/credential-provider.d.ts +3 -3
  402. package/dist/server/credential-provider.js +90 -4
  403. package/dist/server/identity-sso.js +2 -1
  404. package/dist/server/onboarding-html.d.ts +2 -0
  405. package/dist/server/onboarding-html.js +171 -62
  406. package/dist/server/realtime-token.d.ts +1 -1
  407. package/dist/server/self-dispatch.d.ts +5 -4
  408. package/dist/server/self-dispatch.js +25 -14
  409. package/dist/server/ssr-handler.d.ts +1 -1
  410. package/dist/server/ssr-handler.js +1 -1
  411. package/dist/server/workspace-provider-oauth.js +1 -1
  412. package/dist/setup-connections/onboarding.js +1 -1
  413. package/dist/shared/cache-control.d.ts +26 -2
  414. package/dist/shared/cache-control.js +43 -2
  415. package/dist/shared/index.d.ts +1 -1
  416. package/dist/shared/index.js +1 -1
  417. package/dist/shared/password-policy.d.ts +3 -0
  418. package/dist/shared/password-policy.js +3 -0
  419. package/dist/shared/sign-in-journey.d.ts +5 -2
  420. package/dist/shared/sign-in-journey.js +10 -4
  421. package/dist/sharing/actions/set-resource-visibility.js +16 -4
  422. package/dist/sharing/registry.d.ts +14 -0
  423. package/dist/styles/agent-native.css +6 -0
  424. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  425. package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  426. package/dist/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  427. package/dist/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  428. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  429. package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  430. package/dist/templates/chat/app/root.tsx +1 -1
  431. package/dist/templates/chat/app/routes/agent.tsx +7 -7
  432. package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
  433. package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
  434. package/dist/templates/chat/app/routes/team.tsx +1 -1
  435. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  436. package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  437. package/dist/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  438. package/dist/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  439. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  440. package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  441. package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  442. package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  443. package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  444. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  445. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  446. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  447. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  448. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  449. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  450. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  451. package/dist/templates/workspace-root/AGENTS.md +7 -0
  452. package/dist/user-profile/actions/change-password.js +8 -3
  453. package/dist/user-profile/actions/set-password.js +5 -1
  454. package/dist/vite/action-types-plugin.js +59 -0
  455. package/docs/content/authentication.mdx +4 -5
  456. package/docs/content/skills-guide.mdx +13 -0
  457. package/docs/content/template-analytics-developers.mdx +1 -1
  458. package/docs/content/template-calendar-developers.mdx +1 -1
  459. package/docs/content/template-chat-developers.mdx +2 -2
  460. package/docs/content/template-content-developers.mdx +1 -1
  461. package/docs/content/template-mail-developers.mdx +1 -1
  462. package/docs/content/template-plan-developers.mdx +1 -1
  463. package/docs/content/toolkit-command-navigation.mdx +6 -6
  464. package/docs/content/toolkit-org-team.mdx +1 -1
  465. package/docs/content/toolkit-settings.mdx +8 -8
  466. package/docs/content/toolkit-setup-connections.mdx +1 -1
  467. package/package.json +6 -2
  468. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  469. package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  470. package/src/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  471. package/src/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  472. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  473. package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  474. package/src/templates/chat/app/root.tsx +1 -1
  475. package/src/templates/chat/app/routes/agent.tsx +7 -7
  476. package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
  477. package/src/templates/chat/app/routes/settings.$.tsx +1 -0
  478. package/src/templates/chat/app/routes/team.tsx +1 -1
  479. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  480. package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  481. package/src/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  482. package/src/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  483. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  484. package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  485. package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  486. package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  487. package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  488. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  489. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  490. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  491. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  492. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  493. package/src/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  494. package/src/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  495. package/src/templates/workspace-root/AGENTS.md +7 -0
  496. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
  497. package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
  498. package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
  499. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
  500. package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
  501. package/src/templates/default/actions/navigate.spec.ts +0 -53
@@ -79,6 +79,12 @@ import {
79
79
  type PauseTransitionQueue,
80
80
  } from "./pause-transition";
81
81
  import { reconcileProcessingBackup } from "./processing-backup-recovery";
82
+ import {
83
+ guardRecordingStart,
84
+ RECORDING_START_TIMEOUT_MS,
85
+ RecordingStartCancelledError,
86
+ RecordingStartTimeoutError,
87
+ } from "./recording-start-guard";
82
88
  import { buildCaptureTitle, type CaptureTitleResult } from "./recording-title";
83
89
  import { prepareRewindRecordingStart } from "./rewind-recording-start";
84
90
  import { singleFlight } from "./single-flight";
@@ -151,6 +157,20 @@ const NO_SPEECH_TRANSCRIPT_FAILURE =
151
157
  const TRANSCRIPTION_START_FAILURE =
152
158
  "macOS Speech recognition could not start for this recording. Check Speech Recognition, System Audio, and Microphone permissions, then retry transcription.";
153
159
 
160
+ function throwIfRecordingStartAborted(signal?: AbortSignal): void {
161
+ if (signal?.aborted) throw new RecordingStartCancelledError();
162
+ }
163
+
164
+ function stopMediaStream(stream: MediaStream | null | undefined): void {
165
+ stream?.getTracks().forEach((track) => {
166
+ try {
167
+ track.stop();
168
+ } catch {
169
+ // coercion-ok: stopping an already-ended capture track is best-effort cleanup.
170
+ }
171
+ });
172
+ }
173
+
154
174
  function isMacPlatform(): boolean {
155
175
  if (typeof navigator === "undefined") return false;
156
176
  const platform =
@@ -212,6 +232,8 @@ export interface StartParams {
212
232
  cameraOn: boolean;
213
233
  /** Record + transcribe system/desktop audio. Default true. */
214
234
  systemAudioOn?: boolean;
235
+ /** Cancels or bounds the pre-record capture setup. */
236
+ signal?: AbortSignal;
215
237
  localRecordingMode?: LocalRecordingMode;
216
238
  /**
217
239
  * Pre-acquired camera stream owned by the popover's session effect. The
@@ -1507,6 +1529,7 @@ async function createServerRecording(
1507
1529
  requestStreaming?: boolean;
1508
1530
  streamingUploadClient?: StreamingUploadClient;
1509
1531
  visibility?: "public" | "private";
1532
+ signal?: AbortSignal;
1510
1533
  },
1511
1534
  ) {
1512
1535
  const url = `${serverUrl.replace(/\/+$/, "")}/_agent-native/actions/create-recording`;
@@ -1526,6 +1549,7 @@ async function createServerRecording(
1526
1549
  // requests without Allow-Credentials — and dev auth is bypassed, so
1527
1550
  // cookies aren't needed.
1528
1551
  credentials: "include",
1552
+ signal: options?.signal,
1529
1553
  body: JSON.stringify({
1530
1554
  hasCamera,
1531
1555
  hasAudio,
@@ -2087,7 +2111,10 @@ async function selectRegionForRecording(): Promise<RegionCaptureRect> {
2087
2111
  }
2088
2112
  }
2089
2113
 
2090
- async function prepareCountdownEventWaiter(timeoutMs = 4000): Promise<{
2114
+ async function prepareCountdownEventWaiter(
2115
+ timeoutMs = 4000,
2116
+ signal?: AbortSignal,
2117
+ ): Promise<{
2091
2118
  event: Promise<string>;
2092
2119
  cleanup: () => void;
2093
2120
  }> {
@@ -2117,6 +2144,7 @@ async function prepareCountdownEventWaiter(timeoutMs = 4000): Promise<{
2117
2144
 
2118
2145
  const cleanup = () => {
2119
2146
  window.removeEventListener("keydown", onKeyDown);
2147
+ signal?.removeEventListener("abort", onAbort);
2120
2148
  if (timer) {
2121
2149
  clearTimeout(timer);
2122
2150
  timer = null;
@@ -2139,6 +2167,12 @@ async function prepareCountdownEventWaiter(timeoutMs = 4000): Promise<{
2139
2167
  resolveEvent(cause);
2140
2168
  }
2141
2169
  };
2170
+ const onAbort = () => finish("cancel", "abort");
2171
+ if (signal?.aborted) {
2172
+ onAbort();
2173
+ } else {
2174
+ signal?.addEventListener("abort", onAbort, { once: true });
2175
+ }
2142
2176
 
2143
2177
  // Await both registrations before the countdown can become visible. A fast
2144
2178
  // Return previously closed the overlay before these asynchronous listeners
@@ -2190,14 +2224,19 @@ async function showRegionRecordBorder(region: RegionCaptureRect | null) {
2190
2224
  });
2191
2225
  }
2192
2226
 
2193
- async function runRecordingCountdown(wantsScreen: boolean) {
2227
+ async function runRecordingCountdown(
2228
+ wantsScreen: boolean,
2229
+ signal?: AbortSignal,
2230
+ ) {
2194
2231
  // The recording-start chime is intentionally NOT played here. It fires from
2195
2232
  // `audioCue.playBeforeCapture()` at the real capture-start (right before the
2196
2233
  // recorder/native capture is kicked off) so the beep lines up with the moment
2197
2234
  // recording actually begins — not one second early on the countdown's "1".
2198
2235
  const countdown = await prepareCountdownEventWaiter(
2199
2236
  COUNTDOWN_EVENT_TIMEOUT_MS,
2237
+ signal,
2200
2238
  );
2239
+ throwIfRecordingStartAborted(signal);
2201
2240
  await showRegionGuidesForRecording(wantsScreen);
2202
2241
  let countdownGeneration: number;
2203
2242
  try {
@@ -2504,6 +2543,7 @@ async function tryStartRewindFullscreenRecording(
2504
2543
  includeSystemAudio,
2505
2544
  { voiceProcessing: false },
2506
2545
  ).catch((err) => {
2546
+ if (params.signal?.aborted) throw err;
2507
2547
  console.warn("[clips-recorder] rewind transcription start failed:", err);
2508
2548
  return null;
2509
2549
  });
@@ -2518,7 +2558,9 @@ async function tryStartRewindFullscreenRecording(
2518
2558
  void saveTranscriptFailure(TRANSCRIPTION_START_FAILURE);
2519
2559
  }
2520
2560
  };
2521
- await invoke("show_preparing");
2561
+ await guardRecordingStart(invoke("show_preparing"), {
2562
+ signal: params.signal,
2563
+ });
2522
2564
  const recordingPromise = localOnly
2523
2565
  ? Promise.resolve<{ id: string; uploadMode: UploadMode }>({
2524
2566
  id: folderName,
@@ -2542,6 +2584,7 @@ async function tryStartRewindFullscreenRecording(
2542
2584
  // fail with 409 after the local encode has completed.
2543
2585
  requestStreaming: true,
2544
2586
  streamingUploadClient: "desktop-native",
2587
+ signal: params.signal,
2545
2588
  },
2546
2589
  );
2547
2590
  })();
@@ -2549,18 +2592,22 @@ async function tryStartRewindFullscreenRecording(
2549
2592
  const recording = await prepareRewindRecordingStart({
2550
2593
  async prepare() {
2551
2594
  const preparedRecording = await recordingPromise;
2595
+ throwIfRecordingStartAborted(params.signal);
2552
2596
  id = preparedRecording.id;
2553
2597
  uploadMode = preparedRecording.uploadMode ?? "buffered";
2554
- await invoke<RewindClipBackendStatus>("rewind_clip_prepare", {
2555
- artifactLabel: id,
2556
- serverUrl: localOnly ? null : params.serverUrl,
2557
- recordingId: localOnly ? null : id,
2558
- authToken: localOnly ? null : (params.authToken ?? ""),
2559
- cookie: localOnly ? null : (params.cookie ?? ""),
2560
- includeMic,
2561
- includeSystemAudio,
2562
- hasCamera: wantsCamera,
2563
- });
2598
+ await guardRecordingStart(
2599
+ invoke<RewindClipBackendStatus>("rewind_clip_prepare", {
2600
+ artifactLabel: id,
2601
+ serverUrl: localOnly ? null : params.serverUrl,
2602
+ recordingId: localOnly ? null : id,
2603
+ authToken: localOnly ? null : (params.authToken ?? ""),
2604
+ cookie: localOnly ? null : (params.cookie ?? ""),
2605
+ includeMic,
2606
+ includeSystemAudio,
2607
+ hasCamera: wantsCamera,
2608
+ }),
2609
+ { signal: params.signal },
2610
+ );
2564
2611
  return preparedRecording;
2565
2612
  },
2566
2613
  async countdown() {
@@ -2569,14 +2616,17 @@ async function tryStartRewindFullscreenRecording(
2569
2616
  // Overlap whisper startup with the countdown so the capture boundary
2570
2617
  // never waits on it, but have both settled before activation.
2571
2618
  await Promise.all([
2572
- runRecordingCountdown(true),
2619
+ runRecordingCountdown(true, params.signal),
2573
2620
  startRewindTranscription(),
2574
2621
  ]);
2575
2622
  console.log("[rewind-latency] countdown completed");
2576
2623
  },
2577
2624
  async activate(preparedRecording) {
2578
2625
  const activationStarted = performance.now();
2579
- await invoke<RewindClipBackendStatus>("rewind_clip_start");
2626
+ await guardRecordingStart(
2627
+ invoke<RewindClipBackendStatus>("rewind_clip_start"),
2628
+ { signal: params.signal },
2629
+ );
2580
2630
  console.log(
2581
2631
  `[rewind-latency] countdown completion to start acknowledgement ${Math.round(performance.now() - activationStarted)}ms`,
2582
2632
  );
@@ -2913,8 +2963,13 @@ async function tryStartRewindFullscreenRecording(
2913
2963
  console.error("[clips-recorder] Rewind handle.cancel() threw:", error);
2914
2964
  });
2915
2965
  }),
2966
+ listen("clips:toolbar-ready", () => {
2967
+ emit("clips:toolbar-enabled", !stopped).catch(() => {});
2968
+ emitState();
2969
+ }),
2916
2970
  ]);
2917
2971
  tickHandle = window.setInterval(emitState, 500);
2972
+ emit("clips:toolbar-sync").catch(() => {});
2918
2973
  emit("clips:toolbar-enabled", true).catch(() => {});
2919
2974
  emitState();
2920
2975
  return handle;
@@ -2965,14 +3020,28 @@ async function startNativeFullscreenRecording(
2965
3020
  };
2966
3021
  const startNativeTranscriptionBeforeRecording = async () => {
2967
3022
  if (localOnly || !canTranscribeLocally || transcriptionCapture) return;
2968
- transcriptionCapture = await startTranscriptionCapture(
3023
+ throwIfRecordingStartAborted(params.signal);
3024
+ transcriptionCapture = await guardRecordingStart(
3025
+ startTranscriptionCapture(
3026
+ {
3027
+ deviceId: params.micId,
3028
+ label: micDeviceLabel,
3029
+ },
3030
+ wantsSystemAudio,
3031
+ { voiceProcessing: false },
3032
+ ),
2969
3033
  {
2970
- deviceId: params.micId,
2971
- label: micDeviceLabel,
3034
+ signal: params.signal,
3035
+ onLateResolve: (capture) => {
3036
+ void capture?.cancel().catch(() => {});
3037
+ },
2972
3038
  },
2973
- wantsSystemAudio,
2974
- { voiceProcessing: false },
2975
3039
  );
3040
+ if (params.signal?.aborted) {
3041
+ await transcriptionCapture?.cancel().catch(() => {});
3042
+ transcriptionCapture = null;
3043
+ throw new RecordingStartCancelledError();
3044
+ }
2976
3045
  if (
2977
3046
  canTranscribeLocally &&
2978
3047
  !transcriptionCapture &&
@@ -2983,6 +3052,7 @@ async function startNativeFullscreenRecording(
2983
3052
  };
2984
3053
 
2985
3054
  try {
3055
+ throwIfRecordingStartAborted(params.signal);
2986
3056
  await invoke("park_popover_offscreen").catch(() => {});
2987
3057
  emit("clips:popover-visible", false).catch(() => {});
2988
3058
 
@@ -3033,22 +3103,34 @@ async function startNativeFullscreenRecording(
3033
3103
  // current device name.
3034
3104
  if (wantsAudio && params.micId) {
3035
3105
  try {
3036
- const probe = await navigator.mediaDevices.getUserMedia({
3037
- audio: {
3038
- deviceId: { exact: params.micId },
3039
- echoCancellation: false,
3040
- noiseSuppression: false,
3041
- autoGainControl: false,
3106
+ const probe = await guardRecordingStart(
3107
+ navigator.mediaDevices.getUserMedia({
3108
+ audio: {
3109
+ deviceId: { exact: params.micId },
3110
+ echoCancellation: false,
3111
+ noiseSuppression: false,
3112
+ autoGainControl: false,
3113
+ },
3114
+ video: false,
3115
+ }),
3116
+ {
3117
+ signal: params.signal,
3118
+ onLateResolve: stopMediaStream,
3042
3119
  },
3043
- video: false,
3044
- });
3120
+ );
3045
3121
  const liveLabel = probe.getAudioTracks()[0]?.label?.trim();
3046
- probe.getTracks().forEach((track) => track.stop());
3122
+ stopMediaStream(probe);
3047
3123
  if (liveLabel) micDeviceLabel = liveLabel;
3048
3124
  console.log(
3049
3125
  `[clips-recorder] mic resolve: id=${params.micId} storedLabel=${JSON.stringify(params.micLabel ?? null)} liveLabel=${JSON.stringify(liveLabel ?? null)} -> using=${JSON.stringify(micDeviceLabel)}`,
3050
3126
  );
3051
3127
  } catch (probeErr) {
3128
+ if (
3129
+ params.signal?.aborted ||
3130
+ probeErr instanceof RecordingStartTimeoutError
3131
+ ) {
3132
+ throw probeErr;
3133
+ }
3052
3134
  // Probe failed (rotated/stale deviceId, device unplugged, or denied) —
3053
3135
  // fall back to the stored label, which the Rust side name-matches.
3054
3136
  console.warn(
@@ -3070,17 +3152,26 @@ async function startNativeFullscreenRecording(
3070
3152
  // attach the recording output at the exact start moment. No-op when SCK is
3071
3153
  // unavailable — `begin` then does a normal immediate start.
3072
3154
  const warmMic = (recordingId: string) =>
3073
- invoke("native_fullscreen_recording_warm", {
3074
- recordingId,
3075
- ...captureAudioParams,
3076
- }).catch((err) => {
3155
+ guardRecordingStart(
3156
+ invoke("native_fullscreen_recording_warm", {
3157
+ recordingId,
3158
+ ...captureAudioParams,
3159
+ }),
3160
+ { signal: params.signal },
3161
+ ).catch((err) => {
3162
+ if (
3163
+ params.signal?.aborted ||
3164
+ err instanceof RecordingStartTimeoutError
3165
+ ) {
3166
+ throw err;
3167
+ }
3077
3168
  console.warn("[clips-recorder] mic warm failed:", err);
3078
3169
  });
3079
3170
  const clickStartedAt = Date.now();
3080
3171
  if (localOnly) {
3081
3172
  // Local recordings have no create-recording round-trip; still overlap
3082
3173
  // Whisper startup with countdown + deferred SCK warm.
3083
- const countdownPromise = runRecordingCountdown(true);
3174
+ const countdownPromise = runRecordingCountdown(true, params.signal);
3084
3175
  id = localFolderName;
3085
3176
  const warmPromise = (async () => {
3086
3177
  const warmStartedAt = Date.now();
@@ -3105,7 +3196,7 @@ async function startNativeFullscreenRecording(
3105
3196
  mode: params.mode,
3106
3197
  source: params.source,
3107
3198
  });
3108
- const countdownPromise = runRecordingCountdown(true);
3199
+ const countdownPromise = runRecordingCountdown(true, params.signal);
3109
3200
  const recordingPromise = (async () => {
3110
3201
  const captureTitle = await captureTitlePromise;
3111
3202
  const createStartedAt = Date.now();
@@ -3119,6 +3210,7 @@ async function startNativeFullscreenRecording(
3119
3210
  mimeType: NATIVE_FULLSCREEN_MIME_TYPE,
3120
3211
  requestStreaming: true,
3121
3212
  streamingUploadClient: "desktop-native",
3213
+ signal: params.signal,
3122
3214
  },
3123
3215
  );
3124
3216
  } finally {
@@ -3174,19 +3266,23 @@ async function startNativeFullscreenRecording(
3174
3266
  }
3175
3267
 
3176
3268
  await audioCue.playBeforeCapture();
3269
+ throwIfRecordingStartAborted(params.signal);
3177
3270
  // Phase 2: attach the recording output now that the mic is warm (or do a
3178
3271
  // normal immediate start if warming was skipped/failed). Transcription has
3179
3272
  // already been awaited above so AVAudioEngine won't reconfigure mid-write.
3180
3273
  const beginStartedAt = Date.now();
3181
- await invoke("native_fullscreen_recording_begin", {
3182
- recordingId: id,
3183
- ...captureAudioParams,
3184
- // Live-upload credentials are read on the Rust side from the shared
3185
- // meetings-watcher session; only signal whether this is a local-only
3186
- // recording (which never uploads to the server).
3187
- localOnly,
3188
- hasCamera: wantsCamera,
3189
- });
3274
+ await guardRecordingStart(
3275
+ invoke("native_fullscreen_recording_begin", {
3276
+ recordingId: id,
3277
+ ...captureAudioParams,
3278
+ // Live-upload credentials are read on the Rust side from the shared
3279
+ // meetings-watcher session; only signal whether this is a local-only
3280
+ // recording (which never uploads to the server).
3281
+ localOnly,
3282
+ hasCamera: wantsCamera,
3283
+ }),
3284
+ { signal: params.signal },
3285
+ );
3190
3286
  console.log(
3191
3287
  `[clips-recorder] native begin durationMs=${Date.now() - beginStartedAt} clickToLiveMs=${Date.now() - clickStartedAt}`,
3192
3288
  );
@@ -3705,10 +3801,15 @@ async function startNativeFullscreenRecording(
3705
3801
  console.error("[clips-recorder] native handle.cancel() threw:", err);
3706
3802
  });
3707
3803
  }),
3804
+ listen("clips:toolbar-ready", () => {
3805
+ emit("clips:toolbar-enabled", !stopped).catch(() => {});
3806
+ emitState();
3807
+ }),
3708
3808
  ]);
3709
3809
  stateUnlistens = toolbarUnlistens;
3710
3810
  tickHandle = setInterval(emitState, 500);
3711
3811
  startSegmentRotator();
3812
+ emit("clips:toolbar-sync").catch(() => {});
3712
3813
  emit("clips:toolbar-enabled", true).catch(() => {});
3713
3814
  emitState();
3714
3815
 
@@ -3816,8 +3917,28 @@ function bubbleSizeRatioForName(size: string | null | undefined): number {
3816
3917
  export async function startRecording(
3817
3918
  params: StartParams,
3818
3919
  ): Promise<RecorderHandle> {
3920
+ const startController = new AbortController();
3921
+ const cancelStartup = () => {
3922
+ startController.abort();
3923
+ void emit("clips:countdown-cancel").catch(() => {});
3924
+ void invoke("native_fullscreen_recording_cancel").catch(() => {});
3925
+ void invoke("hide_recording_chrome").catch(() => {});
3926
+ };
3927
+ const startPromise = startRecordingInner({
3928
+ ...params,
3929
+ signal: startController.signal,
3930
+ });
3819
3931
  try {
3820
- return await startRecordingInner(params);
3932
+ return await guardRecordingStart(startPromise, {
3933
+ signal: params.signal,
3934
+ timeoutMs: RECORDING_START_TIMEOUT_MS,
3935
+ onCancel: cancelStartup,
3936
+ onLateResolve: (handle) => {
3937
+ void handle.cancel().catch((err) => {
3938
+ console.warn("[clips-recorder] late start cleanup failed:", err);
3939
+ });
3940
+ },
3941
+ });
3821
3942
  } catch (err) {
3822
3943
  await invoke("hide_recording_chrome").catch(() => {});
3823
3944
  const e = err as { name?: string; message?: string } | null;
@@ -4028,21 +4149,72 @@ async function startRecordingInner(
4028
4149
  ? getAudioStreamWithFallback(params.micId, params.micLabel)
4029
4150
  : null;
4030
4151
 
4152
+ // getDisplayMedia can remain pending in a long-lived WebKit tray webview.
4153
+ // Track every stream that does arrive so cancel/timeout can stop it even
4154
+ // when another capture promise never settles.
4155
+ const pendingCaptureStreams = new Set<MediaStream>();
4156
+ const trackCapturePromise = (
4157
+ promise: Promise<MediaStream> | null,
4158
+ ): Promise<MediaStream> | null => {
4159
+ if (!promise) return null;
4160
+ return promise.then((stream) => {
4161
+ if (params.signal?.aborted) {
4162
+ stopMediaStream(stream);
4163
+ } else {
4164
+ pendingCaptureStreams.add(stream);
4165
+ }
4166
+ return stream;
4167
+ });
4168
+ };
4169
+ const trackedDisplayStreamPromise = trackCapturePromise(displayStreamPromise);
4170
+ const trackedCameraStreamPromise = trackCapturePromise(
4171
+ bubbleCameraStreamPromise,
4172
+ );
4173
+ const trackedAudioStreamPromise = trackCapturePromise(audioStreamPromise);
4174
+ const pendingCaptureAbort = () => {
4175
+ pendingCaptureStreams.forEach(stopMediaStream);
4176
+ pendingCaptureStreams.clear();
4177
+ };
4178
+ params.signal?.addEventListener("abort", pendingCaptureAbort, {
4179
+ once: true,
4180
+ });
4181
+ const clearPendingCaptureTracking = () => {
4182
+ params.signal?.removeEventListener("abort", pendingCaptureAbort);
4183
+ pendingCaptureStreams.clear();
4184
+ };
4185
+ let captureStartCleaned = false;
4186
+ const cleanupUnstartedCapture = () => {
4187
+ if (captureStartCleaned) return;
4188
+ captureStartCleaned = true;
4189
+ pendingCaptureAbort();
4190
+ clearPendingCaptureTracking();
4191
+ streamCleanups.forEach((cleanup) => cleanup());
4192
+ };
4193
+ const trackedCapturePromises = [
4194
+ trackedDisplayStreamPromise,
4195
+ trackedCameraStreamPromise,
4196
+ trackedAudioStreamPromise,
4197
+ ];
4198
+
4031
4199
  let captureSuspension: RewindCaptureSuspensionLease;
4032
4200
  try {
4033
- captureSuspension = await suspensionPromise;
4201
+ captureSuspension = await guardRecordingStart(suspensionPromise, {
4202
+ signal: params.signal,
4203
+ onLateResolve: (lease) => {
4204
+ if (!lease.leaseId) return;
4205
+ void invoke("rewind_capture_suspension_release", {
4206
+ leaseId: lease.leaseId,
4207
+ }).catch(() => {});
4208
+ },
4209
+ });
4034
4210
  } catch (err) {
4035
4211
  // A picker may already have resolved while IPC was in flight. Tear down
4036
4212
  // every possible stream before surfacing the fail-closed suspension error.
4037
- const streams = await Promise.allSettled([
4038
- displayStreamPromise,
4039
- bubbleCameraStreamPromise,
4040
- audioStreamPromise,
4041
- ]);
4042
- streams.forEach((result) => {
4043
- if (result.status === "fulfilled") {
4044
- result.value?.getTracks().forEach((track) => track.stop());
4045
- }
4213
+ cleanupUnstartedCapture();
4214
+ void Promise.allSettled(trackedCapturePromises).then((streams) => {
4215
+ streams.forEach((result) => {
4216
+ if (result.status === "fulfilled") stopMediaStream(result.value);
4217
+ });
4046
4218
  });
4047
4219
  throw err;
4048
4220
  }
@@ -4055,11 +4227,10 @@ async function startRecordingInner(
4055
4227
  // track that DID resolve, then re-throw the original error so the caller's
4056
4228
  // catch still sees `NotAllowedError` / `AbortError` as before.
4057
4229
  console.log("[clips-recorder] allSettled IN — streams dispatched");
4058
- const settled = await Promise.allSettled([
4059
- displayStreamPromise,
4060
- bubbleCameraStreamPromise,
4061
- audioStreamPromise,
4062
- ]);
4230
+ const settled = await guardRecordingStart(
4231
+ Promise.allSettled(trackedCapturePromises),
4232
+ { signal: params.signal },
4233
+ );
4063
4234
  console.log(
4064
4235
  "[clips-recorder] allSettled OUT — settled statuses:",
4065
4236
  settled.map((s) => s.status),
@@ -4235,7 +4406,10 @@ async function startRecordingInner(
4235
4406
  combined,
4236
4407
  });
4237
4408
 
4238
- const countdownPromise = runRecordingCountdown(wantsScreen);
4409
+ const countdownPromise = runRecordingCountdown(
4410
+ wantsScreen,
4411
+ params.signal,
4412
+ );
4239
4413
  const localExportPromise = prepareLocalRecordingExport(targets);
4240
4414
  let localExport: Awaited<ReturnType<typeof prepareLocalRecordingExport>>;
4241
4415
  try {
@@ -4244,13 +4418,7 @@ async function startRecordingInner(
4244
4418
  localExportPromise,
4245
4419
  ]);
4246
4420
  } catch (err) {
4247
- [displayStream, audioStream].forEach((stream) =>
4248
- stream?.getTracks().forEach((track) => track.stop()),
4249
- );
4250
- streamCleanups.forEach((cleanup) => cleanup());
4251
- if (!popoverOwnsCamera) {
4252
- bubbleCameraStream?.getTracks().forEach((track) => track.stop());
4253
- }
4421
+ cleanupUnstartedCapture();
4254
4422
  throw err;
4255
4423
  }
4256
4424
 
@@ -4268,7 +4436,10 @@ async function startRecordingInner(
4268
4436
  function emitState(paused: boolean) {
4269
4437
  const now = Date.now();
4270
4438
  const pausedNowMs = paused && pausedAt ? now - pausedAt : 0;
4271
- const elapsedMs = now - startedAt - accumulatedPauseMs - pausedNowMs;
4439
+ const elapsedMs =
4440
+ startedAt > 0
4441
+ ? now - startedAt - accumulatedPauseMs - pausedNowMs
4442
+ : 0;
4272
4443
  emit("clips:recorder-state", {
4273
4444
  paused,
4274
4445
  elapsedMs,
@@ -4299,8 +4470,15 @@ async function startRecordingInner(
4299
4470
  console.error("[clips-recorder] local handle.cancel() threw:", err);
4300
4471
  });
4301
4472
  }),
4473
+ listen("clips:toolbar-ready", () => {
4474
+ emit("clips:toolbar-enabled", startedAt > 0 && !stopped).catch(
4475
+ () => {},
4476
+ );
4477
+ emitState(pausedAt != null);
4478
+ }),
4302
4479
  ]);
4303
4480
  stateUnlistens = toolbarUnlistens;
4481
+ emit("clips:toolbar-sync").catch(() => {});
4304
4482
 
4305
4483
  await showRegionGuidesForRecording(wantsScreen);
4306
4484
  await audioCue.playBeforeCapture();
@@ -4413,7 +4591,12 @@ async function startRecordingInner(
4413
4591
  },
4414
4592
  };
4415
4593
 
4416
- return recorderWithCaptureSuspension(handle, releaseCaptureSuspension);
4594
+ const wrappedHandle = recorderWithCaptureSuspension(
4595
+ handle,
4596
+ releaseCaptureSuspension,
4597
+ );
4598
+ clearPendingCaptureTracking();
4599
+ return wrappedHandle;
4417
4600
  }
4418
4601
 
4419
4602
  const uploadPrimaryVideo = createUploadOptimizedVideoStream(primaryVideo);
@@ -4443,14 +4626,18 @@ async function startRecordingInner(
4443
4626
  console.log(
4444
4627
  "[clips-recorder] invoking show_countdown + createServerRecording",
4445
4628
  );
4446
- const countdownPromise = runRecordingCountdown(wantsScreen);
4629
+ const countdownPromise = runRecordingCountdown(wantsScreen, params.signal);
4447
4630
  console.time("[clips-recorder] createServerRecording duration");
4448
4631
  const recordingPromise = createServerRecording(
4449
4632
  params.serverUrl,
4450
4633
  wantsCamera,
4451
4634
  recordingAudio.tracks.length > 0,
4452
4635
  captureTitle,
4453
- { mimeType: mimeType || "video/webm", requestStreaming: true },
4636
+ {
4637
+ mimeType: mimeType || "video/webm",
4638
+ requestStreaming: true,
4639
+ signal: params.signal,
4640
+ },
4454
4641
  ).finally(() => {
4455
4642
  console.timeEnd("[clips-recorder] createServerRecording duration");
4456
4643
  });
@@ -4664,7 +4851,8 @@ async function startRecordingInner(
4664
4851
  function emitState(paused: boolean) {
4665
4852
  const now = Date.now();
4666
4853
  const pausedNowMs = paused && pausedAt ? now - pausedAt : 0;
4667
- const elapsedMs = now - startedAt - accumulatedPauseMs - pausedNowMs;
4854
+ const elapsedMs =
4855
+ startedAt > 0 ? now - startedAt - accumulatedPauseMs - pausedNowMs : 0;
4668
4856
  emit("clips:recorder-state", {
4669
4857
  paused,
4670
4858
  elapsedMs,
@@ -4716,8 +4904,15 @@ async function startRecordingInner(
4716
4904
  console.error("[clips-recorder] handle.cancel() threw:", err);
4717
4905
  });
4718
4906
  }),
4907
+ listen("clips:toolbar-ready", () => {
4908
+ emit("clips:toolbar-enabled", startedAt > 0 && !stopped).catch(
4909
+ () => {},
4910
+ );
4911
+ emitState(pausedAt != null);
4912
+ }),
4719
4913
  ]);
4720
4914
  stateUnlistens = toolbarUnlistens;
4915
+ emit("clips:toolbar-sync").catch(() => {});
4721
4916
 
4722
4917
  await showRegionGuidesForRecording(wantsScreen);
4723
4918
  await audioCue.playBeforeCapture();
@@ -5228,8 +5423,14 @@ async function startRecordingInner(
5228
5423
  },
5229
5424
  };
5230
5425
 
5231
- return recorderWithCaptureSuspension(handle, releaseCaptureSuspension);
5426
+ const wrappedHandle = recorderWithCaptureSuspension(
5427
+ handle,
5428
+ releaseCaptureSuspension,
5429
+ );
5430
+ clearPendingCaptureTracking();
5431
+ return wrappedHandle;
5232
5432
  } catch (err) {
5433
+ cleanupUnstartedCapture();
5233
5434
  await releaseCaptureSuspension();
5234
5435
  throw err;
5235
5436
  }