@agent-native/core 0.80.4 → 0.80.5

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 (292) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +21 -2
  3. package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -1
  5. package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -1
  7. package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -1
  9. package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -1
  11. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -1
  12. package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
  13. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -1
  14. package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
  15. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -1
  16. package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
  17. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -1
  18. package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
  19. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -1
  20. package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
  21. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -1
  22. package/corpus/core/docs/content/real-time-collaboration.mdx +2 -2
  23. package/corpus/core/docs/content/template-design.mdx +1 -1
  24. package/corpus/core/package.json +3 -7
  25. package/corpus/core/src/action.ts +21 -0
  26. package/corpus/core/src/agent/durable-background.ts +32 -11
  27. package/corpus/core/src/agent/production-agent.ts +11 -1
  28. package/corpus/core/src/agent/run-loop-with-resume.ts +3 -1
  29. package/corpus/core/src/brand-kit/fig/index.ts +17 -37
  30. package/corpus/core/src/cli/index.ts +1 -1
  31. package/corpus/core/src/cli/mcp-config-writers.ts +187 -51
  32. package/corpus/core/src/cli/skills.ts +18 -3
  33. package/corpus/core/src/client/AssistantChat.tsx +11 -1
  34. package/corpus/core/src/client/agent-chat.ts +12 -1
  35. package/corpus/core/src/client/composer/PromptComposer.tsx +100 -41
  36. package/corpus/core/src/client/org/OrgSwitcher.tsx +1 -1
  37. package/corpus/core/src/client/sharing/ShareButton.tsx +3 -27
  38. package/corpus/core/src/collab/presence.ts +1 -1
  39. package/corpus/core/src/mcp/connect-route.ts +34 -5
  40. package/corpus/core/src/server/action-discovery.ts +4 -0
  41. package/corpus/core/src/server/agent-chat-plugin.ts +20 -0
  42. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -0
  43. package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
  44. package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
  45. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +117 -33
  46. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  47. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
  48. package/corpus/templates/analytics/changelog/2026-06-27-dashboards-can-now-nest-under-a-parent-in-the-sidebar-via-a-.md +6 -0
  49. package/corpus/templates/analytics/changelog/2026-06-29-analytics-pages-now-sit-closer-under-the-header-with-tighter.md +6 -0
  50. package/corpus/templates/assets/actions/generate-image-batch.ts +3 -0
  51. package/corpus/templates/assets/actions/generate-image.ts +3 -0
  52. package/corpus/templates/assets/changelog/2026-06-29-longer-agent-generation-window.md +6 -0
  53. package/corpus/templates/assets/netlify.toml +3 -3
  54. package/corpus/templates/assets/server/plugins/agent-chat.ts +4 -1
  55. package/corpus/templates/clips/app/components/capture-install-options.tsx +7 -8
  56. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +1 -1
  57. package/corpus/templates/content/AGENTS.md +12 -0
  58. package/corpus/templates/content/app/blocks/BuilderDocsBlocks.tsx +10 -0
  59. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -0
  60. package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +185 -0
  61. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -0
  62. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +19 -0
  63. package/corpus/templates/content/app/components/editor/extensions/ContentReferenceNode.tsx +82 -0
  64. package/corpus/templates/content/app/global.css +9 -0
  65. package/corpus/templates/content/app/i18n/zh-TW.ts +12 -0
  66. package/corpus/templates/content/app/i18n-data.ts +140 -0
  67. package/corpus/templates/content/changelog/2026-06-29-reusable-mdx-references-now-preview-linked-local-documents-i.md +6 -0
  68. package/corpus/templates/content/shared/builder-docs-blocks.ts +8 -0
  69. package/corpus/templates/content/shared/builder-mdx.ts +141 -4
  70. package/corpus/templates/content/shared/nfm.ts +62 -0
  71. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +10 -11
  72. package/corpus/templates/design/AGENTS.md +5 -0
  73. package/corpus/templates/design/_gitignore +7 -0
  74. package/corpus/templates/design/actions/apply-visual-edit.ts +22 -12
  75. package/corpus/templates/design/actions/create-design-system.ts +15 -4
  76. package/corpus/templates/design/actions/create-file.ts +20 -1
  77. package/corpus/templates/design/actions/delete-design-system.ts +2 -2
  78. package/corpus/templates/design/actions/duplicate-design.ts +35 -9
  79. package/corpus/templates/design/actions/generate-design.ts +146 -69
  80. package/corpus/templates/design/actions/generate-screens.ts +51 -19
  81. package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
  82. package/corpus/templates/design/actions/get-design.ts +2 -0
  83. package/corpus/templates/design/actions/import-design-project.ts +28 -9
  84. package/corpus/templates/design/actions/import-document.ts +22 -8
  85. package/corpus/templates/design/actions/index-design-system-with-builder.ts +6 -153
  86. package/corpus/templates/design/actions/navigate.ts +105 -54
  87. package/corpus/templates/design/actions/set-default-design-system.ts +17 -3
  88. package/corpus/templates/design/actions/show-design-questions.ts +2 -0
  89. package/corpus/templates/design/actions/update-design.ts +2 -1
  90. package/corpus/templates/design/actions/view-screen.ts +12 -6
  91. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +4 -4
  92. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +159 -41
  93. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +8 -4
  94. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +37 -42
  95. package/corpus/templates/design/app/components/design/EditPanel.tsx +336 -183
  96. package/corpus/templates/design/app/components/design/LayersPanel.tsx +153 -34
  97. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +135 -33
  98. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +6 -0
  99. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  100. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +4 -10
  101. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +84 -43
  102. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +4 -4
  103. package/corpus/templates/design/app/components/design/inspector/{FigmaColorPicker.tsx → DesignColorPicker.tsx} +89 -62
  104. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +14 -26
  105. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +40 -12
  106. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -4
  107. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +13 -13
  108. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -9
  109. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +13 -4
  110. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
  111. package/corpus/templates/design/app/components/design/inspector/{figma-icons.tsx → design-icons.tsx} +2 -2
  112. package/corpus/templates/design/app/components/design/inspector/index.ts +12 -12
  113. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +5 -2
  114. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -3
  115. package/corpus/templates/design/app/components/layout/Layout.tsx +5 -2
  116. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  117. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +167 -91
  118. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +16 -3
  119. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +15 -0
  120. package/corpus/templates/design/app/global.css +4 -0
  121. package/corpus/templates/design/app/hooks/use-design-system.ts +4 -1
  122. package/corpus/templates/design/app/hooks/use-navigation-state.ts +38 -3
  123. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +9 -2
  124. package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
  125. package/corpus/templates/design/app/i18n-data.ts +527 -0
  126. package/corpus/templates/design/app/lib/design-systems.ts +6 -4
  127. package/corpus/templates/design/app/lib/design-templates.ts +15 -3
  128. package/corpus/templates/design/app/pages/DesignEditor.tsx +641 -158
  129. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +42 -34
  130. package/corpus/templates/design/app/pages/VisualEdit.tsx +100 -0
  131. package/corpus/templates/design/app/root.tsx +42 -9
  132. package/corpus/templates/design/app/routes/visual-edit.tsx +7 -0
  133. package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +1 -1
  134. package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +1 -1
  135. package/corpus/templates/design/changelog/2026-06-28-visual-code-design-mode.md +6 -0
  136. package/corpus/templates/design/changelog/2026-06-29-design-generation-prompts-now-show-connect-ai-before-sending.md +6 -0
  137. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +1 -1
  138. package/corpus/templates/design/changelog/2026-06-29-fixed-design-editor-exports-and-gradient-edits-so-multi-scre.md +6 -0
  139. package/corpus/templates/design/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  140. package/corpus/templates/design/changelog/2026-06-29-png-export-handles-modern-css-color-functions-more-reliably.md +6 -0
  141. package/corpus/templates/design/changelog/2026-06-29-png-export-now-ignores-duplicate-clicks-while-a-download-is-.md +6 -0
  142. package/corpus/templates/design/changelog/2026-06-29-public-visual-edit-and-shared-design-links-now-invite-visito.md +6 -0
  143. package/corpus/templates/design/changelog/2026-06-29-visual-edit-actions-now-reject-missing-design-targets-before.md +6 -0
  144. package/corpus/templates/design/e2e/README.md +71 -0
  145. package/corpus/templates/design/e2e/global-setup.ts +157 -0
  146. package/corpus/templates/design/e2e/helpers.ts +148 -0
  147. package/corpus/templates/design/netlify.toml +3 -4
  148. package/corpus/templates/design/package.json +5 -1
  149. package/corpus/templates/design/playwright.config.ts +51 -0
  150. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +121 -0
  151. package/corpus/templates/design/server/handlers/uploads.ts +89 -38
  152. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +181 -0
  153. package/corpus/templates/design/server/lib/coding-handoff.ts +33 -4
  154. package/corpus/templates/design/server/lib/design-export.ts +35 -8
  155. package/corpus/templates/design/server/plugins/agent-chat.ts +4 -0
  156. package/corpus/templates/design/server/plugins/auth.ts +10 -1
  157. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  158. package/corpus/templates/design/shared/api.ts +2 -0
  159. package/corpus/templates/design/shared/apply-edits.ts +12 -8
  160. package/corpus/templates/design/shared/canvas-math.ts +19 -1
  161. package/corpus/templates/design/shared/code-layer.ts +87 -28
  162. package/corpus/templates/design/shared/generation-session.ts +1 -0
  163. package/corpus/templates/design/shared/resolve-tweaks.ts +4 -3
  164. package/corpus/templates/design/shared/shader-presets.ts +10 -0
  165. package/corpus/templates/design/shared/source-mode.ts +9 -0
  166. package/corpus/templates/design/vitest.config.ts +2 -1
  167. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
  168. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +1 -1
  169. package/corpus/templates/plan/AGENTS.md +1 -1
  170. package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
  171. package/corpus/templates/plan/actions/validate-local-plan-source.ts +2 -10
  172. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -0
  173. package/corpus/templates/plan/docs/commenting-ux-plan.md +2 -2
  174. package/corpus/templates/plan/server/plan-content.ts +1 -1
  175. package/corpus/templates/plan/server/ui-plan-html.ts +2 -2
  176. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +6 -153
  177. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +53 -38
  178. package/corpus/templates/slides/app/components/design-system/{fig-import-response.ts → builder-index-response.ts} +9 -6
  179. package/corpus/templates/slides/app/components/visual-editor/MultiSelectChip.tsx +1 -1
  180. package/corpus/templates/slides/changelog/2026-06-29-design-system-setup-now-indexes-uploaded-design-files-throug.md +6 -0
  181. package/corpus/templates/slides/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  182. package/corpus/templates/slides/netlify.toml +3 -6
  183. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +116 -0
  184. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +179 -0
  185. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -1
  186. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  187. package/dist/action.d.ts +15 -0
  188. package/dist/action.d.ts.map +1 -1
  189. package/dist/action.js +6 -0
  190. package/dist/action.js.map +1 -1
  191. package/dist/agent/durable-background.d.ts +9 -7
  192. package/dist/agent/durable-background.d.ts.map +1 -1
  193. package/dist/agent/durable-background.js +27 -11
  194. package/dist/agent/durable-background.js.map +1 -1
  195. package/dist/agent/production-agent.d.ts +7 -0
  196. package/dist/agent/production-agent.d.ts.map +1 -1
  197. package/dist/agent/production-agent.js +4 -1
  198. package/dist/agent/production-agent.js.map +1 -1
  199. package/dist/agent/run-loop-with-resume.d.ts +2 -1
  200. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  201. package/dist/agent/run-loop-with-resume.js +2 -2
  202. package/dist/agent/run-loop-with-resume.js.map +1 -1
  203. package/dist/brand-kit/fig/index.d.ts +4 -4
  204. package/dist/brand-kit/fig/index.d.ts.map +1 -1
  205. package/dist/brand-kit/fig/index.js +14 -27
  206. package/dist/brand-kit/fig/index.js.map +1 -1
  207. package/dist/cli/index.js +1 -1
  208. package/dist/cli/index.js.map +1 -1
  209. package/dist/cli/mcp-config-writers.d.ts +15 -7
  210. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  211. package/dist/cli/mcp-config-writers.js +198 -45
  212. package/dist/cli/mcp-config-writers.js.map +1 -1
  213. package/dist/cli/skills.d.ts +1 -1
  214. package/dist/cli/skills.d.ts.map +1 -1
  215. package/dist/cli/skills.js +18 -3
  216. package/dist/cli/skills.js.map +1 -1
  217. package/dist/client/AssistantChat.d.ts.map +1 -1
  218. package/dist/client/AssistantChat.js +11 -1
  219. package/dist/client/AssistantChat.js.map +1 -1
  220. package/dist/client/agent-chat.d.ts.map +1 -1
  221. package/dist/client/agent-chat.js +15 -2
  222. package/dist/client/agent-chat.js.map +1 -1
  223. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  224. package/dist/client/composer/PromptComposer.js +35 -1
  225. package/dist/client/composer/PromptComposer.js.map +1 -1
  226. package/dist/client/org/OrgSwitcher.js +1 -1
  227. package/dist/client/org/OrgSwitcher.js.map +1 -1
  228. package/dist/client/sharing/ShareButton.d.ts +1 -1
  229. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  230. package/dist/client/sharing/ShareButton.js +2 -15
  231. package/dist/client/sharing/ShareButton.js.map +1 -1
  232. package/dist/collab/presence.d.ts +1 -1
  233. package/dist/collab/presence.js +1 -1
  234. package/dist/collab/presence.js.map +1 -1
  235. package/dist/collab/routes.d.ts +1 -1
  236. package/dist/mcp/connect-route.d.ts.map +1 -1
  237. package/dist/mcp/connect-route.js +34 -5
  238. package/dist/mcp/connect-route.js.map +1 -1
  239. package/dist/notifications/routes.d.ts +3 -3
  240. package/dist/observability/routes.d.ts +3 -3
  241. package/dist/resources/handlers.d.ts +1 -1
  242. package/dist/server/action-discovery.d.ts.map +1 -1
  243. package/dist/server/action-discovery.js +5 -0
  244. package/dist/server/action-discovery.js.map +1 -1
  245. package/dist/server/agent-chat-plugin.d.ts +6 -0
  246. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  247. package/dist/server/agent-chat-plugin.js +12 -3
  248. package/dist/server/agent-chat-plugin.js.map +1 -1
  249. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  250. package/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
  251. package/docs/content/locales/ar-SA/template-design.mdx +1 -1
  252. package/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
  253. package/docs/content/locales/de-DE/template-design.mdx +1 -1
  254. package/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
  255. package/docs/content/locales/es-ES/template-design.mdx +1 -1
  256. package/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
  257. package/docs/content/locales/fr-FR/template-design.mdx +1 -1
  258. package/docs/content/locales/hi-IN/template-design.mdx +1 -1
  259. package/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
  260. package/docs/content/locales/ja-JP/template-design.mdx +1 -1
  261. package/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
  262. package/docs/content/locales/ko-KR/template-design.mdx +1 -1
  263. package/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
  264. package/docs/content/locales/pt-BR/template-design.mdx +1 -1
  265. package/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
  266. package/docs/content/locales/zh-CN/template-design.mdx +1 -1
  267. package/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
  268. package/docs/content/locales/zh-TW/template-design.mdx +1 -1
  269. package/docs/content/real-time-collaboration.mdx +2 -2
  270. package/docs/content/template-design.mdx +1 -1
  271. package/package.json +3 -7
  272. package/corpus/core/src/brand-kit/fig/decode.ts +0 -410
  273. package/corpus/core/src/brand-kit/fig/extract-design-system.ts +0 -947
  274. package/corpus/core/src/brand-kit/fig/fig-to-html.ts +0 -1906
  275. package/corpus/templates/design/actions/import-figma.ts +0 -55
  276. package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +0 -6
  277. package/corpus/templates/design/server/handlers/import-figma-system.ts +0 -86
  278. package/corpus/templates/design/server/routes/api/import-figma-system.post.ts +0 -1
  279. package/corpus/templates/slides/server/handlers/import-figma-system.ts +0 -101
  280. package/corpus/templates/slides/server/routes/api/import-figma-system.post.ts +0 -1
  281. package/dist/brand-kit/fig/decode.d.ts +0 -33
  282. package/dist/brand-kit/fig/decode.d.ts.map +0 -1
  283. package/dist/brand-kit/fig/decode.js +0 -358
  284. package/dist/brand-kit/fig/decode.js.map +0 -1
  285. package/dist/brand-kit/fig/extract-design-system.d.ts +0 -44
  286. package/dist/brand-kit/fig/extract-design-system.d.ts.map +0 -1
  287. package/dist/brand-kit/fig/extract-design-system.js +0 -752
  288. package/dist/brand-kit/fig/extract-design-system.js.map +0 -1
  289. package/dist/brand-kit/fig/fig-to-html.d.ts +0 -246
  290. package/dist/brand-kit/fig/fig-to-html.d.ts.map +0 -1
  291. package/dist/brand-kit/fig/fig-to-html.js +0 -1506
  292. package/dist/brand-kit/fig/fig-to-html.js.map +0 -1
@@ -47,7 +47,7 @@ export interface ShareButtonProps {
47
47
  variant?: "compact" | "label";
48
48
  /** Optional trigger style. Defaults to the Google-Docs-style "Share" label. */
49
49
  trigger?: "label" | "icon";
50
- /** Hide the visibility/share glyph in the label trigger. */
50
+ /** @deprecated Label triggers no longer render a visibility/share glyph. */
51
51
  hideTriggerIcon?: boolean;
52
52
  /** Optional className applied to the trigger button. */
53
53
  triggerClassName?: string;
@@ -298,23 +298,8 @@ export function ShareButton(props: ShareButtonProps) {
298
298
  });
299
299
  };
300
300
 
301
- // The default trigger says "Share" the icon reflects the resource's
302
- // current visibility (lock / building / globe), matching Google Docs.
303
- // While the query is loading and we don't know the visibility yet,
304
- // render a skeleton placeholder in the icon slot instead of guessing.
301
+ // The default trigger stays text-only; the icon trigger keeps the share glyph.
305
302
  const iconOnly = props.trigger === "icon";
306
- const loaded = sharesQuery.data !== undefined;
307
- const serverVisibility =
308
- (sharesQuery.data?.visibility as Visibility | null) ?? "private";
309
- const currentVisibility = pendingVisibility ?? serverVisibility;
310
- const VisibilityIcon =
311
- currentVisibility === "public"
312
- ? IconWorld
313
- : currentVisibility === "org"
314
- ? IconBuilding
315
- : IconLock;
316
- const showTriggerIcon = iconOnly || !props.hideTriggerIcon;
317
- const TriggerIcon = iconOnly ? IconShare3 : VisibilityIcon;
318
303
 
319
304
  return (
320
305
  <Popover open={open} onOpenChange={handleOpenChange}>
@@ -327,16 +312,7 @@ export function ShareButton(props: ShareButtonProps) {
327
312
  )}
328
313
  aria-label={iconOnly ? "Share" : undefined}
329
314
  >
330
- {showTriggerIcon ? (
331
- loaded || iconOnly ? (
332
- <TriggerIcon size={16} strokeWidth={1.75} />
333
- ) : (
334
- <span
335
- aria-hidden
336
- className="inline-block h-4 w-4 rounded-sm bg-muted animate-pulse"
337
- />
338
- )
339
- ) : null}
315
+ {iconOnly ? <IconShare3 size={16} strokeWidth={1.75} /> : null}
340
316
  {!iconOnly && <span>Share</span>}
341
317
  </button>
342
318
  </PopoverTrigger>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Presence kit — Liveblocks/Figma-grade presence primitives.
2
+ * Presence kit — Liveblocks/collaboration-grade presence primitives.
3
3
  *
4
4
  * usePresence(awareness) returns:
5
5
  * - others: reactive array of remote participants (human + agent)
@@ -639,6 +639,20 @@ function renderConnectPage(params: {
639
639
  }
640
640
  .primary:hover:not(:disabled) { background: #e4e4e7; }
641
641
  .primary:disabled { opacity: 0.55; cursor: default; }
642
+ .primary.is-loading {
643
+ display: inline-flex; align-items: center; justify-content: center;
644
+ gap: 0.55rem; opacity: 1;
645
+ }
646
+ .primary.is-loading::before {
647
+ content: ""; width: 1rem; height: 1rem; flex: 0 0 auto;
648
+ border-radius: 999px; border: 2px solid rgba(0,0,0,0.22);
649
+ border-top-color: var(--accent-fg);
650
+ animation: spin 0.75s linear infinite;
651
+ }
652
+ @keyframes spin { to { transform: rotate(360deg); } }
653
+ @media (prefers-reduced-motion: reduce) {
654
+ .primary.is-loading::before { animation: none; }
655
+ }
642
656
  .ghost {
643
657
  background: transparent; color: var(--muted);
644
658
  border: 1px solid var(--border-strong); padding: 0.35rem 0.7rem;
@@ -900,7 +914,7 @@ function renderConnectPage(params: {
900
914
  <span class="chev" aria-hidden="true"></span>
901
915
  </summary>
902
916
  <div class="static-token-body">
903
- <div id="msg" class="msg"></div>
917
+ <div id="msg" class="msg" role="status" aria-live="polite"></div>
904
918
  <div id="mintForm">
905
919
  <button id="authorizeBtn" class="primary">${safeUserCode ? "Authorize device" : "Create connection token"}</button>
906
920
  ${tokenAdvancedOptionsHtml}
@@ -983,6 +997,21 @@ function renderConnectPage(params: {
983
997
  }
984
998
  function clearMsg() { msgEl.className = "msg"; msgEl.textContent = ""; }
985
999
 
1000
+ function setButtonLoading(btn, text) {
1001
+ if (!btn.dataset.idleText) btn.dataset.idleText = btn.textContent || "";
1002
+ btn.textContent = text;
1003
+ btn.classList.add("is-loading");
1004
+ btn.setAttribute("aria-busy", "true");
1005
+ btn.disabled = true;
1006
+ }
1007
+
1008
+ function resetButtonLoading(btn) {
1009
+ btn.disabled = false;
1010
+ btn.classList.remove("is-loading");
1011
+ btn.removeAttribute("aria-busy");
1012
+ if (btn.dataset.idleText) btn.textContent = btn.dataset.idleText;
1013
+ }
1014
+
986
1015
  function renderResult(data) {
987
1016
  document.getElementById("mintForm").classList.add("hidden");
988
1017
  var entry = {};
@@ -1060,13 +1089,13 @@ function renderConnectPage(params: {
1060
1089
 
1061
1090
  document.getElementById("authorizeBtn").onclick = async function () {
1062
1091
  var btn = this;
1063
- btn.disabled = true;
1092
+ setButtonLoading(btn, USER_CODE ? "Authorizing device..." : "Creating token...");
1064
1093
  clearMsg();
1065
1094
  try {
1066
1095
  if (USER_CODE) {
1067
1096
  var a = await postJson("/device/authorize", { user_code: USER_CODE });
1068
1097
  if (!a.ok) {
1069
- btn.disabled = false;
1098
+ resetButtonLoading(btn);
1070
1099
  showMsg((a.data && a.data.error) || "Could not authorize this device code.");
1071
1100
  return;
1072
1101
  }
@@ -1128,7 +1157,7 @@ function renderConnectPage(params: {
1128
1157
  var ttlDays = ttlEl ? parseInt(ttlEl.value, 10) || undefined : undefined;
1129
1158
  var m = await postJson("/token", { label: label, ttlDays: ttlDays });
1130
1159
  if (!m.ok) {
1131
- btn.disabled = false;
1160
+ resetButtonLoading(btn);
1132
1161
  showMsg((m.data && m.data.error) || "Could not create token.");
1133
1162
  return;
1134
1163
  }
@@ -1136,7 +1165,7 @@ function renderConnectPage(params: {
1136
1165
  }
1137
1166
  loadTokens();
1138
1167
  } catch (e) {
1139
- btn.disabled = false;
1168
+ resetButtonLoading(btn);
1140
1169
  showMsg("Network error. Please try again.");
1141
1170
  }
1142
1171
  };
@@ -216,6 +216,10 @@ function preserveActionFlags(entry: Record<string, any>): Partial<ActionEntry> {
216
216
  ) {
217
217
  out.chatUI = entry.chatUI;
218
218
  }
219
+ if (typeof entry.timeoutMs === "number") out.timeoutMs = entry.timeoutMs;
220
+ if (typeof entry.maxResultChars === "number") {
221
+ out.maxResultChars = entry.maxResultChars;
222
+ }
219
223
  if (
220
224
  typeof entry.needsApproval === "boolean" ||
221
225
  typeof entry.needsApproval === "function"
@@ -36,6 +36,7 @@ import { DEFAULT_ANTHROPIC_MODEL } from "../agent/default-model.js";
36
36
  import {
37
37
  AGENT_CHAT_PROCESS_RUN_PATH,
38
38
  extractProcessRunId,
39
+ isInBackgroundFunctionRuntime,
39
40
  prepareProcessRunRequest,
40
41
  } from "../agent/durable-background.js";
41
42
  import {
@@ -2567,6 +2568,12 @@ export interface AgentChatPluginOptions {
2567
2568
  * timeout. When reached, long runs continue through the hidden continuation
2568
2569
  * path instead of surfacing a timeout warning. */
2569
2570
  runSoftTimeoutMs?: number;
2571
+ /**
2572
+ * Opt this app into Netlify durable background-function agent-chat runs. This
2573
+ * gives hosted agent turns the 15-minute async-function budget when the app's
2574
+ * Netlify build also emits the background function.
2575
+ */
2576
+ durableBackgroundRuns?: boolean;
2570
2577
  /** Anthropic API key. Falls back to ANTHROPIC_API_KEY env var */
2571
2578
  apiKey?: string;
2572
2579
  /**
@@ -4837,6 +4844,11 @@ export function createAgentChatPlugin(
4837
4844
  signal: controller.signal,
4838
4845
  },
4839
4846
  options?.runSoftTimeoutMs,
4847
+ {
4848
+ backgroundFunction:
4849
+ options?.durableBackgroundRuns === true &&
4850
+ isInBackgroundFunctionRuntime(),
4851
+ },
4840
4852
  );
4841
4853
 
4842
4854
  const { responseText, finalText } = assembleA2AFinalResponse(
@@ -5035,6 +5047,11 @@ export function createAgentChatPlugin(
5035
5047
  signal: controller.signal,
5036
5048
  },
5037
5049
  options?.runSoftTimeoutMs,
5050
+ {
5051
+ backgroundFunction:
5052
+ options?.durableBackgroundRuns === true &&
5053
+ isInBackgroundFunctionRuntime(),
5054
+ },
5038
5055
  );
5039
5056
 
5040
5057
  return accumulatedText || "(no response)";
@@ -5712,6 +5729,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5712
5729
  appId: options?.appId,
5713
5730
  apiKey: options?.apiKey,
5714
5731
  runSoftTimeoutMs: options?.runSoftTimeoutMs,
5732
+ durableBackgroundRuns: options?.durableBackgroundRuns,
5715
5733
  finalResponseGuard: options?.finalResponseGuard,
5716
5734
  prepareRequest: async (details) => {
5717
5735
  // Stash the threadId on runCtx so systemPrompt can check isNewThread().
@@ -5802,6 +5820,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5802
5820
  appId: options?.appId,
5803
5821
  apiKey: options?.apiKey,
5804
5822
  runSoftTimeoutMs: options?.runSoftTimeoutMs,
5823
+ durableBackgroundRuns: options?.durableBackgroundRuns,
5805
5824
  finalResponseGuard: options?.finalResponseGuard,
5806
5825
  prepareRequest: options?.prepareRequest,
5807
5826
  skipFilesContext: true,
@@ -5932,6 +5951,7 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
5932
5951
  appId: options?.appId,
5933
5952
  apiKey: options?.apiKey,
5934
5953
  runSoftTimeoutMs: options?.runSoftTimeoutMs,
5954
+ durableBackgroundRuns: options?.durableBackgroundRuns,
5935
5955
  finalResponseGuard: options?.finalResponseGuard,
5936
5956
  prepareRequest: options?.prepareRequest,
5937
5957
  skipFilesContext,
@@ -28,6 +28,8 @@ type DashboardPatch = {
28
28
  columns?: number;
29
29
  filters?: unknown[];
30
30
  variables?: Record<string, string>;
31
+ /** Id of another dashboard to nest this one under in the sidebar. */
32
+ parentId?: string;
31
33
  };
32
34
 
33
35
  type PanelPatch = {
@@ -277,6 +277,14 @@ export function validateDashboardConfig(
277
277
  if (typeof config.name !== "string" || config.name.trim().length === 0) {
278
278
  return "config.name is required (non-empty string) — without it the dashboard renders as a blank row in the sidebar";
279
279
  }
280
+ if (config.parentId !== undefined && config.parentId !== null) {
281
+ if (
282
+ typeof config.parentId !== "string" ||
283
+ config.parentId.trim().length === 0
284
+ ) {
285
+ return "config.parentId must be a non-empty dashboard id (or omitted) — it nests this dashboard under that parent in the sidebar";
286
+ }
287
+ }
280
288
  // Filter ID collisions cause two controls to read/write the same URL param.
281
289
  // For paired start/end dates use a single date-range filter — the FilterBar
282
290
  // expands it to <id>Start / <id>End at runtime, so the SQL can still
@@ -124,7 +124,7 @@ export function Layout({ children }: LayoutProps) {
124
124
  ? "flex-1 overflow-y-auto"
125
125
  : isAskRoute
126
126
  ? "flex-1 overflow-hidden p-0"
127
- : "flex-1 overflow-y-auto p-4 md:p-6 lg:p-8"
127
+ : "flex-1 overflow-y-auto p-6 pt-2"
128
128
  }
129
129
  >
130
130
  {children}
@@ -30,7 +30,14 @@ import {
30
30
  type QueryKey,
31
31
  } from "@tanstack/react-query";
32
32
  import { useTheme } from "next-themes";
33
- import { useState, useEffect, useCallback, useRef, useMemo } from "react";
33
+ import {
34
+ useState,
35
+ useEffect,
36
+ useCallback,
37
+ useRef,
38
+ useMemo,
39
+ Fragment,
40
+ } from "react";
34
41
  import { Link, useLocation, useNavigate } from "react-router";
35
42
  import { toast } from "sonner";
36
43
 
@@ -51,6 +58,8 @@ type SidebarDashboard = {
51
58
  subviews?: DashboardSubview[];
52
59
  source: "static" | "sql";
53
60
  visibility?: Visibility;
61
+ /** Id of the dashboard this one nests under in the sidebar, if any. */
62
+ parentId?: string;
54
63
  };
55
64
  import {
56
65
  DevDatabaseLink,
@@ -1026,6 +1035,7 @@ type SqlDashboardListItem = {
1026
1035
  id: string;
1027
1036
  name: string;
1028
1037
  visibility?: Visibility;
1038
+ parentId?: string;
1029
1039
  };
1030
1040
 
1031
1041
  async function fetchSqlDashboards(
@@ -1045,6 +1055,10 @@ async function fetchSqlDashboards(
1045
1055
  d.visibility === "org" || d.visibility === "public"
1046
1056
  ? (d.visibility as Visibility)
1047
1057
  : ("private" as Visibility),
1058
+ parentId:
1059
+ typeof d.parentId === "string" && d.parentId.trim().length > 0
1060
+ ? d.parentId
1061
+ : undefined,
1048
1062
  }));
1049
1063
  } catch {
1050
1064
  return [];
@@ -1445,6 +1459,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1445
1459
  name: d.name,
1446
1460
  source: "sql",
1447
1461
  visibility: d.visibility,
1462
+ parentId: d.parentId,
1448
1463
  }));
1449
1464
  const all = [...staticItems, ...sqlItems];
1450
1465
  if (dashboardSortMode === "alphabetical") {
@@ -1482,12 +1497,54 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1482
1497
  [visibleDashboards, dashFilter],
1483
1498
  );
1484
1499
 
1500
+ // Group dashboards that declare a parentId beneath their parent. Nesting is
1501
+ // intentionally one level deep: a dashboard only nests when its parent is
1502
+ // itself top-level. Orphans (parent missing/filtered out), self-references,
1503
+ // cycles, and deeper descendants all fall back to top level so nothing is
1504
+ // ever hidden.
1505
+ const dashboardChildren = useMemo<Map<string, SidebarDashboard[]>>(() => {
1506
+ const byId = new Map(filteredDashboards.map((d) => [d.id, d]));
1507
+ const hasValidParent = (d: SidebarDashboard) =>
1508
+ !!d.parentId && d.parentId !== d.id && byId.has(d.parentId);
1509
+ const byParent = new Map<string, SidebarDashboard[]>();
1510
+ for (const d of filteredDashboards) {
1511
+ if (!hasValidParent(d)) continue;
1512
+ const parent = byId.get(d.parentId as string);
1513
+ if (!parent || hasValidParent(parent)) continue;
1514
+ const arr = byParent.get(d.parentId as string) ?? [];
1515
+ arr.push(d);
1516
+ byParent.set(d.parentId as string, arr);
1517
+ }
1518
+ return byParent;
1519
+ }, [filteredDashboards]);
1520
+
1521
+ const topLevelDashboards = useMemo(() => {
1522
+ const childIds = new Set<string>();
1523
+ for (const arr of dashboardChildren.values())
1524
+ for (const c of arr) childIds.add(c.id);
1525
+ return filteredDashboards.filter((d) => !childIds.has(d.id));
1526
+ }, [filteredDashboards, dashboardChildren]);
1527
+
1485
1528
  const displayedDashboards = useMemo(
1486
1529
  () =>
1487
1530
  dashShowAll
1488
- ? filteredDashboards
1489
- : filteredDashboards.slice(0, SIDEBAR_PREVIEW_COUNT),
1490
- [filteredDashboards, dashShowAll],
1531
+ ? topLevelDashboards
1532
+ : topLevelDashboards.slice(0, SIDEBAR_PREVIEW_COUNT),
1533
+ [topLevelDashboards, dashShowAll],
1534
+ );
1535
+
1536
+ // The flattened id order exactly as rendered (each parent immediately
1537
+ // followed by its nested children). Drag reordering must use this so the
1538
+ // arrayMove indices match what the user sees; the raw `visibleDashboards`
1539
+ // order interleaves children at their sorted positions and would move the
1540
+ // wrong rows once a dashboard is nested.
1541
+ const dashboardRenderOrderIds = useMemo(
1542
+ () =>
1543
+ topLevelDashboards.flatMap((d) => [
1544
+ d.id,
1545
+ ...(dashboardChildren.get(d.id) ?? []).map((c) => c.id),
1546
+ ]),
1547
+ [topLevelDashboards, dashboardChildren],
1491
1548
  );
1492
1549
 
1493
1550
  const handleDashboardDelete = useCallback(
@@ -1801,18 +1858,16 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
1801
1858
  (event: DragEndEvent) => {
1802
1859
  const { active, over } = event;
1803
1860
  if (!over || active.id === over.id) return;
1861
+ const ids = dashboardRenderOrderIds;
1862
+ const oldIndex = ids.indexOf(active.id as string);
1863
+ const newIndex = ids.indexOf(over.id as string);
1864
+ if (oldIndex === -1 || newIndex === -1) return;
1804
1865
  setDashboardSortMode("manual");
1805
- setDashboardOrderState((prev) => {
1806
- const ids = prev.length > 0 ? prev : visibleDashboards.map((d) => d.id);
1807
- const oldIndex = ids.indexOf(active.id as string);
1808
- const newIndex = ids.indexOf(over.id as string);
1809
- if (oldIndex === -1 || newIndex === -1) return prev;
1810
- const newOrder = arrayMove(ids, oldIndex, newIndex);
1811
- setDashboardOrder(newOrder);
1812
- return newOrder;
1813
- });
1866
+ const newOrder = arrayMove(ids, oldIndex, newIndex);
1867
+ setDashboardOrder(newOrder);
1868
+ setDashboardOrderState(newOrder);
1814
1869
  },
1815
- [setDashboardSortMode, visibleDashboards],
1870
+ [setDashboardSortMode, dashboardRenderOrderIds],
1816
1871
  );
1817
1872
 
1818
1873
  const handleAnalysisDragEnd = useCallback(
@@ -2169,27 +2224,56 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2169
2224
  onDragEnd={handleDashboardDragEnd}
2170
2225
  >
2171
2226
  <SortableContext
2172
- items={displayedDashboards.map((d) => d.id)}
2227
+ items={displayedDashboards.flatMap((d) => [
2228
+ d.id,
2229
+ ...(dashboardChildren.get(d.id) ?? []).map((c) => c.id),
2230
+ ])}
2173
2231
  strategy={verticalListSortingStrategy}
2174
2232
  >
2175
2233
  <div className="ms-4 min-w-0 space-y-0.5">
2176
- {displayedDashboards.map((d) => (
2177
- <SortableDashboardItem
2178
- key={d.id}
2179
- d={d}
2180
- isActive={activeDashboardId === d.id}
2181
- location={location}
2182
- favoriteIds={favoriteIds}
2183
- onToggleFavorite={toggleFavorite}
2184
- onDelete={handleDashboardDelete}
2185
- onRename={handleDashboardRename}
2186
- onArchive={handleDashboardArchive}
2187
- onSetVisibility={handleDashboardSetVisibility}
2188
- onPrefetch={prefetchDashboard}
2189
- views={allViewsMap[d.id]}
2190
- />
2191
- ))}
2192
- {filteredDashboards.length > SIDEBAR_PREVIEW_COUNT && (
2234
+ {displayedDashboards.map((d) => {
2235
+ const children = dashboardChildren.get(d.id) ?? [];
2236
+ return (
2237
+ <Fragment key={d.id}>
2238
+ <SortableDashboardItem
2239
+ d={d}
2240
+ isActive={activeDashboardId === d.id}
2241
+ location={location}
2242
+ favoriteIds={favoriteIds}
2243
+ onToggleFavorite={toggleFavorite}
2244
+ onDelete={handleDashboardDelete}
2245
+ onRename={handleDashboardRename}
2246
+ onArchive={handleDashboardArchive}
2247
+ onSetVisibility={handleDashboardSetVisibility}
2248
+ onPrefetch={prefetchDashboard}
2249
+ views={allViewsMap[d.id]}
2250
+ />
2251
+ {children.length > 0 && (
2252
+ <div className="ms-3 space-y-0.5 border-s border-sidebar-border/60 ps-1">
2253
+ {children.map((child) => (
2254
+ <SortableDashboardItem
2255
+ key={child.id}
2256
+ d={child}
2257
+ isActive={activeDashboardId === child.id}
2258
+ location={location}
2259
+ favoriteIds={favoriteIds}
2260
+ onToggleFavorite={toggleFavorite}
2261
+ onDelete={handleDashboardDelete}
2262
+ onRename={handleDashboardRename}
2263
+ onArchive={handleDashboardArchive}
2264
+ onSetVisibility={
2265
+ handleDashboardSetVisibility
2266
+ }
2267
+ onPrefetch={prefetchDashboard}
2268
+ views={allViewsMap[child.id]}
2269
+ />
2270
+ ))}
2271
+ </div>
2272
+ )}
2273
+ </Fragment>
2274
+ );
2275
+ })}
2276
+ {topLevelDashboards.length > SIDEBAR_PREVIEW_COUNT && (
2193
2277
  <button
2194
2278
  onClick={() => setDashShowAll(!dashShowAll)}
2195
2279
  className="flex items-center gap-1 px-3 py-1 text-[11px] text-muted-foreground/70 hover:text-primary"
@@ -2198,7 +2282,7 @@ export function Sidebar({ mobile }: { mobile?: boolean } = {}) {
2198
2282
  ? t("sidebar.showLess")
2199
2283
  : t("sidebar.showMore", {
2200
2284
  count:
2201
- filteredDashboards.length -
2285
+ topLevelDashboards.length -
2202
2286
  SIDEBAR_PREVIEW_COUNT,
2203
2287
  })}
2204
2288
  </button>
@@ -379,6 +379,10 @@ async function fetchDashboard(id: string): Promise<FetchedDashboard | null> {
379
379
  config: {
380
380
  name: data.name ?? "Untitled Dashboard",
381
381
  description: data.description,
382
+ parentId:
383
+ typeof data.parentId === "string" && data.parentId.trim().length > 0
384
+ ? data.parentId
385
+ : undefined,
382
386
  catalog: parseDashboardCatalogMetadata(data.catalog),
383
387
  demo: parseDashboardDemoMetadata(data.demo),
384
388
  filters: data.filters,
@@ -114,6 +114,13 @@ export interface SqlPanel {
114
114
  export interface SqlDashboardConfig {
115
115
  name: string;
116
116
  description?: string;
117
+ /**
118
+ * Optional id of another dashboard this one nests under. When set, the
119
+ * sidebar renders this dashboard indented beneath its parent instead of at
120
+ * the top level. Orphans (parent missing/inaccessible) fall back to the top
121
+ * level. Self-references and cycles are ignored by the renderer.
122
+ */
123
+ parentId?: string;
117
124
  catalog?: {
118
125
  templateId?: string;
119
126
  templateVersion?: string;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-27
4
+ ---
5
+
6
+ Dashboards can now nest under a parent in the sidebar via a parentId field
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Analytics pages now sit closer under the header with tighter content padding.
@@ -22,6 +22,8 @@ import { readImageModelDefault } from "./_image-model-default.js";
22
22
  import generateImage from "./generate-image.js";
23
23
  import { upsertVariantSlot } from "./variant-slots.js";
24
24
 
25
+ const IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12 * 60_000;
26
+
25
27
  export default defineAction({
26
28
  description:
27
29
  "Generate several brand-consistent images in parallel from one brand kit/library. Use @brand-kit mentions as libraryId and @preset mentions as presetId when present. This is synchronous for images: one call waits for every slot and returns final image artifacts. Use this for slide decks, landing pages, and multi-slot design work. Do not call get-generation-run or refresh-generation-run after a normal image batch result.",
@@ -74,6 +76,7 @@ export default defineAction({
74
76
  ),
75
77
  }),
76
78
  parallelSafe: true,
79
+ timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
77
80
  run: async ({ slots, ...inputBase }, context?: ActionRunContext) => {
78
81
  const imageModelDefault = await readImageModelDefault();
79
82
  const libraryId = inputBase.libraryId;
@@ -46,6 +46,8 @@ import {
46
46
  import { readImageModelDefault } from "./_image-model-default.js";
47
47
  import { upsertVariantSlot, wasVariantSlotDismissed } from "./variant-slots.js";
48
48
 
49
+ const IMAGE_GENERATION_TOOL_TIMEOUT_MS = 12 * 60_000;
50
+
49
51
  function resolveModelForTier(
50
52
  tier: ImageQualityTier | undefined,
51
53
  category: ImageCategory | undefined,
@@ -125,6 +127,7 @@ export default defineAction({
125
127
  ),
126
128
  }),
127
129
  parallelSafe: true,
130
+ timeoutMs: IMAGE_GENERATION_TOOL_TIMEOUT_MS,
128
131
  run: async (input, context?: ActionRunContext) => {
129
132
  const imageModelDefault = await readImageModelDefault();
130
133
  const libraryId = input.libraryId;
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Image generation requests have a longer hosted agent window, reducing timeouts for slow models and multi-image batches.
@@ -8,9 +8,9 @@ functions = "templates/assets/.netlify/functions-internal"
8
8
  GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
9
9
  NITRO_PRESET = "netlify"
10
10
  NPM_CONFIG_PRODUCTION = "false"
11
+ AGENT_CHAT_DURABLE_BACKGROUND = "true"
11
12
 
12
- # Image generation calls run a full Gemini round-trip plus Sharp post-processing.
13
- # Each `generate-image` call is ~6-12s; A2A delegation may chain several. Match
14
- # slides' generous timeout so multi-image runs don't get killed mid-flight.
13
+ # Emit the Netlify background function used by Assets' durable agent-chat opt-in.
14
+ # The normal server timeout still protects non-agent image/video HTTP handlers.
15
15
  [functions."*"]
16
16
  timeout = 300
@@ -10,6 +10,8 @@ import actionsRegistry from "../../.generated/actions-registry.js";
10
10
  import { getDb, schema } from "../db/index.js";
11
11
  import "../register-secrets.js";
12
12
 
13
+ const ASSETS_BACKGROUND_RUN_SOFT_TIMEOUT_MS = 13 * 60_000;
14
+
13
15
  const INITIAL_TOOL_NAMES = [
14
16
  "view-screen",
15
17
  "list-libraries",
@@ -240,5 +242,6 @@ export default createAgentChatPlugin({
240
242
  },
241
243
  },
242
244
  },
243
- runSoftTimeoutMs: 240_000,
245
+ durableBackgroundRuns: true,
246
+ runSoftTimeoutMs: ASSETS_BACKGROUND_RUN_SOFT_TIMEOUT_MS,
244
247
  });
@@ -1,4 +1,4 @@
1
- import { useT } from "@agent-native/core/client";
1
+ import { appPath, useT } from "@agent-native/core/client";
2
2
  import {
3
3
  IconBrandApple,
4
4
  IconBrandChrome,
@@ -8,7 +8,6 @@ import {
8
8
  IconExternalLink,
9
9
  } from "@tabler/icons-react";
10
10
  import { type ReactNode } from "react";
11
- import { Link } from "react-router";
12
11
 
13
12
  import { Button, type ButtonProps } from "@/components/ui/button";
14
13
  import {
@@ -91,8 +90,8 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
91
90
  </div>
92
91
  )}
93
92
 
94
- <Link
95
- to={desktopHref}
93
+ <a
94
+ href={appPath(desktopHref)}
96
95
  className="flex items-start gap-3 rounded-md border border-border p-3 text-start transition hover:bg-accent"
97
96
  >
98
97
  <DesktopIcon className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
@@ -104,7 +103,7 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
104
103
  {t("captureInstall.desktopDescription")}
105
104
  </span>
106
105
  </span>
107
- </Link>
106
+ </a>
108
107
  </div>
109
108
  );
110
109
  }
@@ -120,7 +119,7 @@ export function CaptureInstallButton({
120
119
  if (!clipsChromeExtensionEnabled) {
121
120
  return (
122
121
  <Button asChild className={className} {...buttonProps}>
123
- <Link to={desktopHref}>{children}</Link>
122
+ <a href={appPath(desktopHref)}>{children}</a>
124
123
  </Button>
125
124
  );
126
125
  }
@@ -149,9 +148,9 @@ export function CaptureInstallInlineLink({
149
148
  }: CaptureInstallInlineLinkProps) {
150
149
  if (!clipsChromeExtensionEnabled) {
151
150
  return (
152
- <Link to={desktopHref} className={className}>
151
+ <a href={appPath(desktopHref)} className={className}>
153
152
  {children}
154
- </Link>
153
+ </a>
155
154
  );
156
155
  }
157
156
 
@@ -194,7 +194,7 @@ const SCREEN_CAPTURE_MAX_HEIGHT = 1080;
194
194
  // client-side (ffmpeg.wasm re-encode is disabled — see COMPRESSION_ENABLED in
195
195
  // `@/lib/compress`) and the upload provider streams large files directly, so we
196
196
  // capture at a crisp 1080p bitrate instead of a tight budget. 1.2 Mbps left
197
- // dense UI (fine text, Figma, code) visibly fuzzy; 8 Mbps keeps it sharp.
197
+ // dense UI (fine text and code) visibly fuzzy; 8 Mbps keeps it sharp.
198
198
  // Dial down here if file sizes become a concern for a deployment.
199
199
  const RECORDING_VIDEO_BITRATE_BPS = 8_000_000;
200
200
  const RECORDING_AUDIO_BITRATE_BPS = 128_000;