@agent-native/core 0.102.1 → 0.103.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 (295) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/docs/content/local-file-mode.mdx +99 -489
  4. package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  5. package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
  6. package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  7. package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
  8. package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  9. package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
  10. package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  11. package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
  12. package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  13. package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
  14. package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  15. package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
  16. package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  17. package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
  18. package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  19. package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
  20. package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  21. package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
  22. package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  23. package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
  24. package/corpus/core/docs/content/template-content.mdx +26 -42
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/content-local.ts +86 -18
  27. package/corpus/core/src/cli/index.ts +2 -2
  28. package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
  29. package/corpus/core/src/cli/skills.ts +47 -13
  30. package/corpus/core/src/client/AgentPanel.tsx +7 -0
  31. package/corpus/core/src/client/AssistantChat.tsx +7 -0
  32. package/corpus/core/src/client/agent-chat-adapter.ts +7 -1
  33. package/corpus/core/src/client/chat/message-components.tsx +17 -3
  34. package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +96 -6
  35. package/corpus/core/src/client/composer/asset-picker-url.ts +80 -0
  36. package/corpus/core/src/client/error-format.ts +24 -0
  37. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +8 -1
  38. package/corpus/core/src/local-artifacts/index.ts +65 -0
  39. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  40. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  41. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  42. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  43. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  44. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  45. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  46. package/corpus/templates/assets/app/routes/library.tsx +69 -0
  47. package/corpus/templates/assets/changelog/2026-07-15-fixed-top-level-image-picker-selections-returning-to-the-com.md +6 -0
  48. package/corpus/templates/clips/actions/disconnect-calendar.ts +57 -9
  49. package/corpus/templates/clips/actions/generate-workflow.ts +102 -59
  50. package/corpus/templates/clips/actions/get-recording-player-data.ts +64 -1
  51. package/corpus/templates/clips/actions/regenerate-summary.ts +7 -0
  52. package/corpus/templates/clips/actions/regenerate-title.ts +29 -11
  53. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +34 -13
  54. package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +87 -9
  55. package/corpus/templates/clips/app/components/editor/trim-handles.tsx +15 -0
  56. package/corpus/templates/clips/app/components/editor/waveform.tsx +21 -8
  57. package/corpus/templates/clips/app/components/library/folder-tree.tsx +52 -0
  58. package/corpus/templates/clips/app/components/player/settings-panel.tsx +116 -58
  59. package/corpus/templates/clips/app/components/player/share-dialog.tsx +43 -37
  60. package/corpus/templates/clips/app/components/player/video-player.tsx +50 -2
  61. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  62. package/corpus/templates/clips/app/i18n/ar-SA.ts +5 -2
  63. package/corpus/templates/clips/app/i18n/de-DE.ts +6 -2
  64. package/corpus/templates/clips/app/i18n/en-US.ts +5 -2
  65. package/corpus/templates/clips/app/i18n/es-ES.ts +6 -2
  66. package/corpus/templates/clips/app/i18n/fr-FR.ts +5 -2
  67. package/corpus/templates/clips/app/i18n/hi-IN.ts +5 -2
  68. package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -1
  69. package/corpus/templates/clips/app/i18n/ko-KR.ts +5 -2
  70. package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -2
  71. package/corpus/templates/clips/app/i18n/zh-CN.ts +5 -2
  72. package/corpus/templates/clips/app/i18n/zh-TW.ts +5 -2
  73. package/corpus/templates/clips/app/lib/timestamp-mapping.ts +14 -0
  74. package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -2
  75. package/corpus/templates/clips/app/routes/_index.tsx +1 -2
  76. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +98 -32
  77. package/corpus/templates/clips/app/routes/share.$shareId.tsx +4 -1
  78. package/corpus/templates/clips/changelog/2026-07-15-ai-workflow-generation-now-stays-visible-avoids-duplicate-re.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-15-clip-playback-and-cta-controls-remain-interactive-through-ed.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-07-15-clip-titles-no-longer-describe-the-product-as-a-loom-alterna.md +6 -0
  81. package/corpus/templates/clips/changelog/2026-07-15-description-regeneration-now-opens-the-agent-chat-so-progres.md +6 -0
  82. package/corpus/templates/clips/changelog/2026-07-15-direct-recording-urls-now-respect-share-link-and-password-ac.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-07-15-disconnecting-a-calendar-now-clears-unrecorded-synced-meetin.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-07-15-folders-now-expose-rename-and-delete-actions-directly-in-the.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-07-15-recording-pages-now-explain-sign-in-access-and-avoid-showing.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-07-15-the-clips-extension-quick-actions-toolbar-can-be-moved-out-o.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-07-15-the-editor-now-skips-cut-sections-during-preview-and-keeps-t.md +6 -0
  88. package/corpus/templates/clips/changelog/2026-07-15-thumbnail-capture-now-updates-previews-while-scrubbing-and-l.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-07-15-windows-permission-links-open-the-matching-settings-page.md +6 -0
  90. package/corpus/templates/clips/chrome-extension/src/content-script.ts +117 -2
  91. package/corpus/templates/clips/chrome-extension/src/overlay.ts +27 -0
  92. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
  93. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +139 -101
  94. package/corpus/templates/clips/server/lib/recording-page-access.ts +35 -0
  95. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  96. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  97. package/corpus/templates/content/AGENTS.md +25 -30
  98. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  99. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  100. package/corpus/templates/content/actions/_content-files.ts +338 -0
  101. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  102. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  103. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  104. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  105. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  106. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  107. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  108. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  109. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  110. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  111. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  112. package/corpus/templates/content/actions/create-document.ts +65 -55
  113. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  114. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  115. package/corpus/templates/content/actions/delete-document.ts +7 -11
  116. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  117. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  118. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  119. package/corpus/templates/content/actions/edit-document.ts +2 -28
  120. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  121. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  122. package/corpus/templates/content/actions/get-document.ts +0 -14
  123. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  124. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  125. package/corpus/templates/content/actions/list-documents.ts +12 -9
  126. package/corpus/templates/content/actions/move-document.ts +3 -14
  127. package/corpus/templates/content/actions/pull-document.ts +0 -23
  128. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  129. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  130. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  131. package/corpus/templates/content/actions/search-documents.ts +0 -34
  132. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  133. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  134. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  135. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  136. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  137. package/corpus/templates/content/actions/update-document.ts +0 -15
  138. package/corpus/templates/content/actions/view-screen.ts +2 -50
  139. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  140. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  141. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  142. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  143. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  144. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  145. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  146. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  147. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  148. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  149. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  150. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  151. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  152. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  153. package/corpus/templates/content/app/i18n-data.ts +75 -0
  154. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  155. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  156. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  157. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  158. package/corpus/templates/content/parity/matrix.md +2 -1
  159. package/corpus/templates/content/parity/matrix.ts +37 -1
  160. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  161. package/corpus/templates/content/server/db/schema.ts +113 -40
  162. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  163. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  164. package/corpus/templates/content/server/plugins/db.ts +153 -0
  165. package/corpus/templates/content/shared/api.ts +17 -3
  166. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  167. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  168. package/corpus/templates/design/AGENTS.md +6 -1
  169. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  170. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  171. package/corpus/templates/design/actions/navigate.ts +2 -2
  172. package/corpus/templates/design/actions/view-screen.ts +15 -2
  173. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  174. package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +7 -3
  175. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  176. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  177. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  178. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  179. package/corpus/templates/design/app/i18n-data.ts +21 -2
  180. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  181. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  182. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  183. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  184. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  185. package/corpus/templates/design/changelog/2026-07-15-prototype-print-and-download-buttons-now-work-in-preview.md +6 -0
  186. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  187. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  188. package/corpus/templates/mail/AGENTS.md +23 -18
  189. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  190. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  191. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  192. package/corpus/templates/mail/actions/move-email.ts +30 -24
  193. package/corpus/templates/mail/actions/send-email.ts +73 -68
  194. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  195. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  196. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  197. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  198. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  199. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  200. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  201. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  202. package/corpus/templates/slides/actions/extract-pdf.ts +1 -1
  203. package/corpus/templates/slides/actions/import-file.ts +4 -1
  204. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
  205. package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +9 -12
  206. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -21
  207. package/corpus/templates/slides/app/i18n/ar-SA.ts +1 -0
  208. package/corpus/templates/slides/app/i18n/de-DE.ts +1 -0
  209. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  210. package/corpus/templates/slides/app/i18n/es-ES.ts +1 -0
  211. package/corpus/templates/slides/app/i18n/fr-FR.ts +1 -0
  212. package/corpus/templates/slides/app/i18n/hi-IN.ts +1 -0
  213. package/corpus/templates/slides/app/i18n/ja-JP.ts +1 -0
  214. package/corpus/templates/slides/app/i18n/ko-KR.ts +1 -0
  215. package/corpus/templates/slides/app/i18n/pt-BR.ts +1 -0
  216. package/corpus/templates/slides/app/i18n/zh-CN.ts +1 -0
  217. package/corpus/templates/slides/app/i18n/zh-TW.ts +1 -0
  218. package/corpus/templates/slides/changelog/2026-07-15-image-search-and-generation-controls-now-use-the-registered-.md +6 -0
  219. package/corpus/templates/slides/changelog/2026-07-15-pdf-deck-references-can-be-imported-for-slide-inspiration-ag.md +6 -0
  220. package/corpus/templates/slides/changelog/2026-07-15-stable-style-inspector-dock.md +6 -0
  221. package/dist/cli/content-local.d.ts +1 -0
  222. package/dist/cli/content-local.d.ts.map +1 -1
  223. package/dist/cli/content-local.js +75 -17
  224. package/dist/cli/content-local.js.map +1 -1
  225. package/dist/cli/index.js +2 -2
  226. package/dist/cli/index.js.map +1 -1
  227. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  228. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  229. package/dist/cli/skills-content/content-skill.js +36 -25
  230. package/dist/cli/skills-content/content-skill.js.map +1 -1
  231. package/dist/cli/skills.d.ts.map +1 -1
  232. package/dist/cli/skills.js +47 -14
  233. package/dist/cli/skills.js.map +1 -1
  234. package/dist/client/AgentPanel.d.ts.map +1 -1
  235. package/dist/client/AgentPanel.js +5 -0
  236. package/dist/client/AgentPanel.js.map +1 -1
  237. package/dist/client/AssistantChat.d.ts.map +1 -1
  238. package/dist/client/AssistantChat.js +7 -0
  239. package/dist/client/AssistantChat.js.map +1 -1
  240. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  241. package/dist/client/agent-chat-adapter.js +8 -1
  242. package/dist/client/agent-chat-adapter.js.map +1 -1
  243. package/dist/client/chat/message-components.d.ts +4 -0
  244. package/dist/client/chat/message-components.d.ts.map +1 -1
  245. package/dist/client/chat/message-components.js +10 -3
  246. package/dist/client/chat/message-components.js.map +1 -1
  247. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  248. package/dist/client/composer/ComposerPlusMenu.js +58 -6
  249. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  250. package/dist/client/composer/asset-picker-url.d.ts +16 -0
  251. package/dist/client/composer/asset-picker-url.d.ts.map +1 -0
  252. package/dist/client/composer/asset-picker-url.js +64 -0
  253. package/dist/client/composer/asset-picker-url.js.map +1 -0
  254. package/dist/client/error-format.d.ts.map +1 -1
  255. package/dist/client/error-format.js +17 -0
  256. package/dist/client/error-format.js.map +1 -1
  257. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  258. package/dist/client/resources/McpIntegrationDialog.js +6 -1
  259. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  260. package/dist/collab/awareness.d.ts +2 -2
  261. package/dist/collab/awareness.d.ts.map +1 -1
  262. package/dist/collab/struct-routes.d.ts +1 -1
  263. package/dist/local-artifacts/index.d.ts +10 -0
  264. package/dist/local-artifacts/index.d.ts.map +1 -1
  265. package/dist/local-artifacts/index.js +39 -0
  266. package/dist/local-artifacts/index.js.map +1 -1
  267. package/dist/notifications/routes.d.ts +1 -1
  268. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  269. package/dist/resources/handlers.d.ts +3 -3
  270. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  271. package/dist/server/transcribe-voice.d.ts +1 -1
  272. package/docs/content/local-file-mode.mdx +99 -489
  273. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  274. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  275. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  276. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  277. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  278. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  279. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  280. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  281. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  282. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  283. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  284. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  285. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  286. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  287. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  288. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  289. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  290. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  291. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  292. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  293. package/docs/content/template-content.mdx +26 -42
  294. package/package.json +1 -1
  295. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -83,6 +83,17 @@
83
83
  let bubbleDragLayer: HTMLDivElement | null = null;
84
84
  let bubblePersistTimer: ReturnType<typeof setTimeout> | undefined;
85
85
 
86
+ // The quick-actions toolbar is also draggable. Keep its geometry in the
87
+ // content script because the iframe cannot position itself on the host page.
88
+ const TOOLBAR_WIDTH = 68;
89
+ const TOOLBAR_COLLAPSED_HEIGHT = 154;
90
+ const toolbarGeom: { left: number | null; top: number | null } = {
91
+ left: null,
92
+ top: null,
93
+ };
94
+ let toolbarDragLayer: HTMLDivElement | null = null;
95
+ let toolbarPersistTimer: ReturnType<typeof setTimeout> | undefined;
96
+
86
97
  function bubbleSizePx(): number {
87
98
  return BUBBLE_SIZES[bubbleGeom.size] ?? BUBBLE_SIZES.lg;
88
99
  }
@@ -130,6 +141,92 @@
130
141
  }, 200);
131
142
  }
132
143
 
144
+ function clampToolbar(
145
+ left: number,
146
+ top: number,
147
+ height: number,
148
+ ): { left: number; top: number } {
149
+ return {
150
+ left: Math.max(8, Math.min(left, window.innerWidth - TOOLBAR_WIDTH - 8)),
151
+ top: Math.max(8, Math.min(top, window.innerHeight - height - 8)),
152
+ };
153
+ }
154
+
155
+ function applyToolbarGeom(): void {
156
+ const frame = document.getElementById(
157
+ partFrameId("toolbar"),
158
+ ) as HTMLIFrameElement | null;
159
+ if (!frame) return;
160
+ const height =
161
+ frame.getBoundingClientRect().height || TOOLBAR_COLLAPSED_HEIGHT;
162
+ const base = clampToolbar(
163
+ toolbarGeom.left ?? 16,
164
+ toolbarGeom.top ?? window.innerHeight / 2 - TOOLBAR_COLLAPSED_HEIGHT / 2,
165
+ height,
166
+ );
167
+ Object.assign(frame.style, {
168
+ left: `${base.left}px`,
169
+ top: `${base.top}px`,
170
+ right: "auto",
171
+ });
172
+ }
173
+
174
+ function persistToolbarGeom(): void {
175
+ clearTimeout(toolbarPersistTimer);
176
+ toolbarPersistTimer = setTimeout(() => {
177
+ try {
178
+ chrome.storage.local.set({ toolbarGeom });
179
+ } catch {
180
+ /* ignore */
181
+ }
182
+ }, 200);
183
+ }
184
+
185
+ function startToolbarDrag(): void {
186
+ if (toolbarDragLayer) return;
187
+ const frame = document.getElementById(
188
+ partFrameId("toolbar"),
189
+ ) as HTMLIFrameElement | null;
190
+ if (!frame) return;
191
+
192
+ const layer = document.createElement("div");
193
+ Object.assign(layer.style, {
194
+ position: "fixed",
195
+ inset: "0",
196
+ zIndex: "2147483647",
197
+ cursor: "grabbing",
198
+ });
199
+ (document.documentElement || document.body).appendChild(layer);
200
+ toolbarDragLayer = layer;
201
+
202
+ const onMove = (e: PointerEvent): void => {
203
+ const rect = frame.getBoundingClientRect();
204
+ const next = clampToolbar(
205
+ rect.left + e.movementX,
206
+ rect.top + e.movementY,
207
+ rect.height || TOOLBAR_COLLAPSED_HEIGHT,
208
+ );
209
+ toolbarGeom.left = next.left;
210
+ toolbarGeom.top = next.top;
211
+ Object.assign(frame.style, {
212
+ left: `${next.left}px`,
213
+ top: `${next.top}px`,
214
+ right: "auto",
215
+ });
216
+ };
217
+ const onUp = (): void => {
218
+ layer.removeEventListener("pointermove", onMove);
219
+ layer.removeEventListener("pointerup", onUp);
220
+ layer.removeEventListener("pointercancel", onUp);
221
+ layer.remove();
222
+ toolbarDragLayer = null;
223
+ persistToolbarGeom();
224
+ };
225
+ layer.addEventListener("pointermove", onMove);
226
+ layer.addEventListener("pointerup", onUp);
227
+ layer.addEventListener("pointercancel", onUp);
228
+ }
229
+
133
230
  function startBubbleDrag(): void {
134
231
  if (bubbleDragLayer) return;
135
232
  const frame = document.getElementById(
@@ -177,7 +274,7 @@
177
274
  }
178
275
 
179
276
  try {
180
- chrome.storage.local.get("bubbleGeom", (value) => {
277
+ chrome.storage.local.get(["bubbleGeom", "toolbarGeom"], (value) => {
181
278
  if (chrome.runtime.lastError) return;
182
279
  const g = value.bubbleGeom as
183
280
  | { size?: unknown; left?: unknown; top?: unknown }
@@ -188,12 +285,24 @@
188
285
  bubbleGeom.top = typeof g.top === "number" ? g.top : null;
189
286
  }
190
287
  applyBubbleGeom();
288
+ const toolbar = value.toolbarGeom as
289
+ | { left?: unknown; top?: unknown }
290
+ | undefined;
291
+ if (toolbar && typeof toolbar === "object") {
292
+ toolbarGeom.left =
293
+ typeof toolbar.left === "number" ? toolbar.left : null;
294
+ toolbarGeom.top = typeof toolbar.top === "number" ? toolbar.top : null;
295
+ }
296
+ applyToolbarGeom();
191
297
  });
192
298
  } catch {
193
299
  /* ignore */
194
300
  }
195
301
 
196
- window.addEventListener("resize", () => applyBubbleGeom());
302
+ window.addEventListener("resize", () => {
303
+ applyBubbleGeom();
304
+ applyToolbarGeom();
305
+ });
197
306
 
198
307
  function readCurrentParts(
199
308
  callback: (parts: OverlayPart[] | null) => void,
@@ -338,6 +447,7 @@
338
447
  styleFrame(frame, part);
339
448
  container.appendChild(frame);
340
449
  if (part === "bubble") applyBubbleGeom();
450
+ if (part === "toolbar") applyToolbarGeom();
341
451
  }
342
452
 
343
453
  // Camera-ready gating + connecting spinner: while the camera connects we show a
@@ -520,6 +630,7 @@
520
630
  const frame = document.getElementById(partFrameId("toolbar"));
521
631
  if (frame && typeof data.height === "number") {
522
632
  frame.style.height = `${Math.round(data.height)}px`;
633
+ applyToolbarGeom();
523
634
  }
524
635
  return;
525
636
  }
@@ -537,6 +648,10 @@
537
648
  startBubbleDrag();
538
649
  return;
539
650
  }
651
+ if (data.kind === "toolbar-drag-start") {
652
+ startToolbarDrag();
653
+ return;
654
+ }
540
655
  if (data.kind === "bubble-size") {
541
656
  bubbleGeom.size = data.size === "sm" ? "sm" : "lg";
542
657
  applyBubbleGeom();
@@ -84,6 +84,17 @@ function postToolbarSize(height: number): void {
84
84
  }
85
85
  }
86
86
 
87
+ function postToolbarDragStart(): void {
88
+ try {
89
+ window.parent.postMessage(
90
+ { source: "clips-overlay", kind: "toolbar-drag-start", part: "toolbar" },
91
+ "*",
92
+ );
93
+ } catch {
94
+ /* parent gone */
95
+ }
96
+ }
97
+
87
98
  function postCountdownFinished(): void {
88
99
  try {
89
100
  window.parent.postMessage(
@@ -406,6 +417,22 @@ function initCountdown(): void {
406
417
  function initToolbar(): void {
407
418
  const pill = document.createElement("div");
408
419
  pill.className = "toolbar-v";
420
+ pill.style.cursor = "grab";
421
+
422
+ // The content script owns iframe geometry, so ask it to capture the pointer
423
+ // page-wide when the user drags the toolbar outside its current bounds.
424
+ pill.addEventListener("pointerdown", (event) => {
425
+ if (event.button !== 0) return;
426
+ if ((event.target as HTMLElement).closest("button")) return;
427
+ event.preventDefault();
428
+ pill.style.cursor = "grabbing";
429
+ postToolbarDragStart();
430
+ const restore = (): void => {
431
+ pill.style.cursor = "grab";
432
+ window.removeEventListener("pointerup", restore);
433
+ };
434
+ window.addEventListener("pointerup", restore);
435
+ });
409
436
 
410
437
  const makeBtn = (
411
438
  cls: string,
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "plugins": {
42
42
  "shell": {
43
- "open": "(((mailto:\\w+)|(tel:\\w+)|(https?://\\w+)).+|(zoommtg://([A-Za-z0-9-]+\\.)*zoom\\.us/join\\?.+))"
43
+ "open": "(((mailto:\\w+)|(tel:\\w+)|(https?://\\w+)).+|(zoommtg://([A-Za-z0-9-]+\\.)*zoom\\.us/join\\?.+)|(ms-settings:[A-Za-z0-9._?=&-]+))"
44
44
  },
45
45
  "updater": {
46
46
  "active": true,
@@ -274,6 +274,27 @@ export function calendarEventToMeetingView(args: {
274
274
  };
275
275
  }
276
276
 
277
+ /**
278
+ * Acquire the calendar account's write lock inside a caller-owned transaction.
279
+ * Disconnect and materialization both take this lock before touching the
280
+ * account's calendar events, so cleanup cannot race a meeting insert.
281
+ */
282
+ export async function lockCalendarAccount(
283
+ db: any,
284
+ accountId: string,
285
+ ownerEmail?: string | null,
286
+ ): Promise<boolean> {
287
+ const ownerFilter = ownerEmail
288
+ ? eq(schema.calendarAccounts.ownerEmail, ownerEmail)
289
+ : isNull(schema.calendarAccounts.ownerEmail);
290
+ const locked = await db
291
+ .update(schema.calendarAccounts)
292
+ .set({ updatedAt: new Date().toISOString() })
293
+ .where(and(eq(schema.calendarAccounts.id, accountId), ownerFilter))
294
+ .returning({ id: schema.calendarAccounts.id });
295
+ return locked.length > 0;
296
+ }
297
+
277
298
  export async function fetchLiveCalendarEventFromId(virtualId: string) {
278
299
  const parsed = parseCalendarMeetingId(virtualId);
279
300
  if (!parsed) return null;
@@ -317,11 +338,14 @@ export async function fetchLiveCalendarEventFromId(virtualId: string) {
317
338
  }
318
339
  }
319
340
 
320
- export async function upsertCalendarEventSnapshot(args: {
321
- account: CalendarAccountForEvents;
322
- event: CalendarEvent;
323
- }) {
324
- const db = getDb();
341
+ export async function upsertCalendarEventSnapshot(
342
+ args: {
343
+ account: CalendarAccountForEvents;
344
+ event: CalendarEvent;
345
+ },
346
+ database: any = getDb(),
347
+ ) {
348
+ const db = database;
325
349
  const startIso = eventStartIso(args.event);
326
350
  const endIso = eventEndIso(args.event);
327
351
  if (!args.event.id || !startIso || !endIso) {
@@ -397,120 +421,134 @@ export async function materializeCalendarMeetingFromVirtualId(
397
421
  const live = await fetchLiveCalendarEventFromId(virtualId);
398
422
  if (!live) return null;
399
423
  const db = getDb();
400
- const snapshot = await upsertCalendarEventSnapshot(live);
401
-
402
- if (snapshot.meetingId) {
403
- const [existingMeeting] = await db
404
- .select()
405
- .from(schema.meetings)
406
- .where(eq(schema.meetings.id, snapshot.meetingId))
407
- .limit(1);
408
- if (existingMeeting) return { meeting: existingMeeting, created: false };
409
- }
410
-
411
424
  const ownerEmail = getCurrentOwnerEmail();
412
425
  const orgId = (await getActiveOrganizationId().catch(() => null)) ?? null;
413
426
  const joinUrl = pickJoinUrl(live.event);
414
427
  const meetingId = nanoid();
415
428
  const nowIso = new Date().toISOString();
416
429
 
417
- // Claim the calendar_events row atomically before inserting the meeting
418
- // row, so two concurrent materialize calls for the same event can't both
419
- // insert a `meetings` row (check-then-act TOCTOU). Only the caller whose
420
- // UPDATE actually matched a row (meetingId still NULL) proceeds to insert;
421
- // the loser re-reads and returns the winner's meeting instead.
422
- const claimed = await db
423
- .update(schema.calendarEvents)
424
- .set({ meetingId, updatedAt: nowIso })
425
- .where(
426
- and(
427
- eq(schema.calendarEvents.id, snapshot.id),
428
- isNull(schema.calendarEvents.meetingId),
429
- ),
430
+ return db.transaction(async (tx: any) => {
431
+ // Disconnect takes this same account-row write lock before it snapshots
432
+ // and deletes events. If disconnect wins, the account is gone by the time
433
+ // this transaction reaches the lock and materialization becomes a no-op.
434
+ if (
435
+ !(await lockCalendarAccount(tx, live.account.id, live.account.ownerEmail))
430
436
  )
431
- .returning({ id: schema.calendarEvents.id });
432
-
433
- if (!claimed.length) {
434
- // Someone else claimed it first — re-read and return their meeting.
435
- const [winnerEvent] = await db
436
- .select({ meetingId: schema.calendarEvents.meetingId })
437
- .from(schema.calendarEvents)
438
- .where(eq(schema.calendarEvents.id, snapshot.id))
439
- .limit(1);
440
- if (winnerEvent?.meetingId) {
441
- const [winnerMeeting] = await db
437
+ return null;
438
+
439
+ const snapshot = await upsertCalendarEventSnapshot(live, tx);
440
+
441
+ if (snapshot.meetingId) {
442
+ const [existingMeeting] = await tx
442
443
  .select()
443
444
  .from(schema.meetings)
444
- .where(eq(schema.meetings.id, winnerEvent.meetingId))
445
+ .where(eq(schema.meetings.id, snapshot.meetingId))
445
446
  .limit(1);
446
- if (winnerMeeting) return { meeting: winnerMeeting, created: false };
447
+ if (existingMeeting) {
448
+ return { meeting: existingMeeting, created: false };
449
+ }
447
450
  }
448
- return null;
449
- }
450
451
 
451
- try {
452
- await db.insert(schema.meetings).values({
453
- id: meetingId,
454
- organizationId: orgId ?? live.account.orgId ?? null,
455
- title: live.event.summary || "Untitled meeting",
456
- scheduledStart: eventStartIso(live.event),
457
- scheduledEnd: eventEndIso(live.event),
458
- actualStart: null,
459
- actualEnd: null,
460
- platform: detectPlatform(joinUrl),
461
- joinUrl: joinUrl ?? null,
462
- calendarEventId: snapshot.id,
463
- recordingId: null,
464
- transcriptStatus: "idle",
465
- summaryMd: "",
466
- bulletsJson: "[]",
467
- actionItemsJson: "[]",
468
- source: "calendar",
469
- reminderFiredAt: null,
470
- createdAt: nowIso,
471
- updatedAt: nowIso,
472
- ownerEmail,
473
- orgId,
474
- visibility: "private",
475
- } as any);
476
-
477
- const participants = calendarEventParticipants(live.event).filter(
478
- (p) => p.email,
479
- );
480
- if (participants.length) {
481
- await db.insert(schema.meetingParticipants).values(
482
- participants.map((p) => ({
483
- id: nanoid(),
484
- meetingId,
485
- email: p.email,
486
- name: p.name ?? null,
487
- isOrganizer: !!p.isOrganizer,
488
- attendedAt: null,
489
- createdAt: nowIso,
490
- })) as any,
491
- );
492
- }
493
- } catch (err) {
494
- // Roll back the claim so a future call can retry — but only if it still
495
- // points at our own meetingId (don't clobber a legitimate later claim).
496
- await db
452
+ // Claim the calendar_events row atomically before inserting the meeting
453
+ // row, so two concurrent materialize calls for the same event can't both
454
+ // insert a `meetings` row (check-then-act TOCTOU). Only the caller whose
455
+ // UPDATE actually matched a row (meetingId still NULL) proceeds to insert;
456
+ // the loser re-reads and returns the winner's meeting instead.
457
+ const claimed = await tx
497
458
  .update(schema.calendarEvents)
498
- .set({ meetingId: null, updatedAt: new Date().toISOString() })
459
+ .set({ meetingId, updatedAt: nowIso })
499
460
  .where(
500
461
  and(
501
462
  eq(schema.calendarEvents.id, snapshot.id),
502
- eq(schema.calendarEvents.meetingId, meetingId),
463
+ isNull(schema.calendarEvents.meetingId),
503
464
  ),
504
465
  )
505
- .catch(() => {});
506
- throw err;
507
- }
466
+ .returning({ id: schema.calendarEvents.id });
467
+
468
+ if (!claimed.length) {
469
+ // Someone else claimed it first — re-read and return the winner.
470
+ const [winnerEvent] = await tx
471
+ .select({ meetingId: schema.calendarEvents.meetingId })
472
+ .from(schema.calendarEvents)
473
+ .where(eq(schema.calendarEvents.id, snapshot.id))
474
+ .limit(1);
475
+ if (winnerEvent?.meetingId) {
476
+ const [winnerMeeting] = await tx
477
+ .select()
478
+ .from(schema.meetings)
479
+ .where(eq(schema.meetings.id, winnerEvent.meetingId))
480
+ .limit(1);
481
+ if (winnerMeeting) {
482
+ return { meeting: winnerMeeting, created: false };
483
+ }
484
+ }
485
+ return null;
486
+ }
508
487
 
509
- const [meeting] = await db
510
- .select()
511
- .from(schema.meetings)
512
- .where(eq(schema.meetings.id, meetingId))
513
- .limit(1);
488
+ try {
489
+ await tx.insert(schema.meetings).values({
490
+ id: meetingId,
491
+ organizationId: orgId ?? live.account.orgId ?? null,
492
+ title: live.event.summary || "Untitled meeting",
493
+ scheduledStart: eventStartIso(live.event),
494
+ scheduledEnd: eventEndIso(live.event),
495
+ actualStart: null,
496
+ actualEnd: null,
497
+ platform: detectPlatform(joinUrl),
498
+ joinUrl: joinUrl ?? null,
499
+ calendarEventId: snapshot.id,
500
+ recordingId: null,
501
+ transcriptStatus: "idle",
502
+ summaryMd: "",
503
+ bulletsJson: "[]",
504
+ actionItemsJson: "[]",
505
+ source: "calendar",
506
+ reminderFiredAt: null,
507
+ createdAt: nowIso,
508
+ updatedAt: nowIso,
509
+ ownerEmail,
510
+ orgId,
511
+ visibility: "private",
512
+ } as any);
513
+
514
+ const participants = calendarEventParticipants(live.event).filter(
515
+ (p) => p.email,
516
+ );
517
+ if (participants.length) {
518
+ await tx.insert(schema.meetingParticipants).values(
519
+ participants.map((p) => ({
520
+ id: nanoid(),
521
+ meetingId,
522
+ email: p.email,
523
+ name: p.name ?? null,
524
+ isOrganizer: !!p.isOrganizer,
525
+ attendedAt: null,
526
+ createdAt: nowIso,
527
+ })) as any,
528
+ );
529
+ }
530
+ } catch (err) {
531
+ // Roll back the claim so a future call can retry — but only if it still
532
+ // points at our own meetingId (don't clobber a legitimate later claim).
533
+ await tx
534
+ .update(schema.calendarEvents)
535
+ .set({ meetingId: null, updatedAt: new Date().toISOString() })
536
+ .where(
537
+ and(
538
+ eq(schema.calendarEvents.id, snapshot.id),
539
+ eq(schema.calendarEvents.meetingId, meetingId),
540
+ ),
541
+ )
542
+ .catch(() => {});
543
+ throw err;
544
+ }
514
545
 
515
- return { meeting, created: true };
546
+ const [meeting] = await tx
547
+ .select()
548
+ .from(schema.meetings)
549
+ .where(eq(schema.meetings.id, meetingId))
550
+ .limit(1);
551
+
552
+ return { meeting, created: true };
553
+ });
516
554
  }
@@ -0,0 +1,35 @@
1
+ export type RecordingPageAccessRole = "owner" | "admin" | "editor" | "viewer";
2
+
3
+ /**
4
+ * Match the public recording and video routes: only a finite expiry date can
5
+ * expire a recording, and the expiry is strict so the exact instant remains
6
+ * valid until time advances past it.
7
+ */
8
+ export function isRecordingExpired(
9
+ expiresAt: string | null | undefined,
10
+ now = Date.now(),
11
+ ): boolean {
12
+ if (!expiresAt) return false;
13
+
14
+ const expires = new Date(expiresAt).getTime();
15
+ return Number.isFinite(expires) && expires < now;
16
+ }
17
+
18
+ /**
19
+ * Decide whether an authenticated request may use the editor/player route.
20
+ * Public visibility is intentionally a share-link concern; a direct `/r/*`
21
+ * request must either be the owner or have an explicit share grant.
22
+ * Password-protected recordings stay on the password-aware share flow for
23
+ * every non-owner so the authenticated action cannot mint a bypass token.
24
+ */
25
+ export function canOpenDirectRecordingPage(input: {
26
+ role: RecordingPageAccessRole;
27
+ visibility: "private" | "org" | "public";
28
+ hasPassword: boolean;
29
+ hasExplicitShare: boolean;
30
+ }): boolean {
31
+ if (input.role === "owner") return true;
32
+ if (input.hasPassword) return false;
33
+ if (input.visibility === "public") return input.hasExplicitShare;
34
+ return true;
35
+ }
@@ -18,13 +18,16 @@ import {
18
18
  } from "@agent-native/core/secrets/crypto";
19
19
 
20
20
  /**
21
- * Encrypt a share password for storage. Empty / nullish clears it (returns
22
- * null), matching the prior `args.password ?? null` semantics.
21
+ * Encrypt a share password for storage. Empty / nullish / whitespace-only
22
+ * input clears it (returns null), matching the prior `args.password ?? null`
23
+ * semantics while also rejecting spaces-only "passwords". Real values are
24
+ * trimmed so accidental leading/trailing whitespace doesn't get baked into
25
+ * the stored password.
23
26
  */
24
27
  export function encryptSharePassword(
25
28
  raw: string | null | undefined,
26
29
  ): string | null {
27
- const value = typeof raw === "string" ? raw : "";
30
+ const value = typeof raw === "string" ? raw.trim() : "";
28
31
  if (!value) return null;
29
32
  return encryptSecretValue(value);
30
33
  }