@agent-native/core 0.141.6 → 0.144.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (501) hide show
  1. package/agent-native.eject.json +9 -0
  2. package/corpus/README.md +1 -1
  3. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
  4. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +11 -6
  5. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
  6. package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +19 -0
  7. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +296 -0
  8. package/corpus/templates/analytics/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  9. package/corpus/templates/analytics/.agents/skills/turn-into-skill/SKILL.md +112 -0
  10. package/corpus/templates/analytics/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  11. package/corpus/templates/analytics/AGENTS.md +37 -40
  12. package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
  13. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +1 -1
  14. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +17 -22
  15. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
  16. package/corpus/templates/analytics/actions/navigate.ts +5 -6
  17. package/corpus/templates/analytics/actions/provider-api-register.ts +50 -0
  18. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +46 -13
  19. package/corpus/templates/analytics/actions/test-custom-api-connection.ts +106 -0
  20. package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
  21. package/corpus/templates/analytics/actions/view-screen.ts +0 -20
  22. package/corpus/templates/analytics/app/components/CustomApiCard.tsx +430 -0
  23. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  24. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
  25. package/corpus/templates/analytics/app/i18n-data.ts +682 -90
  26. package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +6 -0
  27. package/corpus/templates/analytics/app/lib/sql-query.ts +33 -6
  28. package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
  29. package/corpus/templates/analytics/app/pages/DataSources.tsx +4 -0
  30. package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
  31. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
  32. package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
  33. package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
  34. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  35. package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
  36. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-load-reliably-during-background-data-sw.md +6 -0
  37. package/corpus/templates/analytics/changelog/2026-08-06-analytics-dashboards-remain-responsive-while-historical-data.md +6 -0
  38. package/corpus/templates/analytics/changelog/2026-08-06-analytics-query-results-now-render-as-a-table-with-csv-downl.md +6 -0
  39. package/corpus/templates/analytics/changelog/2026-08-06-connect-public-https-custom-apis-and-hand-tested-endpoints-t.md +6 -0
  40. package/corpus/templates/analytics/changelog/2026-08-06-durable-agent-worker-memory.md +6 -0
  41. package/corpus/templates/analytics/changelog/2026-08-06-live-dashboard-panels-use-the-shared-query-timeout.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-08-06-on-demand-billing-is-available-as-a-native-dashboard-with-ta.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-08-06-visible-dashboard-names-are-unique.md +6 -0
  44. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
  45. package/corpus/templates/analytics/netlify.toml +7 -0
  46. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +4 -0
  47. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
  48. package/corpus/templates/analytics/server/db/index.ts +27 -0
  49. package/corpus/templates/analytics/server/db/schema.ts +10 -0
  50. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
  51. package/corpus/templates/analytics/server/lib/dashboards-store.ts +227 -60
  52. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1147 -0
  53. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +34 -15
  54. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +57 -4
  55. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +227 -1
  56. package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
  57. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -4
  58. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +1 -0
  59. package/corpus/templates/analytics/server/plugins/db.ts +52 -18
  60. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +36 -8
  61. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +16 -1
  62. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
  63. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +296 -0
  64. package/corpus/templates/assets/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  65. package/corpus/templates/assets/.agents/skills/turn-into-skill/SKILL.md +112 -0
  66. package/corpus/templates/assets/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  67. package/corpus/templates/assets/AGENTS.md +1 -0
  68. package/corpus/templates/assets/actions/_helpers.ts +12 -0
  69. package/corpus/templates/assets/actions/generate-image.ts +9 -3
  70. package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
  71. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
  72. package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
  73. package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
  74. package/corpus/templates/assets/app/routes/settings.tsx +5 -0
  75. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  76. package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
  77. package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
  78. package/corpus/templates/assets/server/lib/generation.ts +58 -0
  79. package/corpus/templates/assets/shared/api.ts +12 -0
  80. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +296 -0
  81. package/corpus/templates/brain/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  82. package/corpus/templates/brain/.agents/skills/turn-into-skill/SKILL.md +112 -0
  83. package/corpus/templates/brain/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  84. package/corpus/templates/brain/actions/list-connection-providers.ts +24 -3
  85. package/corpus/templates/brain/app/lib/dispatch-links.ts +44 -0
  86. package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
  87. package/corpus/templates/brain/app/routes/sources.tsx +15 -7
  88. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  89. package/corpus/templates/brain/app/routes.ts +1 -1
  90. package/corpus/templates/brain/changelog/2026-08-06-brain-provider-setup-links-open-dispatch-correctly-in-standa.md +6 -0
  91. package/corpus/templates/brain/server/plugins/agent-chat.ts +1 -0
  92. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +26 -0
  93. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +296 -0
  94. package/corpus/templates/calendar/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  95. package/corpus/templates/calendar/.agents/skills/turn-into-skill/SKILL.md +112 -0
  96. package/corpus/templates/calendar/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  97. package/corpus/templates/calendar/AGENTS.md +11 -32
  98. package/corpus/templates/calendar/actions/update-event.ts +107 -1
  99. package/corpus/templates/calendar/app/components/calendar/EventCalendarSelect.tsx +83 -0
  100. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +57 -0
  101. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +59 -73
  102. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
  103. package/corpus/templates/calendar/app/hooks/use-events.ts +4 -1
  104. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  105. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
  106. package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
  107. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  108. package/corpus/templates/calendar/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  109. package/corpus/templates/calendar/changelog/2026-08-06-existing-events-can-move-between-connected-google-account-ca.md +6 -0
  110. package/corpus/templates/calendar/server/lib/google-calendar.ts +101 -0
  111. package/corpus/templates/calendar/server/plugins/agent-chat.ts +7 -1
  112. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  113. package/corpus/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  114. package/corpus/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  115. package/corpus/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  116. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  117. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  118. package/corpus/templates/chat/app/root.tsx +1 -1
  119. package/corpus/templates/chat/app/routes/agent.tsx +7 -7
  120. package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
  121. package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
  122. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  123. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +296 -0
  124. package/corpus/templates/clips/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  125. package/corpus/templates/clips/.agents/skills/turn-into-skill/SKILL.md +112 -0
  126. package/corpus/templates/clips/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  127. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +9 -24
  128. package/corpus/templates/clips/app/components/editor/timeline-geometry.ts +15 -0
  129. package/corpus/templates/clips/app/components/editor/waveform.tsx +4 -17
  130. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  131. package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +14 -3
  132. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
  133. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
  134. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
  135. package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
  136. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +28 -38
  137. package/corpus/templates/clips/app/routes/share.$shareId.tsx +21 -27
  138. package/corpus/templates/clips/changelog/2026-08-06-clips-recovers-from-stalled-recording-setup-without-requirin.md +6 -0
  139. package/corpus/templates/clips/changelog/2026-08-06-clips-starts-faster-after-a-deploy-or-idle-period-startup-no.md +6 -0
  140. package/corpus/templates/clips/changelog/2026-08-06-deleting-a-recording-returns-cleanly-to-the-library-and-fit-.md +6 -0
  141. package/corpus/templates/clips/changelog/2026-08-06-hide-clip-insights-from-viewers-without-analytics-access.md +6 -0
  142. package/corpus/templates/clips/desktop/src/app.tsx +48 -20
  143. package/corpus/templates/clips/desktop/src/lib/recorder.ts +275 -74
  144. package/corpus/templates/clips/desktop/src/lib/recording-start-guard.ts +84 -0
  145. package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +14 -4
  146. package/corpus/templates/clips/server/plugins/db.ts +214 -26
  147. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +296 -0
  148. package/corpus/templates/content/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  149. package/corpus/templates/content/.agents/skills/turn-into-skill/SKILL.md +112 -0
  150. package/corpus/templates/content/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  151. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
  152. package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
  153. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  154. package/corpus/templates/content/e2e/global-setup.ts +1 -1
  155. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +296 -0
  156. package/corpus/templates/crm/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  157. package/corpus/templates/crm/.agents/skills/turn-into-skill/SKILL.md +112 -0
  158. package/corpus/templates/crm/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  159. package/corpus/templates/crm/changelog/2026-08-06-crm-starts-faster-after-a-deploy-or-idle-period-one-time-dat.md +6 -0
  160. package/corpus/templates/crm/server/plugins/db.ts +11 -8
  161. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +36 -1
  162. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
  163. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +296 -0
  164. package/corpus/templates/design/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  165. package/corpus/templates/design/.agents/skills/turn-into-skill/SKILL.md +112 -0
  166. package/corpus/templates/design/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  167. package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
  168. package/corpus/templates/design/actions/import-from-url.ts +6 -5
  169. package/corpus/templates/design/app/components/design/LayersPanel.tsx +27 -18
  170. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
  171. package/corpus/templates/design/app/pages/Index.tsx +6 -2
  172. package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
  173. package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
  174. package/corpus/templates/design/app/routes/team.tsx +1 -1
  175. package/corpus/templates/design/changelog/2026-08-05-deleting-a-design-from-the-home-page-no-longer-leaves-the-ap.md +6 -0
  176. package/corpus/templates/design/changelog/2026-08-05-renaming-a-layer-now-focuses-its-name-field-automatically.md +6 -0
  177. package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
  178. package/corpus/templates/design/e2e/global-setup.ts +1 -1
  179. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  180. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +296 -0
  181. package/corpus/templates/dispatch/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  182. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/SKILL.md +112 -0
  183. package/corpus/templates/dispatch/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  184. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  185. package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
  186. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  187. package/corpus/templates/dispatch/changelog/2026-08-06-ready-workspace-apps-stay-one-click-away-in-a-compact-dispat.md +6 -0
  188. package/corpus/templates/dispatch/changelog/2026-08-06-shared-vault-values-and-management-are-now-restricted-to-wor.md +6 -0
  189. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +296 -0
  190. package/corpus/templates/factory/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  191. package/corpus/templates/factory/.agents/skills/turn-into-skill/SKILL.md +112 -0
  192. package/corpus/templates/factory/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  193. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
  194. package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
  195. package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
  196. package/corpus/templates/factory/app/routes/team.tsx +1 -1
  197. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +296 -0
  198. package/corpus/templates/forms/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  199. package/corpus/templates/forms/.agents/skills/turn-into-skill/SKILL.md +112 -0
  200. package/corpus/templates/forms/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  201. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
  202. package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
  203. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  204. package/corpus/templates/forms/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  205. package/corpus/templates/forms/server/plugins/agent-chat.ts +4 -0
  206. package/corpus/templates/forms/shared/navigation.ts +1 -1
  207. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +296 -0
  208. package/corpus/templates/macros/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  209. package/corpus/templates/macros/.agents/skills/turn-into-skill/SKILL.md +112 -0
  210. package/corpus/templates/macros/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  211. package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
  212. package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
  213. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  214. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +296 -0
  215. package/corpus/templates/mail/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  216. package/corpus/templates/mail/.agents/skills/turn-into-skill/SKILL.md +112 -0
  217. package/corpus/templates/mail/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  218. package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
  219. package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
  220. package/corpus/templates/mail/changelog/2026-08-06-delegated-requests-continue-in-the-background.md +6 -0
  221. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  222. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +296 -0
  223. package/corpus/templates/plan/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  224. package/corpus/templates/plan/.agents/skills/turn-into-skill/SKILL.md +112 -0
  225. package/corpus/templates/plan/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  226. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  227. package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
  228. package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
  229. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  230. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +48 -5
  231. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +40 -2
  232. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +6 -0
  233. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +15 -2
  234. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +29 -24
  235. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +296 -0
  236. package/corpus/templates/slides/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  237. package/corpus/templates/slides/.agents/skills/turn-into-skill/SKILL.md +112 -0
  238. package/corpus/templates/slides/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  239. package/corpus/templates/slides/AGENTS.md +7 -9
  240. package/corpus/templates/slides/_gitignore +1 -0
  241. package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
  242. package/corpus/templates/slides/actions/export-pptx.ts +402 -36
  243. package/corpus/templates/slides/actions/import-file.ts +46 -85
  244. package/corpus/templates/slides/actions/import-from-url.ts +6 -6
  245. package/corpus/templates/slides/actions/import-google-slides-reference.ts +271 -12
  246. package/corpus/templates/slides/actions/import-pptx.ts +85 -60
  247. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -13
  248. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
  249. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +5 -1
  250. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +25 -12
  251. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +112 -57
  252. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +202 -0
  253. package/corpus/templates/slides/app/components/editor/GoogleSlidesReferenceImport.tsx +26 -14
  254. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +176 -0
  255. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +182 -141
  256. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +1 -0
  257. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +18 -0
  258. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +85 -0
  259. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -0
  260. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +13 -3
  261. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +27 -1
  262. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
  263. package/corpus/templates/slides/app/global.css +6 -0
  264. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  265. package/corpus/templates/slides/app/lib/create-deck-generation.ts +10 -6
  266. package/corpus/templates/slides/app/lib/deck-preview-frame.ts +15 -0
  267. package/corpus/templates/slides/app/lib/deck-share-links.ts +9 -0
  268. package/corpus/templates/slides/app/lib/export-pptx-client.ts +89 -0
  269. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +3 -0
  270. package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
  271. package/corpus/templates/slides/app/pages/Index.tsx +107 -6
  272. package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
  273. package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
  274. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  275. package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
  276. package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
  277. package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
  278. package/corpus/templates/slides/changelog/2026-08-06-clicking-the-gray-canvas-around-a-slide-clears-its-selection.md +6 -0
  279. package/corpus/templates/slides/changelog/2026-08-06-deck-thumbnails-keep-a-consistent-listing-frame-across-aspec.md +6 -0
  280. package/corpus/templates/slides/changelog/2026-08-06-deep-linked-decks-open-directly-instead-of-being-interrupted.md +6 -0
  281. package/corpus/templates/slides/changelog/2026-08-06-google-slides-exports-now-show-clear-import-steps-when-direc.md +6 -0
  282. package/corpus/templates/slides/changelog/2026-08-06-import-pdf-google-slides-and-powerpoint-decks-directly-from-.md +6 -0
  283. package/corpus/templates/slides/changelog/2026-08-06-imported-powerpoint-exports-keep-text-and-images-editable-an.md +6 -0
  284. package/corpus/templates/slides/changelog/2026-08-06-presentation-prompts-continue-adding-rendered-slides-until-t.md +6 -0
  285. package/corpus/templates/slides/changelog/2026-08-06-public-decks-now-share-read-only-presentation-links-by-defau.md +6 -0
  286. package/corpus/templates/slides/changelog/2026-08-06-slides-offers-a-direct-connect-google-button-when-a-drive-co.md +6 -0
  287. package/corpus/templates/slides/changelog/2026-08-06-slides-preserve-uploaded-pdf-layouts-accept-larger-reference.md +6 -0
  288. package/corpus/templates/slides/changelog/2026-08-06-styled-words-stay-part-of-their-text-box-instead-of-becoming.md +6 -0
  289. package/corpus/templates/slides/server/handlers/google-docs-auth.ts +7 -20
  290. package/corpus/templates/slides/server/handlers/import/html-converter.ts +236 -13
  291. package/corpus/templates/slides/server/handlers/import/pptx-assets.ts +67 -0
  292. package/corpus/templates/slides/server/handlers/import/pptx-parser.ts +2 -0
  293. package/corpus/templates/slides/server/handlers/uploads.ts +7 -3
  294. package/corpus/templates/slides/server/lib/chat-attachments.ts +6 -3
  295. package/corpus/templates/slides/server/lib/emails.ts +7 -2
  296. package/corpus/templates/slides/server/lib/google-docs-error.ts +50 -0
  297. package/corpus/templates/slides/server/lib/google-docs-oauth.ts +1 -0
  298. package/corpus/templates/slides/server/lib/import-asset-storage.ts +127 -0
  299. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -3
  300. package/corpus/templates/slides/server/routes/api/import-assets/[token].get.ts +70 -0
  301. package/corpus/templates/slides/shared/upload-types.ts +3 -0
  302. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +296 -0
  303. package/corpus/templates/tasks/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  304. package/corpus/templates/tasks/.agents/skills/turn-into-skill/SKILL.md +112 -0
  305. package/corpus/templates/tasks/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  306. package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
  307. package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
  308. package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
  309. package/dist/agent/engine/ai-sdk-engine.js +49 -3
  310. package/dist/agent/production-agent.d.ts +18 -0
  311. package/dist/agent/production-agent.js +80 -1
  312. package/dist/agent/types.d.ts +7 -0
  313. package/dist/cli/agent-plugin.d.ts +112 -0
  314. package/dist/cli/agent-plugin.js +1081 -0
  315. package/dist/cli/app-skill.js +39 -1
  316. package/dist/cli/code-agent-executor.d.ts +3 -0
  317. package/dist/cli/code-agent-executor.js +51 -13
  318. package/dist/cli/index.js +13 -0
  319. package/dist/cli/templates-meta.js +1 -1
  320. package/dist/client/AgentPanel.d.ts +3 -1
  321. package/dist/client/AgentPanel.js +9 -2
  322. package/dist/client/AssistantChat.d.ts +8 -0
  323. package/dist/client/AssistantChat.js +23 -8
  324. package/dist/client/NewWorkspaceAppFlow.js +1 -0
  325. package/dist/client/agent-chat-adapter.d.ts +1 -1
  326. package/dist/client/agent-chat-adapter.js +153 -7
  327. package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
  328. package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
  329. package/dist/client/assistant-ui-recovery.d.ts +2 -1
  330. package/dist/client/assistant-ui-recovery.js +7 -3
  331. package/dist/client/chat/run-recovery.js +5 -1
  332. package/dist/client/embed-auth.js +6 -2
  333. package/dist/client/error-format.js +12 -0
  334. package/dist/client/integrations/IntegrationsPanel.js +40 -6
  335. package/dist/client/onboarding/FirstRunOnboarding.js +11 -4
  336. package/dist/client/org/OrgSwitcher.js +2 -2
  337. package/dist/client/org/TeamPage.js +92 -72
  338. package/dist/client/require-session.d.ts +1 -1
  339. package/dist/client/require-session.js +4 -4
  340. package/dist/client/resources/McpConnectionSuggestion.d.ts +1 -0
  341. package/dist/client/resources/McpConnectionSuggestion.js +27 -10
  342. package/dist/client/resources/McpIntegrationDialog.js +77 -45
  343. package/dist/client/resources/mcp-integration-catalog.d.ts +13 -1
  344. package/dist/client/resources/mcp-integration-catalog.js +255 -2
  345. package/dist/client/resources/mcp-integration-logos.js +18 -2
  346. package/dist/client/resources/use-mcp-servers.d.ts +5 -0
  347. package/dist/client/resources/use-mcp-servers.js +36 -0
  348. package/dist/client/settings/AccountSettingsCard.js +42 -36
  349. package/dist/client/settings/AutomationsSection.js +1 -1
  350. package/dist/client/settings/DemoModeSection.d.ts +3 -1
  351. package/dist/client/settings/DemoModeSection.js +4 -1
  352. package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
  353. package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
  354. package/dist/client/settings/SchedulingTimezoneField.js +9 -5
  355. package/dist/client/settings/SettingsPanel.d.ts +4 -1
  356. package/dist/client/settings/SettingsPanel.js +21 -21
  357. package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
  358. package/dist/client/settings/SettingsTabsPage.js +98 -45
  359. package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
  360. package/dist/client/settings/useSettingsPanelController.js +47 -8
  361. package/dist/collab/routes.d.ts +1 -1
  362. package/dist/db/client.d.ts +9 -0
  363. package/dist/db/client.js +51 -16
  364. package/dist/db/create-get-db.js +2 -2
  365. package/dist/db/ddl-guard.d.ts +11 -10
  366. package/dist/db/ddl-guard.js +40 -12
  367. package/dist/deploy/build.d.ts +2 -0
  368. package/dist/deploy/build.js +97 -4
  369. package/dist/deploy/workspace-deploy.js +0 -2
  370. package/dist/guards/no-env-credentials.js +9 -0
  371. package/dist/ingestion/index.d.ts +1 -1
  372. package/dist/ingestion/pptx.d.ts +68 -0
  373. package/dist/ingestion/pptx.js +660 -213
  374. package/dist/ingestion/website.d.ts +30 -0
  375. package/dist/integrations/plugin.js +9 -6
  376. package/dist/localization/default-messages.d.ts +59 -0
  377. package/dist/localization/default-messages.js +62 -2
  378. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  379. package/dist/mcp-client/oauth-routes.d.ts +2 -0
  380. package/dist/mcp-client/oauth-routes.js +74 -7
  381. package/dist/mcp-client/routes.d.ts +1 -0
  382. package/dist/mcp-client/routes.js +49 -0
  383. package/dist/navigation/index.js +16 -5
  384. package/dist/notifications/routes.d.ts +1 -1
  385. package/dist/observability/routes.d.ts +3 -3
  386. package/dist/onboarding/app-profile.js +11 -0
  387. package/dist/progress/routes.d.ts +1 -1
  388. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  389. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  390. package/dist/resources/store.js +15 -1
  391. package/dist/scripts/call-agent.js +45 -4
  392. package/dist/secrets/routes.d.ts +6 -6
  393. package/dist/server/agent-chat/action-filters-a2a.d.ts +1 -1
  394. package/dist/server/agent-chat/action-filters-a2a.js +36 -1
  395. package/dist/server/agent-chat-plugin.js +29 -8
  396. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  397. package/dist/server/auth.js +55 -17
  398. package/dist/server/better-auth-instance.js +5 -3
  399. package/dist/server/core-routes-plugin.d.ts +8 -1
  400. package/dist/server/core-routes-plugin.js +28 -3
  401. package/dist/server/credential-provider.d.ts +3 -3
  402. package/dist/server/credential-provider.js +90 -4
  403. package/dist/server/identity-sso.js +2 -1
  404. package/dist/server/onboarding-html.d.ts +2 -0
  405. package/dist/server/onboarding-html.js +171 -62
  406. package/dist/server/realtime-token.d.ts +1 -1
  407. package/dist/server/self-dispatch.d.ts +5 -4
  408. package/dist/server/self-dispatch.js +25 -14
  409. package/dist/server/ssr-handler.d.ts +1 -1
  410. package/dist/server/ssr-handler.js +1 -1
  411. package/dist/server/workspace-provider-oauth.js +1 -1
  412. package/dist/setup-connections/onboarding.js +1 -1
  413. package/dist/shared/cache-control.d.ts +26 -2
  414. package/dist/shared/cache-control.js +43 -2
  415. package/dist/shared/index.d.ts +1 -1
  416. package/dist/shared/index.js +1 -1
  417. package/dist/shared/password-policy.d.ts +3 -0
  418. package/dist/shared/password-policy.js +3 -0
  419. package/dist/shared/sign-in-journey.d.ts +5 -2
  420. package/dist/shared/sign-in-journey.js +10 -4
  421. package/dist/sharing/actions/set-resource-visibility.js +16 -4
  422. package/dist/sharing/registry.d.ts +14 -0
  423. package/dist/styles/agent-native.css +6 -0
  424. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  425. package/dist/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  426. package/dist/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  427. package/dist/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  428. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  429. package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  430. package/dist/templates/chat/app/root.tsx +1 -1
  431. package/dist/templates/chat/app/routes/agent.tsx +7 -7
  432. package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
  433. package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
  434. package/dist/templates/chat/app/routes/team.tsx +1 -1
  435. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  436. package/dist/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  437. package/dist/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  438. package/dist/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  439. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  440. package/dist/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  441. package/dist/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  442. package/dist/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  443. package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  444. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  445. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  446. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  447. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  448. package/dist/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  449. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  450. package/dist/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  451. package/dist/templates/workspace-root/AGENTS.md +7 -0
  452. package/dist/user-profile/actions/change-password.js +8 -3
  453. package/dist/user-profile/actions/set-password.js +5 -1
  454. package/dist/vite/action-types-plugin.js +59 -0
  455. package/docs/content/authentication.mdx +4 -5
  456. package/docs/content/skills-guide.mdx +13 -0
  457. package/docs/content/template-analytics-developers.mdx +1 -1
  458. package/docs/content/template-calendar-developers.mdx +1 -1
  459. package/docs/content/template-chat-developers.mdx +2 -2
  460. package/docs/content/template-content-developers.mdx +1 -1
  461. package/docs/content/template-mail-developers.mdx +1 -1
  462. package/docs/content/template-plan-developers.mdx +1 -1
  463. package/docs/content/toolkit-command-navigation.mdx +6 -6
  464. package/docs/content/toolkit-org-team.mdx +1 -1
  465. package/docs/content/toolkit-settings.mdx +8 -8
  466. package/docs/content/toolkit-setup-connections.mdx +1 -1
  467. package/package.json +6 -2
  468. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +296 -0
  469. package/src/templates/chat/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  470. package/src/templates/chat/.agents/skills/turn-into-skill/SKILL.md +112 -0
  471. package/src/templates/chat/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  472. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  473. package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  474. package/src/templates/chat/app/root.tsx +1 -1
  475. package/src/templates/chat/app/routes/agent.tsx +7 -7
  476. package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
  477. package/src/templates/chat/app/routes/settings.$.tsx +1 -0
  478. package/src/templates/chat/app/routes/team.tsx +1 -1
  479. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +296 -0
  480. package/src/templates/default/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  481. package/src/templates/default/.agents/skills/turn-into-skill/SKILL.md +112 -0
  482. package/src/templates/default/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  483. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +296 -0
  484. package/src/templates/headless/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  485. package/src/templates/headless/.agents/skills/turn-into-skill/SKILL.md +112 -0
  486. package/src/templates/headless/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  487. package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +6 -0
  488. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  489. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  490. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +51 -0
  491. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +296 -0
  492. package/src/templates/workspace-core/.agents/skills/turn-into-app/agents/openai.yaml +4 -0
  493. package/src/templates/workspace-core/.agents/skills/turn-into-skill/SKILL.md +112 -0
  494. package/src/templates/workspace-core/.agents/skills/turn-into-skill/agents/openai.yaml +4 -0
  495. package/src/templates/workspace-root/AGENTS.md +7 -0
  496. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
  497. package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
  498. package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
  499. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
  500. package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
  501. package/src/templates/default/actions/navigate.spec.ts +0 -53
@@ -71,6 +71,7 @@ import {
71
71
  createSlidesCanvasGestureController,
72
72
  isWithinSlidesCanvasEdgeMoveBand,
73
73
  resolveSlidesCanvasPointerIntent,
74
+ resolveSlidesCanvasDragTarget,
74
75
  slidesCanvasInteractionCore,
75
76
  } from "./canvas-interactions";
76
77
  import ImageOverlay from "./ImageOverlay";
@@ -125,6 +126,12 @@ import {
125
126
  } from "./slide-object-interactions";
126
127
  import { getPassiveSlidePresenceUsers } from "./slide-presence";
127
128
  import { type SlideStylePatch, type SlideStyleSnapshot } from "./slide-style";
129
+ import {
130
+ findSmartBlock,
131
+ isInlineTextElement,
132
+ isTextLeaf,
133
+ shouldStampBuilderId,
134
+ } from "./slide-text-targets";
128
135
  import { SlideContextToolbar } from "./SlideContextToolbar";
129
136
  import { SlideOverflowWarning } from "./SlideOverflowWarning";
130
137
  import { SpeakerNotesPanel } from "./SpeakerNotesPanel";
@@ -145,6 +152,15 @@ function isPersistedFreeformObject(element: HTMLElement): boolean {
145
152
  );
146
153
  }
147
154
 
155
+ function isSlideCanvasShell(element: HTMLElement): boolean {
156
+ return (
157
+ element.classList.contains("fmd-slide") ||
158
+ element.classList.contains("fmd-autofit-scale") ||
159
+ element.hasAttribute("data-fmd-autofit-content") ||
160
+ element.hasAttribute("data-slide-canvas")
161
+ );
162
+ }
163
+
148
164
  function ensureBuilderId(element: HTMLElement): string {
149
165
  const existing = element.getAttribute("data-builder-id");
150
166
  if (existing) return existing;
@@ -153,12 +169,16 @@ function ensureBuilderId(element: HTMLElement): string {
153
169
  return id;
154
170
  }
155
171
 
156
- /** Stamp all elements inside a container with unique data-builder-id attributes */
172
+ /** Stamp selectable elements inside a container with transient builder ids. */
157
173
  function stampBuilderIds(container: HTMLElement) {
158
174
  const elements = container.querySelectorAll("*");
159
175
  elements.forEach((el) => {
160
- if ((el as HTMLElement).classList.contains("fmd-layout-spacer")) return;
161
- ensureBuilderId(el as HTMLElement);
176
+ const element = el as HTMLElement;
177
+ if (!shouldStampBuilderId(element)) {
178
+ element.removeAttribute("data-builder-id");
179
+ return;
180
+ }
181
+ ensureBuilderId(element);
162
182
  });
163
183
  }
164
184
 
@@ -169,108 +189,9 @@ function getBuilderSelector(el: HTMLElement): string | null {
169
189
  return null;
170
190
  }
171
191
 
172
- /** Inline tags allowed inside a "text leaf" element */
173
- const INLINE_TAGS = new Set([
174
- "SPAN",
175
- "STRONG",
176
- "EM",
177
- "B",
178
- "I",
179
- "U",
180
- "A",
181
- "BR",
182
- "CODE",
183
- "SUB",
184
- "SUP",
185
- "MARK",
186
- "SMALL",
187
- "S",
188
- "FONT",
189
- ]);
190
-
191
192
  /** Block tags that can hold rich multi-paragraph content */
192
193
  const RICH_BLOCK_TAGS = new Set(["P", "DIV", "BLOCKQUOTE", "LI", "UL", "OL"]);
193
194
 
194
- /**
195
- * A "text leaf" is a block-level element whose children are only text nodes
196
- * or inline elements — i.e. it's safe to make contentEditable without
197
- * exposing layout containers to editing.
198
- */
199
- function isTextLeaf(el: HTMLElement): boolean {
200
- if (!el || el.tagName === "IMG") return false;
201
- if (el.classList.contains("fmd-img-placeholder")) return false;
202
- // A user-placed text box stays editable even after its content is fully
203
- // deleted, so an emptied box does not degrade into an unrecognized shape.
204
- if (el.classList.contains("fmd-text-box")) return true;
205
- // Must contain some text
206
- if (!el.textContent?.trim()) return false;
207
- for (const child of Array.from(el.children)) {
208
- if (!INLINE_TAGS.has(child.tagName)) return false;
209
- }
210
- return true;
211
- }
212
-
213
- /**
214
- * A "smart group" is a container whose children are all text leaves OR
215
- * nested smart groups — i.e. a container that exists purely to hold text
216
- * chunks with no images / layout islands mixed in. These are safe to edit
217
- * as a single contentEditable region so users can work with multiple
218
- * chunks (bullet rows, stat pairs, bodies of paragraphs) at once.
219
- */
220
- function isSmartGroup(el: HTMLElement): boolean {
221
- if (!el) return false;
222
- if (el.tagName === "IMG") return false;
223
- if (el.classList.contains("fmd-img-placeholder")) return false;
224
- const children = Array.from(el.children);
225
- if (children.length < 2) return false;
226
- // Must contain some text overall
227
- if (!el.textContent?.trim()) return false;
228
- for (const child of children) {
229
- const c = child as HTMLElement;
230
- if (c.tagName === "IMG") return false;
231
- if (c.classList.contains("fmd-img-placeholder")) return false;
232
- if (!isTextLeaf(c) && !isSmartGroup(c)) return false;
233
- }
234
- return true;
235
- }
236
-
237
- /**
238
- * Find the "smart block" to edit for a given click target. A smart block is
239
- * either:
240
- * - a text leaf (single line / single rich text block), or
241
- * - a smart group that is itself inside the top-level fmd-slide wrapper —
242
- * i.e. a logical grouping of text chunks (a bullet list, a pair of
243
- * stat number + label, etc.).
244
- *
245
- * We walk up from the click target and prefer the DEEPEST meaningful block
246
- * so each double-click targets the most specific editable region. Users who
247
- * want to edit multiple chunks together can double-click the whitespace
248
- * between them, or double-click a group's border — the click will resolve
249
- * to the group element rather than any single child.
250
- */
251
- function findSmartBlock(
252
- target: HTMLElement,
253
- root: HTMLElement,
254
- ): HTMLElement | null {
255
- let el: HTMLElement | null = target;
256
- while (el && root.contains(el)) {
257
- if (isTextLeaf(el)) {
258
- // A list item (native <li> or a styled bullet row) must be edited
259
- // together with its siblings so Enter can add a new item — editing a
260
- // single item in isolation traps Enter inside that one row.
261
- const list = findEnclosingList(el, root);
262
- if (list) return list;
263
- return el;
264
- }
265
- // The click landed on a container (e.g. a flex wrapper around stat
266
- // rows). If that container is a smart group, use IT as the block so
267
- // the user gets multi-chunk editing of everything inside.
268
- if (isSmartGroup(el)) return el;
269
- el = el.parentElement;
270
- }
271
- return null;
272
- }
273
-
274
195
  /** Strip renderer/editor-only attributes from an HTML string before saving */
275
196
  function stripBuilderIds(html: string): string {
276
197
  let cleaned = html;
@@ -2485,16 +2406,19 @@ export default function SlideEditor({
2485
2406
  ]);
2486
2407
 
2487
2408
  /**
2488
- * Find the nearest meaningful "element" for multi-select from a click target.
2489
- * Walks up to the closest [data-builder-id] inside the slide content. Skips
2490
- * the slide-content root itself (clicking the slide background means
2491
- * "deselect / start marquee", not "select the whole slide").
2409
+ * Find the nearest meaningful element for multi-select from a click target.
2410
+ * Inline text markup is presentation only, so walk through it to the
2411
+ * containing block. The slide-content root itself starts a marquee instead.
2492
2412
  */
2493
2413
  const findSelectableId = useCallback(
2494
2414
  (target: HTMLElement, slideContent: HTMLElement): string | null => {
2495
2415
  let el: HTMLElement | null = target;
2496
2416
  while (el && slideContent.contains(el) && el !== slideContent) {
2497
2417
  if (el.classList.contains("fmd-layout-spacer")) return null;
2418
+ if (isInlineTextElement(el)) {
2419
+ el = el.parentElement;
2420
+ continue;
2421
+ }
2498
2422
  const id = el.getAttribute("data-builder-id");
2499
2423
  if (id) return id;
2500
2424
  el = el.parentElement;
@@ -2509,6 +2433,10 @@ export default function SlideEditor({
2509
2433
  let el: HTMLElement | null = target;
2510
2434
  while (el && slideContent.contains(el) && el !== slideContent) {
2511
2435
  if (el.classList.contains("fmd-layout-spacer")) return null;
2436
+ if (isInlineTextElement(el)) {
2437
+ el = el.parentElement;
2438
+ continue;
2439
+ }
2512
2440
  if (el.getAttribute("data-builder-id")) return el;
2513
2441
  el = el.parentElement;
2514
2442
  }
@@ -2522,9 +2450,10 @@ export default function SlideEditor({
2522
2450
  (target: HTMLElement, slideContent: HTMLElement): boolean => {
2523
2451
  // The slide root itself, or a direct child container that has no text /
2524
2452
  // image content at the point of click. Simplest heuristic: target IS
2525
- // the slide-content element, OR it's the .fmd-slide wrapper.
2526
- if (target === slideContent) return true;
2527
- if (target.classList.contains("fmd-slide")) return true;
2453
+ // the slide-content element, OR it's one of the renderer's structural
2454
+ // shells. Those shells are not user objects and must remain marquee
2455
+ // surfaces rather than becoming draggable freeform objects.
2456
+ if (target === slideContent || isSlideCanvasShell(target)) return true;
2528
2457
  return false;
2529
2458
  },
2530
2459
  [],
@@ -2840,9 +2769,7 @@ export default function SlideEditor({
2840
2769
  const slideCanvas = element.closest(
2841
2770
  ".fmd-slide, [data-slide-canvas]",
2842
2771
  ) as HTMLElement | null;
2843
- if (!slideCanvas || getComputedStyle(element).position !== "absolute") {
2844
- return;
2845
- }
2772
+ if (!slideCanvas) return;
2846
2773
 
2847
2774
  const slideRect = slideCanvas.getBoundingClientRect();
2848
2775
  const slideWidth = slideCanvas.offsetWidth;
@@ -2866,10 +2793,74 @@ export default function SlideEditor({
2866
2793
  suppressNextClickRef.current = true;
2867
2794
  }
2868
2795
 
2869
- const origin = getObjectGeometry(element);
2796
+ const initiallyAbsolute =
2797
+ getComputedStyle(element).position === "absolute";
2798
+ let origin = initiallyAbsolute ? getObjectGeometry(element) : null;
2870
2799
  const originalObjectId = element.getAttribute("data-slide-object-id");
2800
+ const originalClassName = element.className;
2801
+ const originalStyle = element.getAttribute("style");
2802
+ const originalContentEditable = element.getAttribute("contenteditable");
2803
+ const originalEditingBlock = element.getAttribute("data-editing-block");
2871
2804
  let activeElement = element;
2872
2805
  let clone: HTMLElement | null = null;
2806
+ let restoreMarkdownTree: (() => void) | undefined;
2807
+ let promotedToFreeform = false;
2808
+
2809
+ const promoteForDrag = () => {
2810
+ if (origin) return true;
2811
+ const frozen = freezeElementForFreeformSelection(element);
2812
+ if (!frozen) {
2813
+ return false;
2814
+ }
2815
+ restoreMarkdownTree = frozen.restoreMarkdownTree;
2816
+ if (getComputedStyle(element).position !== "absolute") {
2817
+ restoreMarkdownTree?.();
2818
+ restoreMarkdownTree = undefined;
2819
+ return false;
2820
+ }
2821
+ promotedToFreeform = true;
2822
+ origin = getObjectGeometry(element);
2823
+ return true;
2824
+ };
2825
+
2826
+ const removeFreeformLayoutSpacer = () => {
2827
+ const objectId = element.getAttribute("data-slide-object-id");
2828
+ if (!objectId) return;
2829
+ const spacer = Array.from(
2830
+ element.parentElement?.querySelectorAll<HTMLElement>(
2831
+ "[data-slide-layout-spacer-for]",
2832
+ ) ?? [],
2833
+ ).find(
2834
+ (candidate) =>
2835
+ candidate.getAttribute("data-slide-layout-spacer-for") === objectId,
2836
+ );
2837
+ spacer?.remove();
2838
+ };
2839
+
2840
+ const restorePromotedElement = () => {
2841
+ removeFreeformLayoutSpacer();
2842
+ restoreMarkdownTree?.();
2843
+ if (!restoreMarkdownTree) {
2844
+ element.className = originalClassName;
2845
+ if (originalStyle === null) element.removeAttribute("style");
2846
+ else element.setAttribute("style", originalStyle);
2847
+ if (originalContentEditable === null) {
2848
+ element.removeAttribute("contenteditable");
2849
+ } else {
2850
+ element.setAttribute("contenteditable", originalContentEditable);
2851
+ }
2852
+ if (originalEditingBlock === null) {
2853
+ element.removeAttribute("data-editing-block");
2854
+ } else {
2855
+ element.setAttribute("data-editing-block", originalEditingBlock);
2856
+ }
2857
+ }
2858
+ if (originalObjectId) {
2859
+ element.setAttribute("data-slide-object-id", originalObjectId);
2860
+ } else {
2861
+ element.removeAttribute("data-slide-object-id");
2862
+ }
2863
+ };
2873
2864
 
2874
2865
  const stop = () => {
2875
2866
  window.removeEventListener("pointermove", onMove);
@@ -2883,13 +2874,16 @@ export default function SlideEditor({
2883
2874
  const restore = () => {
2884
2875
  if (clone) {
2885
2876
  clone.remove();
2886
- } else {
2887
- applyObjectGeometry(element, origin);
2888
2877
  }
2889
- if (originalObjectId) {
2890
- element.setAttribute("data-slide-object-id", originalObjectId);
2878
+ if (promotedToFreeform) {
2879
+ restorePromotedElement();
2891
2880
  } else {
2892
- element.removeAttribute("data-slide-object-id");
2881
+ if (!clone && origin) applyObjectGeometry(element, origin);
2882
+ if (originalObjectId) {
2883
+ element.setAttribute("data-slide-object-id", originalObjectId);
2884
+ } else {
2885
+ element.removeAttribute("data-slide-object-id");
2886
+ }
2893
2887
  }
2894
2888
  const selector = getBuilderSelector(element);
2895
2889
  if (selector) selectElementForStyling(element, selector);
@@ -2897,6 +2891,13 @@ export default function SlideEditor({
2897
2891
 
2898
2892
  const controller = createSlidesCanvasGestureController({
2899
2893
  preview: (gesture) => {
2894
+ if (!promoteForDrag()) {
2895
+ return { handled: false, reason: "unhandled" };
2896
+ }
2897
+ const dragOrigin = origin;
2898
+ if (!dragOrigin) {
2899
+ return { handled: false, reason: "unhandled" };
2900
+ }
2900
2901
  // React still receives a click after a pointer drag. Suppress that
2901
2902
  // trailing click so a moved text box does not immediately reopen
2902
2903
  // inline editing.
@@ -2910,15 +2911,19 @@ export default function SlideEditor({
2910
2911
  activeElement = clone;
2911
2912
  }
2912
2913
  applyObjectGeometry(activeElement, {
2913
- ...origin,
2914
- x: origin.x + gesture.canvasDelta.x,
2915
- y: origin.y + gesture.canvasDelta.y,
2914
+ ...dragOrigin,
2915
+ x: dragOrigin.x + gesture.canvasDelta.x,
2916
+ y: dragOrigin.y + gesture.canvasDelta.y,
2916
2917
  });
2917
2918
  const selector = getBuilderSelector(activeElement);
2918
2919
  if (selector) selectElementForStyling(activeElement, selector);
2919
2920
  return { handled: true };
2920
2921
  },
2921
2922
  commit: (gesture) => {
2923
+ if (!origin) {
2924
+ restore();
2925
+ return { handled: false, reason: "unhandled" };
2926
+ }
2922
2927
  // Keeping Option pressed is the explicit duplicate commit. If it
2923
2928
  // was released before drop, turn the gesture back into a normal move.
2924
2929
  if (clone && !gesture.duplicate) {
@@ -2927,6 +2932,15 @@ export default function SlideEditor({
2927
2932
  activeElement = element;
2928
2933
  }
2929
2934
  const html = readCurrentSlideContentHtml();
2935
+ // Markdown slides temporarily move their ReactMarkdown children into
2936
+ // an fmd canvas during promotion. Restore that live tree after
2937
+ // serialization and before the state write so React can reconcile
2938
+ // the switch to persisted raw HTML cleanly.
2939
+ if (restoreMarkdownTree) {
2940
+ removeFreeformLayoutSpacer();
2941
+ restoreMarkdownTree();
2942
+ restoreMarkdownTree = undefined;
2943
+ }
2930
2944
  if (html !== null) {
2931
2945
  onUpdateSlideRef.current({ content: html }, undefined, {
2932
2946
  persistence: "immediate",
@@ -3441,7 +3455,7 @@ export default function SlideEditor({
3441
3455
  const selected = resolveSelectedElement();
3442
3456
  const shouldShowMoveCursor =
3443
3457
  selected !== null &&
3444
- getComputedStyle(selected).position === "absolute" &&
3458
+ !isSlideCanvasShell(selected) &&
3445
3459
  isWithinSlidesCanvasEdgeMoveBand(
3446
3460
  selected.getBoundingClientRect(),
3447
3461
  e.clientX,
@@ -3490,29 +3504,32 @@ export default function SlideEditor({
3490
3504
  }
3491
3505
 
3492
3506
  const selected = resolveSelectedElement();
3507
+ const clicked = findSelectableElement(target, slideContent);
3508
+ const dragTarget = resolveSlidesCanvasDragTarget(
3509
+ selected && !isSlideCanvasShell(selected) ? selected : null,
3510
+ clicked && !isSlideCanvasShell(clicked) ? clicked : null,
3511
+ );
3493
3512
  const pointerIntent = resolveSlidesCanvasPointerIntent({
3494
- hasSelectedObject:
3495
- selected !== null &&
3496
- getComputedStyle(selected).position === "absolute",
3497
- targetWithinSelectedObject: selected?.contains(target) ?? false,
3498
- targetContainsSelectedObject: selected
3499
- ? target.contains(selected)
3513
+ hasSelectedObject: dragTarget !== null,
3514
+ targetWithinSelectedObject: dragTarget?.contains(target) ?? false,
3515
+ targetContainsSelectedObject: dragTarget
3516
+ ? target.contains(dragTarget)
3500
3517
  : false,
3501
3518
  pointerWithinMoveBand:
3502
- selected !== null &&
3519
+ dragTarget !== null &&
3503
3520
  isWithinSlidesCanvasEdgeMoveBand(
3504
- selected.getBoundingClientRect(),
3521
+ dragTarget.getBoundingClientRect(),
3505
3522
  e.clientX,
3506
3523
  e.clientY,
3507
3524
  ),
3508
3525
  targetIsEditableText: Boolean(findSmartBlock(target, slideContent)),
3509
3526
  });
3510
3527
  if (
3511
- selected &&
3528
+ dragTarget &&
3512
3529
  (pointerIntent === "move-object-body" ||
3513
3530
  pointerIntent === "move-object-perimeter")
3514
3531
  ) {
3515
- startElementDrag(e, selected, {
3532
+ startElementDrag(e, dragTarget, {
3516
3533
  preserveClickWithoutMove: pointerIntent === "move-object-body",
3517
3534
  });
3518
3535
  return;
@@ -3598,6 +3615,7 @@ export default function SlideEditor({
3598
3615
  candidates.forEach((node) => {
3599
3616
  const el = node as HTMLElement;
3600
3617
  if (el.classList.contains("fmd-layout-spacer")) return;
3618
+ if (isInlineTextElement(el)) return;
3601
3619
  const id = el.getAttribute("data-builder-id");
3602
3620
  if (!id) return;
3603
3621
  // Skip the slide-content root itself if it ever got stamped
@@ -3744,6 +3762,21 @@ export default function SlideEditor({
3744
3762
  ],
3745
3763
  );
3746
3764
 
3765
+ const clearCanvasSelection = useCallback(() => {
3766
+ if (editingEl) exitInlineEdit();
3767
+ if (multiSelection.size > 0) clearMultiSelection();
3768
+ setSelectedImg(null);
3769
+ setImageOverlay(null);
3770
+ clearSelectedElement();
3771
+ syncSelectionToAppState(null);
3772
+ }, [
3773
+ clearMultiSelection,
3774
+ clearSelectedElement,
3775
+ editingEl,
3776
+ exitInlineEdit,
3777
+ multiSelection.size,
3778
+ ]);
3779
+
3747
3780
  const handleSlideClick = useCallback(
3748
3781
  (e: React.MouseEvent) => {
3749
3782
  if (suppressNextClickRef.current) {
@@ -3776,9 +3809,7 @@ export default function SlideEditor({
3776
3809
  // pointerdown already cleared it for non-additive drags, but a click
3777
3810
  // with zero movement won't trigger pointerup with a real rect).
3778
3811
  if (slideContent && isSlideWhitespaceTarget(target, slideContent)) {
3779
- if (multiSelection.size > 0) clearMultiSelection();
3780
- clearSelectedElement();
3781
- syncSelectionToAppState(null);
3812
+ clearCanvasSelection();
3782
3813
  return;
3783
3814
  }
3784
3815
 
@@ -3834,7 +3865,7 @@ export default function SlideEditor({
3834
3865
  multiSelection,
3835
3866
  applyMultiSelection,
3836
3867
  clearMultiSelection,
3837
- clearSelectedElement,
3868
+ clearCanvasSelection,
3838
3869
  selectElementForStyling,
3839
3870
  readOnly,
3840
3871
  isHtmlSlide,
@@ -3842,6 +3873,16 @@ export default function SlideEditor({
3842
3873
  ],
3843
3874
  );
3844
3875
 
3876
+ const handleCanvasBackgroundPointerDown = useCallback(
3877
+ (e: React.PointerEvent<HTMLDivElement>) => {
3878
+ if (e.button !== 0) return;
3879
+ const target = e.target instanceof Element ? e.target : null;
3880
+ if (target && slideCanvasRef.current?.contains(target)) return;
3881
+ clearCanvasSelection();
3882
+ },
3883
+ [clearCanvasSelection],
3884
+ );
3885
+
3845
3886
  const handleSlideContextMenu = useCallback(
3846
3887
  (e: React.MouseEvent) => {
3847
3888
  const target = e.target as HTMLElement;
@@ -4096,11 +4137,9 @@ export default function SlideEditor({
4096
4137
  const slideElementSelected =
4097
4138
  !!selectedImg || !!editingEl || !!selectedStyleSnapshot;
4098
4139
 
4099
- // Dragging is only offered for elements with position: absolute — our own
4100
- // placed text boxes, and any similarly-positioned shape. left/top on a
4101
- // position: relative element is an offset from its normal flow position
4102
- // rather than an absolute coordinate, so treating "not static" as
4103
- // draggable would move relative-positioned elements by the wrong amount.
4140
+ // Resize handles are only offered for elements with position: absolute.
4141
+ // Flow objects become absolute only after a real drag crosses the gesture
4142
+ // threshold, so a stationary click keeps its existing edit/select behavior.
4104
4143
  const selectedForDrag =
4105
4144
  selectedElementRect && !editingEl ? resolveSelectedElement() : null;
4106
4145
  const isSelectedElementDraggable = selectedForDrag
@@ -4215,6 +4254,7 @@ export default function SlideEditor({
4215
4254
  <div
4216
4255
  ref={canvasTrackRef}
4217
4256
  className="flex min-h-full w-max min-w-full items-center justify-center p-2 pt-14 sm:p-4 sm:pt-14 md:p-8 md:pt-16"
4257
+ onPointerDown={handleCanvasBackgroundPointerDown}
4218
4258
  >
4219
4259
  <div
4220
4260
  ref={containerRef}
@@ -4232,6 +4272,7 @@ export default function SlideEditor({
4232
4272
  onPointerDown={handleSlidePointerDown}
4233
4273
  onPointerMove={handleSlidePointerMove}
4234
4274
  onPointerLeave={clearEdgeMoveCursor}
4275
+ onDragStart={(event) => event.preventDefault()}
4235
4276
  onDragOver={handleSlideDragOver}
4236
4277
  onDrop={handleSlideDrop}
4237
4278
  >
@@ -2,6 +2,7 @@ export {
2
2
  createSlidesCanvasGestureController,
3
3
  createSlidesCanvasInteractionCore,
4
4
  isWithinSlidesCanvasEdgeMoveBand,
5
+ resolveSlidesCanvasDragTarget,
5
6
  resolveSlidesCanvasPointerIntent,
6
7
  slidesCanvasInteractionCore,
7
8
  SLIDES_CANVAS_EDGE_MOVE_BAND,
@@ -95,6 +95,24 @@ export type SlidesCanvasPointerIntent =
95
95
  | "move-object-perimeter"
96
96
  | "none";
97
97
 
98
+ /** Prefer the current selection when the pointer is inside it; otherwise the
99
+ * object under the pointer becomes the drag candidate, including on the first
100
+ * press before its click has updated selection state. */
101
+ export function resolveSlidesCanvasDragTarget(
102
+ selectedObject: HTMLElement | null,
103
+ pointerObject: HTMLElement | null,
104
+ ): HTMLElement | null {
105
+ if (
106
+ selectedObject &&
107
+ pointerObject &&
108
+ (selectedObject.contains(pointerObject) ||
109
+ pointerObject.contains(selectedObject))
110
+ ) {
111
+ return selectedObject;
112
+ }
113
+ return pointerObject ?? selectedObject;
114
+ }
115
+
98
116
  /**
99
117
  * Slides supplies hit testing and this policy decision, while the shared
100
118
  * gesture controller owns threshold, coordinate, modifier, and resize math.
@@ -0,0 +1,85 @@
1
+ import { findEnclosingList } from "./bullet-editing";
2
+
3
+ /** Inline markup changes presentation, but does not create a canvas target. */
4
+ const INLINE_TEXT_TAGS = new Set([
5
+ "SPAN",
6
+ "STRONG",
7
+ "EM",
8
+ "B",
9
+ "I",
10
+ "U",
11
+ "A",
12
+ "BR",
13
+ "CODE",
14
+ "SUB",
15
+ "SUP",
16
+ "MARK",
17
+ "SMALL",
18
+ "S",
19
+ "FONT",
20
+ ]);
21
+
22
+ export function isInlineTextElement(element: Element): boolean {
23
+ return INLINE_TEXT_TAGS.has(element.tagName);
24
+ }
25
+
26
+ export function shouldStampBuilderId(element: HTMLElement): boolean {
27
+ return (
28
+ !element.classList.contains("fmd-layout-spacer") &&
29
+ !isInlineTextElement(element)
30
+ );
31
+ }
32
+
33
+ /**
34
+ * A text leaf is a block-level element whose children are text nodes or inline
35
+ * elements. Inline style runs are deliberately not text leaves themselves.
36
+ */
37
+ export function isTextLeaf(element: HTMLElement): boolean {
38
+ if (!element || isInlineTextElement(element) || element.tagName === "IMG") {
39
+ return false;
40
+ }
41
+ if (element.classList.contains("fmd-img-placeholder")) return false;
42
+ // A user-placed text box stays editable after its content is deleted.
43
+ if (element.classList.contains("fmd-text-box")) return true;
44
+ if (!element.textContent?.trim()) return false;
45
+ for (const child of Array.from(element.children)) {
46
+ if (!isInlineTextElement(child)) return false;
47
+ }
48
+ return true;
49
+ }
50
+
51
+ /** A container made only of text leaves or nested text groups. */
52
+ export function isSmartGroup(element: HTMLElement): boolean {
53
+ if (!element || isInlineTextElement(element) || element.tagName === "IMG") {
54
+ return false;
55
+ }
56
+ if (element.classList.contains("fmd-img-placeholder")) return false;
57
+ const children = Array.from(element.children);
58
+ if (children.length < 2) return false;
59
+ if (!element.textContent?.trim()) return false;
60
+ for (const child of children) {
61
+ const childElement = child as HTMLElement;
62
+ if (childElement.tagName === "IMG") return false;
63
+ if (childElement.classList.contains("fmd-img-placeholder")) return false;
64
+ if (!isTextLeaf(childElement) && !isSmartGroup(childElement)) return false;
65
+ }
66
+ return true;
67
+ }
68
+
69
+ /** Resolve a click inside inline markup to the containing editable text block. */
70
+ export function findSmartBlock(
71
+ target: HTMLElement,
72
+ root: HTMLElement,
73
+ ): HTMLElement | null {
74
+ let element: HTMLElement | null = target;
75
+ while (element && root.contains(element)) {
76
+ if (isTextLeaf(element)) {
77
+ const list = findEnclosingList(element, root);
78
+ if (list) return list;
79
+ return element;
80
+ }
81
+ if (isSmartGroup(element)) return element;
82
+ element = element.parentElement;
83
+ }
84
+ return null;
85
+ }
@@ -12,6 +12,7 @@ import { useSidebarCollapsed } from "@/hooks/use-sidebar-collapsed";
12
12
  import { TAB_ID } from "@/lib/tab-id";
13
13
  import { cn } from "@/lib/utils";
14
14
 
15
+ import { GoogleDriveConnectionCta } from "../editor/GoogleDriveConnectionCta";
15
16
  import { AgentWorkIndicator } from "./AgentWorkIndicator";
16
17
  import { Header } from "./Header";
17
18
  import {
@@ -117,7 +118,9 @@ export function Layout({ children }: LayoutProps) {
117
118
  scope={deckScope}
118
119
  browserTabId={TAB_ID}
119
120
  agentPageHref="/agent"
121
+ suppressFirstRunOnboarding={isSlidesEditorRoute(location.pathname)}
120
122
  composerSlot={<CreativeContextComposerChip />}
123
+ threadFooterSlot={<GoogleDriveConnectionCta />}
121
124
  >
122
125
  <div className="agent-layout-shell flex h-screen w-full overflow-hidden bg-background text-foreground">
123
126
  {sidebarOpen && (