@agent-native/core-corpus 0.219.0 → 0.220.0-nightly-20261023183804

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 (211) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  3. package/corpus/templates/analytics/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  4. package/corpus/templates/assets/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  5. package/corpus/templates/assets/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  6. package/corpus/templates/brain/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  7. package/corpus/templates/brain/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  8. package/corpus/templates/calendar/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  9. package/corpus/templates/calendar/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  10. package/corpus/templates/chat/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  11. package/corpus/templates/clips/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  12. package/corpus/templates/clips/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  13. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +146 -14
  14. package/corpus/templates/clips/app/routes/record.tsx +11 -0
  15. package/corpus/templates/clips/desktop/src/app.tsx +74 -16
  16. package/corpus/templates/clips/desktop/src/lib/recorder.ts +12 -4
  17. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +54 -0
  18. package/corpus/templates/clips/desktop/src/lib/url.ts +7 -1
  19. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +50 -2
  20. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +1 -0
  21. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +95 -0
  22. package/corpus/templates/community/qr/package.json +1 -1
  23. package/corpus/templates/content/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  24. package/corpus/templates/content/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  25. package/corpus/templates/content/actions/_content-save-outcomes.ts +1 -0
  26. package/corpus/templates/content/actions/_database-utils.ts +49 -1
  27. package/corpus/templates/content/actions/_document-edit-mutation.ts +273 -0
  28. package/corpus/templates/content/actions/_document-flush.ts +13 -0
  29. package/corpus/templates/content/actions/_github-source.ts +664 -267
  30. package/corpus/templates/content/actions/_move-page-to-space.ts +5 -0
  31. package/corpus/templates/content/actions/_property-utils.ts +4 -2
  32. package/corpus/templates/content/actions/add-comment.ts +56 -29
  33. package/corpus/templates/content/actions/attach-content-database-source.ts +29 -14
  34. package/corpus/templates/content/actions/bind-content-database-source-field.ts +18 -5
  35. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +12 -5
  36. package/corpus/templates/content/actions/change-content-database-source-role.ts +13 -7
  37. package/corpus/templates/content/actions/create-content-database.ts +11 -4
  38. package/corpus/templates/content/actions/delete-document.ts +8 -0
  39. package/corpus/templates/content/actions/disconnect-content-database-source.ts +18 -5
  40. package/corpus/templates/content/actions/duplicate-database-item.ts +13 -7
  41. package/corpus/templates/content/actions/duplicate-database-items.ts +28 -22
  42. package/corpus/templates/content/actions/edit-document.ts +86 -2
  43. package/corpus/templates/content/actions/execute-builder-source-execution.ts +13 -5
  44. package/corpus/templates/content/actions/get-content-database.ts +30 -13
  45. package/corpus/templates/content/actions/materialize-builder-required-fields.ts +9 -3
  46. package/corpus/templates/content/actions/move-database-item.ts +12 -6
  47. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +8 -4
  48. package/corpus/templates/content/actions/preview-content-database-source-attach.ts +6 -3
  49. package/corpus/templates/content/actions/query-content-database-items.ts +24 -5
  50. package/corpus/templates/content/actions/remove-database-items.ts +19 -10
  51. package/corpus/templates/content/actions/restore-document-version.ts +302 -63
  52. package/corpus/templates/content/actions/review-content-database-source-change-set.ts +11 -4
  53. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +11 -4
  54. package/corpus/templates/content/actions/set-image-alt-text.ts +10 -2
  55. package/corpus/templates/content/actions/set-live-body-switch.ts +4 -1
  56. package/corpus/templates/content/actions/stage-builder-revision.ts +10 -4
  57. package/corpus/templates/content/actions/suggest-document-edit.ts +256 -15
  58. package/corpus/templates/content/actions/transcribe-media.ts +16 -2
  59. package/corpus/templates/content/actions/update-content-database-view.ts +8 -2
  60. package/corpus/templates/content/actions/update-document.ts +1 -1
  61. package/corpus/templates/content/actions/validate-builder-source-execution.ts +11 -4
  62. package/corpus/templates/content/app/components/editor/CommentComposer.tsx +142 -2
  63. package/corpus/templates/content/app/components/editor/CommentEntry.tsx +30 -5
  64. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +707 -283
  65. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +38 -40
  66. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +2 -2
  67. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +27 -4
  68. package/corpus/templates/content/app/components/editor/SuggestionReviewCard.tsx +264 -0
  69. package/corpus/templates/content/app/components/editor/SuggestionReviewCardSkeleton.tsx +13 -0
  70. package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +3 -2
  71. package/corpus/templates/content/app/components/editor/block-field-comment-threads.ts +3 -2
  72. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +16 -18
  73. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +128 -0
  74. package/corpus/templates/content/app/components/editor/database/navigation-state.ts +10 -3
  75. package/corpus/templates/content/app/components/editor/database/settings.tsx +8 -8
  76. package/corpus/templates/content/app/components/editor/discussion-panel.tsx +504 -0
  77. package/corpus/templates/content/app/components/layout/ContentStartupShell.tsx +12 -0
  78. package/corpus/templates/content/app/discussion-messages.ts +270 -0
  79. package/corpus/templates/content/app/hooks/use-comments.ts +6 -0
  80. package/corpus/templates/content/app/hooks/use-content-database.ts +114 -110
  81. package/corpus/templates/content/app/hooks/use-github-folder.ts +15 -5
  82. package/corpus/templates/content/app/i18n-data.ts +17 -0
  83. package/corpus/templates/content/app/lib/content-database-query-cache.ts +60 -0
  84. package/corpus/templates/content/app/root.tsx +23 -8
  85. package/corpus/templates/content/app/routes/suggestions.$documentId.tsx +79 -0
  86. package/corpus/templates/content/changelog/2026-10-21-agents-suggested-edits-show-inline-in-mcp-apps-hosts-like-cl.md +6 -0
  87. package/corpus/templates/content/changelog/2026-10-21-comments-panel-is-now-discussion.md +6 -0
  88. package/corpus/templates/content/changelog/2026-10-22-a-big-github-folder-fills-its-collection-much-faster-and-kee.md +6 -0
  89. package/corpus/templates/content/changelog/2026-10-22-collections-with-large-select-properties-open-faster-and-use.md +6 -0
  90. package/corpus/templates/content/changelog/2026-10-22-pressing-escape-while-writing-a-comment-reply-or-edit-cancel.md +6 -0
  91. package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +7 -7
  92. package/corpus/templates/content/docs/product/capabilities/content.discussion.page.md +43 -28
  93. package/corpus/templates/content/docs/product/encyclopedia.md +7 -7
  94. package/corpus/templates/content/e2e/helpers.ts +1 -1
  95. package/corpus/templates/content/server/db/schema.ts +25 -0
  96. package/corpus/templates/content/server/lib/comment-ai.ts +13 -17
  97. package/corpus/templates/content/server/lib/comment-anchor-follow.ts +107 -0
  98. package/corpus/templates/content/server/lib/comment-notifications.ts +3 -3
  99. package/corpus/templates/content/server/lib/document-body-steps.ts +182 -0
  100. package/corpus/templates/content/server/lib/document-history.ts +44 -0
  101. package/corpus/templates/content/server/lib/document-title-propagation.ts +16 -3
  102. package/corpus/templates/content/server/lib/github-source-client.ts +100 -12
  103. package/corpus/templates/content/server/lib/live-body-writer.ts +38 -11
  104. package/corpus/templates/content/server/lib/private-icon-references.ts +4 -2
  105. package/corpus/templates/content/server/lib/suggested-edits.ts +361 -355
  106. package/corpus/templates/content/server/lib/suggestion-live-accept.ts +284 -0
  107. package/corpus/templates/content/server/lib/suggestion-rebaseline.ts +237 -0
  108. package/corpus/templates/content/server/live-copy/anchor-text.ts +48 -0
  109. package/corpus/templates/content/server/live-copy/apply-suggestion-edit.ts +528 -0
  110. package/corpus/templates/content/server/live-copy/binding.ts +3 -0
  111. package/corpus/templates/content/server/live-copy/persist.ts +94 -30
  112. package/corpus/templates/content/server/live-copy/rebase.ts +330 -0
  113. package/corpus/templates/content/server/live-copy/switch.ts +5 -1
  114. package/corpus/templates/content/server/live-copy/task-names.ts +1 -0
  115. package/corpus/templates/content/server/live-copy/tasks.ts +160 -6
  116. package/corpus/templates/content/server/live-copy/write-cause.ts +9 -0
  117. package/corpus/templates/content/server/live-copy/write.ts +146 -3
  118. package/corpus/templates/content/server/plugins/db.ts +16 -0
  119. package/corpus/templates/content/shared/api.ts +59 -4
  120. package/corpus/templates/content/shared/comment-ai.ts +9 -0
  121. package/corpus/templates/content/shared/comment-reanchor.ts +148 -2
  122. package/corpus/templates/content/shared/database-row-properties.ts +172 -0
  123. package/corpus/templates/content/shared/suggestion-apply.ts +314 -0
  124. package/corpus/templates/content/shared/suggestion-link.ts +11 -0
  125. package/corpus/templates/content/shared/suggestion-rebase.ts +1 -1
  126. package/corpus/templates/content/shared/suggestion-rebaseline.ts +256 -0
  127. package/corpus/templates/content/vite.config.ts +1 -1
  128. package/corpus/templates/crm/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  129. package/corpus/templates/crm/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  130. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +46 -8
  131. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +12 -1
  132. package/corpus/templates/design/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  133. package/corpus/templates/design/.agents/skills/hubspot-email/SKILL.md +146 -0
  134. package/corpus/templates/design/.agents/skills/hubspot-email-review/SKILL.md +84 -0
  135. package/corpus/templates/design/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  136. package/corpus/templates/design/.generated/bridge/native-builtin-metadata.generated.ts +326 -0
  137. package/corpus/templates/design/.generated/bridge/native-worker-source.generated.ts +6 -0
  138. package/corpus/templates/design/AGENTS.md +22 -23
  139. package/corpus/templates/design/actions/create-file.ts +40 -4
  140. package/corpus/templates/design/actions/edit-design.ts +1 -1
  141. package/corpus/templates/design/actions/export-hubspot-html.ts +110 -0
  142. package/corpus/templates/design/actions/export-png.ts +42 -3
  143. package/corpus/templates/design/actions/generate-design.ts +84 -28
  144. package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
  145. package/corpus/templates/design/actions/get-design-surface-index.ts +1 -0
  146. package/corpus/templates/design/actions/take-design-screenshot.ts +217 -116
  147. package/corpus/templates/design/actions/update-file.ts +9 -0
  148. package/corpus/templates/design/actions/view-screen.ts +16 -1
  149. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +8 -1
  150. package/corpus/templates/design/app/components/design/EditPanel.tsx +1 -5
  151. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3 -3
  152. package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +13 -3
  153. package/corpus/templates/design/app/components/design/design-canvas/interact-document.ts +10 -3
  154. package/corpus/templates/design/app/i18n/en-US.ts +13 -0
  155. package/corpus/templates/design/app/pages/design-editor/commands/download-hubspot-html.ts +86 -0
  156. package/corpus/templates/design/app/pages/design-editor/design-editor-view.tsx +1 -0
  157. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-canvas-and-screens.ts +2 -1
  158. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-export-and-handoff.ts +18 -0
  159. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-screen-rendering.tsx +1 -0
  160. package/corpus/templates/design/app/pages/design-editor/domains/use-hubspot-html-export.ts +124 -0
  161. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +15 -0
  162. package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +12 -1
  163. package/corpus/templates/design/app/pages/design-editor/view/editor-dialogs.tsx +10 -0
  164. package/corpus/templates/design/app/pages/design-editor/view/export-submenu-content.tsx +11 -0
  165. package/corpus/templates/design/app/pages/design-editor/view/hubspot-email-conversion-dialog.tsx +54 -0
  166. package/corpus/templates/design/changelog/2026-10-22-create-hubspot-email-frames-rebuild-existing-frames-for-emai.md +6 -0
  167. package/corpus/templates/design/changelog/2026-10-22-hubspot-conversion-exports-can-verify-that-original-link-des.md +6 -0
  168. package/corpus/templates/design/changelog/2026-10-22-hubspot-emails-receive-an-independent-content-and-mobile-lay.md +6 -0
  169. package/corpus/templates/design/changelog/2026-10-22-hug-frames-expand-to-show-their-full-content-in-the-canvas-a.md +6 -0
  170. package/corpus/templates/design/changelog/2026-10-22-preview-hubspot-emails-with-sample-recipient-names-and-butto.md +6 -0
  171. package/corpus/templates/design/changelog/2026-10-22-screenshot-previews-use-local-chromium-when-available-for-fa.md +6 -0
  172. package/corpus/templates/design/changelog/2026-10-22-verification-and-export-requests-finish-without-requiring-un.md +6 -0
  173. package/corpus/templates/design/changelog/2026-10-23-external-stylesheet-image-export.md +6 -0
  174. package/corpus/templates/design/changelog/2026-10-23-html-motion-edits-preserve-interactive-alpine-visibility-sta.md +6 -0
  175. package/corpus/templates/design/changelog/2026-10-23-motion-editing-works-again-on-designs-that-load-tailwind-and.md +6 -0
  176. package/corpus/templates/design/changelog/2026-10-23-promo-artwork-uses-delivery-size-typography.md +6 -0
  177. package/corpus/templates/design/server/lib/design-response-guard.ts +29 -1
  178. package/corpus/templates/design/server/lib/email-css-properties.ts +128 -0
  179. package/corpus/templates/design/server/lib/email-css-values.ts +36 -0
  180. package/corpus/templates/design/server/lib/email-css-visibility.ts +72 -0
  181. package/corpus/templates/design/server/lib/email-image-bytes.ts +87 -0
  182. package/corpus/templates/design/server/lib/email-text-color.ts +85 -0
  183. package/corpus/templates/design/server/lib/hubl-interpolation.ts +63 -0
  184. package/corpus/templates/design/server/lib/hubspot-email-export.ts +1005 -0
  185. package/corpus/templates/design/server/lib/hubspot-email-preview.ts +153 -0
  186. package/corpus/templates/design/server/lib/playwright-runtime.ts +6 -6
  187. package/corpus/templates/design/server/lib/protect-hubl-tokens.ts +31 -0
  188. package/corpus/templates/design/server/lib/screenshot-readiness.ts +176 -0
  189. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  190. package/corpus/templates/design/server/source-workspace.ts +22 -0
  191. package/corpus/templates/design/shared/hubl-tokens.ts +55 -0
  192. package/corpus/templates/design/shared/hubspot-email-authoring.ts +35 -0
  193. package/corpus/templates/design/shared/hubspot-email.ts +100 -0
  194. package/corpus/templates/design/shared/motion-html-source.ts +547 -16
  195. package/corpus/templates/dispatch/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  196. package/corpus/templates/dispatch/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  197. package/corpus/templates/factory/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  198. package/corpus/templates/forms/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  199. package/corpus/templates/forms/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  200. package/corpus/templates/mail/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  201. package/corpus/templates/mail/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  202. package/corpus/templates/plan/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  203. package/corpus/templates/plan/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  204. package/corpus/templates/slides/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  205. package/corpus/templates/slides/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  206. package/corpus/templates/slides/scripts/edit-fidelity/run-options.ts +26 -0
  207. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +25 -4
  208. package/corpus/templates/slides-2/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  209. package/corpus/templates/slides-2/.deploy-tmp/agent-native-provided-plugins.mjs +10 -0
  210. package/corpus/templates/tasks/.agents/skills/external-agents/references/mcp-apps-embedding.md +12 -5
  211. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -32,4 +32,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core-corpus/corpu
32
32
 
33
33
  ## Generated Counts
34
34
 
35
- - template files: 14579
35
+ - template files: 14683
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
162
162
  concise structured content plus the link; it should not dump large catalogs or
163
163
  HTML.
164
164
 
165
- Compatibility target: build to the standard once, not per-client shims. MCP
166
- Apps-capable hosts should include Claude/Claude Desktop/Claude Code, ChatGPT
167
- custom MCP apps, VS Code GitHub Copilot, Goose, Postman, MCPJam, Cursor, and
168
- any future host that follows the extension negotiation. Host support varies by
169
- plan, release channel, and client version, so keep the deep link fallback.
165
+ Compatibility target: build to the standard once, not per-client shims.
166
+ Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
167
+ mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
168
+ Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
169
+ and T3 Code (in Codex threads). Desktop Code rendering is observed in
170
+ [widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
171
+ Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
172
+ [Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
173
+ and using it on the same connector can make inline apps disappear in Chat until
174
+ you reconnect the connector. Host support varies by plan, release channel, and client version,
175
+ so keep the deep link fallback. See the community-maintained
176
+ [MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","analytics-alert-jobs","analytics-rollup-backfill-jobs","auth","collab","config","content-metrics-collection-jobs","core-routes","creative-context","dashboard-report-jobs","db","feature-flags","labs","onboarding","otel","session-replay-retention-jobs","transactional-emails","uptime-monitor-jobs"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
162
162
  concise structured content plus the link; it should not dump large catalogs or
163
163
  HTML.
164
164
 
165
- Compatibility target: build to the standard once, not per-client shims. MCP
166
- Apps-capable hosts should include Claude/Claude Desktop/Claude Code, ChatGPT
167
- custom MCP apps, VS Code GitHub Copilot, Goose, Postman, MCPJam, Cursor, and
168
- any future host that follows the extension negotiation. Host support varies by
169
- plan, release channel, and client version, so keep the deep link fallback.
165
+ Compatibility target: build to the standard once, not per-client shims.
166
+ Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
167
+ mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
168
+ Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
169
+ and T3 Code (in Codex threads). Desktop Code rendering is observed in
170
+ [widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
171
+ Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
172
+ [Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
173
+ and using it on the same connector can make inline apps disappear in Chat until
174
+ you reconnect the connector. Host support varies by plan, release channel, and client version,
175
+ so keep the deep link fallback. See the community-maintained
176
+ [MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","creative-context","db","labs","onboarding","otel"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
162
162
  concise structured content plus the link; it should not dump large catalogs or
163
163
  HTML.
164
164
 
165
- Compatibility target: build to the standard once, not per-client shims. MCP
166
- Apps-capable hosts should include Claude/Claude Desktop/Claude Code, ChatGPT
167
- custom MCP apps, VS Code GitHub Copilot, Goose, Postman, MCPJam, Cursor, and
168
- any future host that follows the extension negotiation. Host support varies by
169
- plan, release channel, and client version, so keep the deep link fallback.
165
+ Compatibility target: build to the standard once, not per-client shims.
166
+ Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
167
+ mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
168
+ Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
169
+ and T3 Code (in Codex threads). Desktop Code rendering is observed in
170
+ [widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
171
+ Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
172
+ [Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
173
+ and using it on the same connector can make inline apps disappear in Chat until
174
+ you reconnect the connector. Host support varies by plan, release channel, and client version,
175
+ so keep the deep link fallback. See the community-maintained
176
+ [MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","brain-jobs","core-routes","db","otel"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
162
162
  concise structured content plus the link; it should not dump large catalogs or
163
163
  HTML.
164
164
 
165
- Compatibility target: build to the standard once, not per-client shims. MCP
166
- Apps-capable hosts should include Claude/Claude Desktop/Claude Code, ChatGPT
167
- custom MCP apps, VS Code GitHub Copilot, Goose, Postman, MCPJam, Cursor, and
168
- any future host that follows the extension negotiation. Host support varies by
169
- plan, release channel, and client version, so keep the deep link fallback.
165
+ Compatibility target: build to the standard once, not per-client shims.
166
+ Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
167
+ mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
168
+ Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
169
+ and T3 Code (in Codex threads). Desktop Code rendering is observed in
170
+ [widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
171
+ Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
172
+ [Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
173
+ and using it on the same connector can make inline apps disappear in Chat until
174
+ you reconnect the connector. Host support varies by plan, release channel, and client version,
175
+ so keep the deep link fallback. See the community-maintained
176
+ [MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","core-routes","db","event-rules","oauth-refresh","otel","transactional-emails"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","otel"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -162,8 +162,15 @@ actions for Forms, Content, Clips, Slides, and Design. The action should return
162
162
  concise structured content plus the link; it should not dump large catalogs or
163
163
  HTML.
164
164
 
165
- Compatibility target: build to the standard once, not per-client shims. MCP
166
- Apps-capable hosts should include Claude/Claude Desktop/Claude Code, ChatGPT
167
- custom MCP apps, VS Code GitHub Copilot, Goose, Postman, MCPJam, Cursor, and
168
- any future host that follows the extension negotiation. Host support varies by
169
- plan, release channel, and client version, so keep the deep link fallback.
165
+ Compatibility target: build to the standard once, not per-client shims.
166
+ Hosts that render MCP Apps inline today include Claude chat on web, desktop, and
167
+ mobile, plus the Claude Desktop Code tab; ChatGPT; VS Code GitHub Copilot (1.109+);
168
+ Microsoft 365 Copilot; Cursor (2.6+); Goose; Postman; MCPJam; Codex Desktop (26.616+);
169
+ and T3 Code (in Codex threads). Desktop Code rendering is observed in
170
+ [widget bug reports](https://github.com/anthropics/claude-code/issues/103809).
171
+ Treat the Claude Code terminal, Cowork, and Code on claude.ai web as link-out hosts.
172
+ [Cowork doesn't declare MCP Apps yet](https://github.com/anthropics/claude-code/issues/103924),
173
+ and using it on the same connector can make inline apps disappear in Chat until
174
+ you reconnect the connector. Host support varies by plan, release channel, and client version,
175
+ so keep the deep link fallback. See the community-maintained
176
+ [MCP client matrix](https://modelcontextprotocol.io/extensions/client-matrix).
@@ -0,0 +1,10 @@
1
+ // AUTO-GENERATED by @agent-native/core deploy build
2
+ import { markDefaultPluginProvided } from "@agent-native/core/server/edge";
3
+
4
+ const pluginStems = ["agent-chat","auth","calendar-jobs","config","core-routes","db","feature-flags","labs","onboarding","onboarding-calendar","otel","transactional-email-catalog"];
5
+
6
+ export default function markBuildDiscoveredPlugins(nitroApp) {
7
+ for (const stem of pluginStems) {
8
+ markDefaultPluginProvided(nitroApp, stem);
9
+ }
10
+ }
@@ -31,10 +31,11 @@ import {
31
31
  // success should finish this card's setup; a connection made long after the
32
32
  // cancel belongs to something else.
33
33
  const CANCELLED_SETUP_RECOVERY_MS = 60_000;
34
+ // The custom-keys form finishes in another tab with no signal back, so a
35
+ // status read on leaving or returning credits it only this long after it was
36
+ // opened; storage set up later belongs to something else.
37
+ const CUSTOM_KEYS_CREDIT_WINDOW_MS = 15 * 60_000;
34
38
 
35
- // The S3 path finishes in settings, outside this card, so only the choice is
36
- // recorded here; a later `recording_started.storage_connected` shows whether
37
- // it landed.
38
39
  function trackStorageStep(
39
40
  name:
40
41
  | "onboarding_step_viewed"
@@ -66,6 +67,8 @@ export interface StorageSetupCardProps {
66
67
  */
67
68
  recordingAttemptId?: string;
68
69
  outputId?: string;
70
+ /** Why the host is showing the card, recorded on every step event. */
71
+ setupReason?: string;
69
72
  }
70
73
 
71
74
  export function StorageSetupCard({
@@ -79,6 +82,7 @@ export function StorageSetupCard({
79
82
  connectFlow = "file_upload",
80
83
  recordingAttemptId,
81
84
  outputId,
85
+ setupReason,
82
86
  }: StorageSetupCardProps) {
83
87
  const t = useT();
84
88
  const storageSetupHref = useStorageSetupHref();
@@ -100,6 +104,9 @@ export function StorageSetupCard({
100
104
  const connectRequestedRef = useRef(false);
101
105
  const connectIntentExpiresAtRef = useRef<number | null>(null);
102
106
  const [stepViewId] = useState(createOnboardingCorrelationId);
107
+ // Fixed for the showing: the host's status can change while the card is up,
108
+ // and every event under one step_view_id must agree on why it showed.
109
+ const [viewSetupReason] = useState(setupReason);
103
110
  const stepProperties = useMemo(
104
111
  () => ({
105
112
  flow: connectSource,
@@ -108,8 +115,69 @@ export function StorageSetupCard({
108
115
  ? { recording_attempt_id: recordingAttemptId }
109
116
  : {}),
110
117
  ...(outputId ? { output_id: outputId } : {}),
118
+ ...(viewSetupReason ? { setup_reason: viewSetupReason } : {}),
111
119
  }),
112
- [connectSource, stepViewId, recordingAttemptId, outputId],
120
+ [connectSource, stepViewId, recordingAttemptId, outputId, viewSetupReason],
121
+ );
122
+ const stepPropertiesRef = useRef(stepProperties);
123
+ stepPropertiesRef.current = stepProperties;
124
+ const builderHandedOffRef = useRef(false);
125
+ const skippedRef = useRef(false);
126
+ const customKeysOpenedAtRef = useRef<number | null>(null);
127
+ const customKeysOpenedRecently = useCallback(() => {
128
+ const openedAt = customKeysOpenedAtRef.current;
129
+ return (
130
+ openedAt !== null && Date.now() - openedAt <= CUSTOM_KEYS_CREDIT_WINDOW_MS
131
+ );
132
+ }, []);
133
+ const completionTrackedRef = useRef(false);
134
+ const returnReadInFlightRef = useRef(false);
135
+ const storageBeforeSetupRef = useRef<Promise<
136
+ { status: VideoStorageStatus } | { error: unknown }
137
+ > | null>(null);
138
+ const readStorageBeforeSetup = useCallback(() => {
139
+ storageBeforeSetupRef.current ??= fetchVideoStorageStatus({
140
+ timeoutMs: 10_000,
141
+ }).then(
142
+ (status) => ({ status }),
143
+ (error: unknown) => ({ error }),
144
+ );
145
+ }, []);
146
+
147
+ // The route hosting this card can unmount it the moment the shared storage
148
+ // status reads configured, before this card's poll sees it, so the unmount
149
+ // path below reads the status once more and records through this too.
150
+ // Only a change to the workspace's own storage completes this step: free
151
+ // hosted clips also read as configured, and own storage that was already
152
+ // there at the showing's first setup click was not set up by this card.
153
+ // The read of storage before setup must start before any setup request
154
+ // can, since one-request account activation can finish before a read sent
155
+ // with it.
156
+ const trackCompletedIfConfigured = useCallback(
157
+ (status: VideoStorageStatus) => {
158
+ const before = storageBeforeSetupRef.current;
159
+ if (status.mode !== "own" || !before || completionTrackedRef.current) {
160
+ return;
161
+ }
162
+ // Claimed before the wait, so reads landing together record once.
163
+ completionTrackedRef.current = true;
164
+ void before.then((read) => {
165
+ if ("error" in read) {
166
+ console.warn(
167
+ "[storage-setup] storage before setup could not be read:",
168
+ read.error,
169
+ );
170
+ return;
171
+ }
172
+ if (read.status.mode === "own" || skippedRef.current) return;
173
+ const methodId = status.activeProvider?.id;
174
+ trackStorageStep("onboarding_step_completed", {
175
+ ...stepPropertiesRef.current,
176
+ ...(methodId ? { method_id: methodId } : {}),
177
+ });
178
+ });
179
+ },
180
+ [],
113
181
  );
114
182
 
115
183
  const clearConfiguredTimeout = useCallback(() => {
@@ -177,10 +245,7 @@ export function StorageSetupCard({
177
245
  stop();
178
246
  setConnecting(false);
179
247
  setConnected(true);
180
- trackStorageStep("onboarding_step_completed", {
181
- ...stepProperties,
182
- method_id: "builder",
183
- });
248
+ trackCompletedIfConfigured(status);
184
249
  clearConfiguredTimeout();
185
250
  configuredTimeoutRef.current = setTimeout(() => {
186
251
  configuredTimeoutRef.current = null;
@@ -211,9 +276,9 @@ export function StorageSetupCard({
211
276
  }, [
212
277
  clearConfiguredTimeout,
213
278
  onConfigured,
214
- stepProperties,
215
279
  stopVisibilityHandler,
216
280
  t,
281
+ trackCompletedIfConfigured,
217
282
  ]);
218
283
 
219
284
  const handleBuilderConnected = useCallback(() => {
@@ -222,6 +287,7 @@ export function StorageSetupCard({
222
287
  connectRequestedRef.current = false;
223
288
  connectIntentExpiresAtRef.current = null;
224
289
  if (expiresAt !== null && Date.now() > expiresAt) return;
290
+ builderHandedOffRef.current = true;
225
291
  startFileUploadPoll();
226
292
  }, [startFileUploadPoll]);
227
293
 
@@ -269,6 +335,7 @@ export function StorageSetupCard({
269
335
  setStatusIssue("connect-not-allowed");
270
336
  return;
271
337
  }
338
+ readStorageBeforeSetup();
272
339
  connectRequestedRef.current = true;
273
340
  connectIntentExpiresAtRef.current = null;
274
341
  setStatusIssue(null);
@@ -281,7 +348,12 @@ export function StorageSetupCard({
281
348
  scope: builderConnectionScope,
282
349
  });
283
350
  },
284
- [builderConnectionScope, builderConnect.start, stepProperties],
351
+ [
352
+ builderConnectionScope,
353
+ builderConnect.start,
354
+ readStorageBeforeSetup,
355
+ stepProperties,
356
+ ],
285
357
  );
286
358
  const handleBuilderCancel = useCallback(() => {
287
359
  connectIntentExpiresAtRef.current =
@@ -290,6 +362,7 @@ export function StorageSetupCard({
290
362
  }, [builderConnect.cancel]);
291
363
  const handleSkip = useCallback(() => {
292
364
  clearConfiguredTimeout();
365
+ skippedRef.current = true;
293
366
  trackStorageStep("onboarding_step_skipped", {
294
367
  ...stepProperties,
295
368
  });
@@ -307,8 +380,41 @@ export function StorageSetupCard({
307
380
  )
308
381
  : null;
309
382
  const builderConnecting = builderConnect.connecting;
383
+ const builderConnectingRef = useRef(builderConnecting);
384
+ builderConnectingRef.current = builderConnecting;
310
385
  const actionConnecting = connecting || builderConnecting;
311
386
 
387
+ // The custom-keys form opens in another tab, and some routes keep this card
388
+ // mounted after it saves, so a return to this tab reads the status too.
389
+ useEffect(() => {
390
+ const readOnReturn = () => {
391
+ if (
392
+ document.hidden ||
393
+ !customKeysOpenedRecently() ||
394
+ completionTrackedRef.current ||
395
+ skippedRef.current ||
396
+ returnReadInFlightRef.current
397
+ ) {
398
+ return;
399
+ }
400
+ returnReadInFlightRef.current = true;
401
+ void fetchVideoStorageStatus({ timeoutMs: 10_000 })
402
+ .then(trackCompletedIfConfigured)
403
+ .catch((err) => {
404
+ console.warn("[storage-setup] completion check failed:", err);
405
+ })
406
+ .finally(() => {
407
+ returnReadInFlightRef.current = false;
408
+ });
409
+ };
410
+ window.addEventListener("focus", readOnReturn);
411
+ document.addEventListener("visibilitychange", readOnReturn);
412
+ return () => {
413
+ window.removeEventListener("focus", readOnReturn);
414
+ document.removeEventListener("visibilitychange", readOnReturn);
415
+ };
416
+ }, [customKeysOpenedRecently, trackCompletedIfConfigured]);
417
+
312
418
  useEffect(() => {
313
419
  trackStorageStep("onboarding_step_viewed", {
314
420
  ...stepProperties,
@@ -319,6 +425,24 @@ export function StorageSetupCard({
319
425
  mountedRef.current = true;
320
426
  return () => {
321
427
  mountedRef.current = false;
428
+ // The route can unmount this card mid-setup: while the connect flow is
429
+ // still connecting, or after this card accepted the connect but before
430
+ // its storage poll saw it. After a cancel the flow keeps connecting
431
+ // through its popup-closed grace, so crediting then relies on that grace
432
+ // staying shorter than CANCELLED_SETUP_RECOVERY_MS.
433
+ const setupPending =
434
+ !completionTrackedRef.current &&
435
+ !skippedRef.current &&
436
+ (builderHandedOffRef.current ||
437
+ builderConnectingRef.current ||
438
+ customKeysOpenedRecently());
439
+ if (setupPending) {
440
+ void fetchVideoStorageStatus({ timeoutMs: 10_000 })
441
+ .then(trackCompletedIfConfigured)
442
+ .catch((err) => {
443
+ console.warn("[storage-setup] completion check failed:", err);
444
+ });
445
+ }
322
446
  clearConfiguredTimeout();
323
447
  if (pollRef.current) {
324
448
  clearInterval(pollRef.current);
@@ -326,7 +450,12 @@ export function StorageSetupCard({
326
450
  }
327
451
  stopVisibilityHandler();
328
452
  };
329
- }, [clearConfiguredTimeout, stopVisibilityHandler]);
453
+ }, [
454
+ clearConfiguredTimeout,
455
+ customKeysOpenedRecently,
456
+ stopVisibilityHandler,
457
+ trackCompletedIfConfigured,
458
+ ]);
330
459
 
331
460
  return (
332
461
  <div className="mx-auto flex w-full max-w-md flex-col gap-5 rounded-2xl border border-border bg-card p-6 shadow-lg">
@@ -357,6 +486,7 @@ export function StorageSetupCard({
357
486
  cancel: undefined,
358
487
  }}
359
488
  onConnect={handleBuilderConnect}
489
+ onTriggerClick={readStorageBeforeSetup}
360
490
  >
361
491
  <Button
362
492
  type="button"
@@ -379,12 +509,14 @@ export function StorageSetupCard({
379
509
  href={storageSetupHref}
380
510
  target="_blank"
381
511
  rel="noopener noreferrer"
382
- onClick={() =>
512
+ onClick={() => {
513
+ readStorageBeforeSetup();
514
+ customKeysOpenedAtRef.current = Date.now();
383
515
  trackStorageStep("onboarding_method_clicked", {
384
516
  ...stepProperties,
385
517
  method_id: "s3",
386
- })
387
- }
518
+ });
519
+ }}
388
520
  >
389
521
  {t("storageSetup.customKeys")}
390
522
  </a>
@@ -1279,6 +1279,16 @@ export function shouldRedirectToStorageSetupHome({
1279
1279
  );
1280
1280
  }
1281
1281
 
1282
+ export function storageSetupReason(
1283
+ status: Pick<VideoStorageStatus, "denyReason"> | undefined,
1284
+ ): string {
1285
+ if (!status) return "status_unavailable";
1286
+ // "disabled" means free hosted clips are off, not that a hosted clip was denied.
1287
+ return status.denyReason && status.denyReason !== "disabled"
1288
+ ? `hosted_${status.denyReason}`
1289
+ : "storage_required";
1290
+ }
1291
+
1282
1292
  export function shouldShowFirstRunStorageSetup({
1283
1293
  storageConfigured,
1284
1294
  storageStatusUnavailable,
@@ -4187,6 +4197,7 @@ export default function RecordRoute() {
4187
4197
  showBuilderStorageOffer={!hostedAllowanceExhausted}
4188
4198
  connectSource="clips_record_storage_setup_card"
4189
4199
  connectFlow="record"
4200
+ setupReason={storageSetupReason(storageQuery.data)}
4190
4201
  />
4191
4202
  </div>
4192
4203
  ) : showStorageStatusUnavailable ? (
@@ -230,7 +230,9 @@ import {
230
230
  import {
231
231
  RECORDING_SERVER_UNAVAILABLE,
232
232
  RECORDING_SESSION_EXPIRED,
233
+ buildVideoStorageSetupUrl,
233
234
  isStorageSetupFailureMessage,
235
+ onStorageSetupRequired,
234
236
  } from "./lib/recording-request";
235
237
  import {
236
238
  listenForRecordingShortcutStopAcks,
@@ -1392,6 +1394,8 @@ export function App({
1392
1394
  );
1393
1395
  }, [videoStorageIdentity, serverUrl, signedInAs]);
1394
1396
 
1397
+ const videoStorageProbeSeqRef = useRef(0);
1398
+
1395
1399
  const refreshVideoStorageStatus = useCallback(async () => {
1396
1400
  if (authStatus !== "authed") {
1397
1401
  hostedClipLimitMsRef.current = null;
@@ -1400,11 +1404,15 @@ export function App({
1400
1404
  }
1401
1405
 
1402
1406
  const probedIdentity = videoStorageIdentity;
1407
+ // Probes are slow enough to overlap; only the newest result may land, or a
1408
+ // pre-connect "missing" overwrites the "configured" that came after it.
1409
+ const probeSeq = ++videoStorageProbeSeqRef.current;
1403
1410
  const { probe, maxClipDurationMs } = await hasConfiguredVideoStorage(
1404
1411
  serverUrl,
1405
1412
  signedInAs,
1406
1413
  );
1407
1414
  if (videoStorageIdentityRef.current !== probedIdentity) return false;
1415
+ if (videoStorageProbeSeqRef.current !== probeSeq) return false;
1408
1416
  if (probe === "unavailable") {
1409
1417
  setVideoStorageStatus("unavailable");
1410
1418
  return false;
@@ -1433,8 +1441,10 @@ export function App({
1433
1441
  });
1434
1442
  };
1435
1443
  const interval = window.setInterval(tick, 5000);
1444
+ // Showing the popover always re-probes, even past an in-flight probe: the
1445
+ // newest probe wins.
1436
1446
  const onVisibilityChange = () => {
1437
- if (!document.hidden) tick();
1447
+ if (!document.hidden) void refreshVideoStorageStatus();
1438
1448
  };
1439
1449
  document.addEventListener("visibilitychange", onVisibilityChange);
1440
1450
  return () => {
@@ -3855,6 +3865,7 @@ export function App({
3855
3865
  ) => {
3856
3866
  if (recordFirstUploading || !recordFirstKey || !signedInAs) return;
3857
3867
  if (videoStorageStatus !== "configured") {
3868
+ setRecError(STORAGE_SETUP_HELP_TEXT);
3858
3869
  openVideoStorageSetup();
3859
3870
  return;
3860
3871
  }
@@ -4147,19 +4158,62 @@ export function App({
4147
4158
  const openVideoStorageSetup = useCallback(
4148
4159
  (targetServerUrl?: string) => {
4149
4160
  const base = (targetServerUrl?.trim() || serverUrl).replace(/\/+$/, "");
4150
- setRecError(STORAGE_SETUP_HELP_TEXT);
4151
- void openExternal(`${base}/record?connectStorage=1`).catch((err) => {
4161
+ void openExternal(buildVideoStorageSetupUrl(base)).catch((err) => {
4152
4162
  setRecError(
4153
4163
  err instanceof Error
4154
4164
  ? err.message
4155
4165
  : "Could not open Clips storage setup.",
4156
4166
  );
4157
4167
  });
4158
- void refreshVideoStorageStatus();
4159
4168
  },
4160
- [refreshVideoStorageStatus, serverUrl],
4169
+ [serverUrl],
4170
+ );
4171
+
4172
+ // Drops the "missing" banner until the next probe. A probe already in flight
4173
+ // would land after this and restore it, so it is invalidated too.
4174
+ const forgetMissingVideoStorage = useCallback(() => {
4175
+ videoStorageProbeSeqRef.current += 1;
4176
+ setRecError((message) =>
4177
+ isStorageSetupFailureMessage(message) ? null : message,
4178
+ );
4179
+ setVideoStorageStatus((status) =>
4180
+ status === "missing" ? "checking" : status,
4181
+ );
4182
+ }, []);
4183
+
4184
+ // The banner goes away on click; reopening the popover re-probes, and the
4185
+ // banner returns only if storage is still missing.
4186
+ const connectVideoStorage = useCallback(() => {
4187
+ forgetMissingVideoStorage();
4188
+ openVideoStorageSetup();
4189
+ }, [forgetMissingVideoStorage, openVideoStorageSetup]);
4190
+
4191
+ useEffect(
4192
+ () =>
4193
+ onStorageSetupRequired(() => {
4194
+ setRecError(STORAGE_SETUP_HELP_TEXT);
4195
+ invoke("show_popover").catch(() => {});
4196
+ void refreshVideoStorageStatus();
4197
+ }),
4198
+ [refreshVideoStorageStatus],
4161
4199
  );
4162
4200
 
4201
+ useEffect(() => {
4202
+ if (videoStorageStatus !== "configured") return;
4203
+ setRecError((message) =>
4204
+ isStorageSetupFailureMessage(message) ? null : message,
4205
+ );
4206
+ }, [videoStorageStatus]);
4207
+
4208
+ useEffect(() => {
4209
+ const onVisibilityChange = () => {
4210
+ if (document.hidden) forgetMissingVideoStorage();
4211
+ };
4212
+ document.addEventListener("visibilitychange", onVisibilityChange);
4213
+ return () =>
4214
+ document.removeEventListener("visibilitychange", onVisibilityChange);
4215
+ }, [forgetMissingVideoStorage]);
4216
+
4163
4217
  async function handleStartRecording(options?: {
4164
4218
  resumeCapture?: RestartHandoff;
4165
4219
  }): Promise<RecorderHandle | null> {
@@ -4877,14 +4931,14 @@ export function App({
4877
4931
  delete next[key];
4878
4932
  return next;
4879
4933
  });
4880
- void openExternal(
4881
- `${targetServerUrl.replace(/\/+$/, "")}/record?connectStorage=1`,
4882
- ).catch((error) => {
4883
- setRecoveryActionErrors((errors) => ({
4884
- ...errors,
4885
- [key]: error instanceof Error ? error.message : String(error),
4886
- }));
4887
- });
4934
+ void openExternal(buildVideoStorageSetupUrl(targetServerUrl)).catch(
4935
+ (error) => {
4936
+ setRecoveryActionErrors((errors) => ({
4937
+ ...errors,
4938
+ [key]: error instanceof Error ? error.message : String(error),
4939
+ }));
4940
+ },
4941
+ );
4888
4942
  },
4889
4943
  };
4890
4944
  const pendingUploadBanner = shouldShowRecordingRecoveryBanner(
@@ -5475,9 +5529,7 @@ export function App({
5475
5529
  onRetry={() => beginRecording()}
5476
5530
  />
5477
5531
  ) : isStorageSetupFailureMessage(recError) ? (
5478
- <StorageConnectionBanner
5479
- onConnect={() => openVideoStorageSetup()}
5480
- />
5532
+ <StorageConnectionBanner onConnect={connectVideoStorage} />
5481
5533
  ) : recError === RECORDING_SESSION_EXPIRED ? (
5482
5534
  <SessionExpiredBanner onReconnect={() => void reconnectSession()} />
5483
5535
  ) : recError === RECORDING_SERVER_UNAVAILABLE ? (
@@ -5486,6 +5538,12 @@ export function App({
5486
5538
  <div className="error-banner">{recError}</div>
5487
5539
  )
5488
5540
  ) : null}
5541
+ {!recError &&
5542
+ localRecordingMode === "off" &&
5543
+ authStatus === "authed" &&
5544
+ videoStorageStatus === "missing" ? (
5545
+ <StorageConnectionBanner onConnect={connectVideoStorage} />
5546
+ ) : null}
5489
5547
  {cameraError && !recError ? (
5490
5548
  cameraError === MACOS_CAPTURE_PERMISSION_MESSAGE ||
5491
5549
  cameraError === DESKTOP_CAPTURE_PERMISSION_MESSAGE ? (