@agent-native/core 0.149.1 → 0.149.3

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 (422) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +54 -488
  3. package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +136 -0
  4. package/corpus/templates/analytics/.agents/skills/actions/references/examples.md +94 -0
  5. package/corpus/templates/analytics/.agents/skills/actions/references/provider-apis.md +95 -0
  6. package/corpus/templates/analytics/.agents/skills/create-skill/SKILL.md +9 -35
  7. package/corpus/templates/analytics/.agents/skills/creative-context/SKILL.md +4 -2
  8. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +2 -0
  9. package/corpus/templates/analytics/.agents/skills/frontend-design/SKILL.md +4 -5
  10. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +18 -31
  11. package/corpus/templates/analytics/AGENTS.md +25 -32
  12. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
  13. package/corpus/templates/analytics/app/global.css +1 -1
  14. package/corpus/templates/analytics/app/i18n-data.ts +37 -0
  15. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
  16. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
  17. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
  18. package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
  19. package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  20. package/corpus/templates/analytics/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  21. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +54 -488
  22. package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +136 -0
  23. package/corpus/templates/assets/.agents/skills/actions/references/examples.md +94 -0
  24. package/corpus/templates/assets/.agents/skills/actions/references/provider-apis.md +95 -0
  25. package/corpus/templates/assets/.agents/skills/create-skill/SKILL.md +9 -35
  26. package/corpus/templates/assets/.agents/skills/creative-context/SKILL.md +4 -2
  27. package/corpus/templates/assets/.agents/skills/frontend-design/SKILL.md +4 -5
  28. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +18 -31
  29. package/corpus/templates/assets/AGENTS.md +6 -9
  30. package/corpus/templates/assets/app/global.css +1 -1
  31. package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  32. package/corpus/templates/assets/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  33. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +54 -488
  34. package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +136 -0
  35. package/corpus/templates/brain/.agents/skills/actions/references/examples.md +94 -0
  36. package/corpus/templates/brain/.agents/skills/actions/references/provider-apis.md +95 -0
  37. package/corpus/templates/brain/.agents/skills/create-skill/SKILL.md +9 -35
  38. package/corpus/templates/brain/.agents/skills/frontend-design/SKILL.md +4 -5
  39. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +18 -31
  40. package/corpus/templates/brain/AGENTS.md +5 -12
  41. package/corpus/templates/brain/app/global.css +1 -1
  42. package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  43. package/corpus/templates/brain/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  44. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +54 -488
  45. package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +136 -0
  46. package/corpus/templates/calendar/.agents/skills/actions/references/examples.md +94 -0
  47. package/corpus/templates/calendar/.agents/skills/actions/references/provider-apis.md +95 -0
  48. package/corpus/templates/calendar/.agents/skills/create-skill/SKILL.md +9 -35
  49. package/corpus/templates/calendar/.agents/skills/frontend-design/SKILL.md +4 -5
  50. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +18 -31
  51. package/corpus/templates/calendar/AGENTS.md +8 -9
  52. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  53. package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  54. package/corpus/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  55. package/corpus/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  56. package/corpus/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  57. package/corpus/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  58. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  59. package/corpus/templates/chat/AGENTS.md +32 -80
  60. package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  61. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +54 -488
  62. package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +136 -0
  63. package/corpus/templates/clips/.agents/skills/actions/references/examples.md +94 -0
  64. package/corpus/templates/clips/.agents/skills/actions/references/provider-apis.md +95 -0
  65. package/corpus/templates/clips/.agents/skills/create-skill/SKILL.md +9 -35
  66. package/corpus/templates/clips/.agents/skills/frontend-design/SKILL.md +4 -5
  67. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +18 -31
  68. package/corpus/templates/clips/AGENTS.md +25 -31
  69. package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
  70. package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
  71. package/corpus/templates/clips/desktop/src/app.tsx +274 -137
  72. package/corpus/templates/clips/desktop/src/styles.css +35 -0
  73. package/corpus/templates/content/.agents/skills/actions/SKILL.md +54 -488
  74. package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +136 -0
  75. package/corpus/templates/content/.agents/skills/actions/references/examples.md +94 -0
  76. package/corpus/templates/content/.agents/skills/actions/references/provider-apis.md +95 -0
  77. package/corpus/templates/content/.agents/skills/create-skill/SKILL.md +9 -35
  78. package/corpus/templates/content/.agents/skills/creative-context/SKILL.md +4 -2
  79. package/corpus/templates/content/.agents/skills/frontend-design/SKILL.md +4 -5
  80. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +18 -31
  81. package/corpus/templates/content/AGENTS.md +5 -9
  82. package/corpus/templates/content/app/global.css +1 -0
  83. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +18 -31
  84. package/corpus/templates/crm/AGENTS.md +7 -11
  85. package/corpus/templates/crm/app/global.css +1 -1
  86. package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  87. package/corpus/templates/crm/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  88. package/corpus/templates/design/.agents/skills/actions/SKILL.md +54 -488
  89. package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +136 -0
  90. package/corpus/templates/design/.agents/skills/actions/references/examples.md +94 -0
  91. package/corpus/templates/design/.agents/skills/actions/references/provider-apis.md +95 -0
  92. package/corpus/templates/design/.agents/skills/create-skill/SKILL.md +9 -35
  93. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +4 -2
  94. package/corpus/templates/design/.agents/skills/frontend-design/SKILL.md +4 -5
  95. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +18 -31
  96. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +70 -3
  97. package/corpus/templates/design/AGENTS.md +13 -21
  98. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +12 -0
  99. package/corpus/templates/design/app/components/design/LayersPanel.tsx +19 -2
  100. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +115 -7
  101. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +82 -7
  102. package/corpus/templates/design/app/components/design/design-trace.ts +120 -0
  103. package/corpus/templates/design/app/components/design/dnd-debug.ts +2 -1
  104. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +40 -26
  105. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -1
  106. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +12 -3
  107. package/corpus/templates/design/app/components/design/multi-screen/screen-content-cache.ts +4 -0
  108. package/corpus/templates/design/app/components/design/multi-screen/types.ts +7 -0
  109. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -2
  110. package/corpus/templates/design/app/components/layout/Sidebar.tsx +2 -2
  111. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +3 -0
  112. package/corpus/templates/design/app/i18n-data.ts +1 -0
  113. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +5 -5
  114. package/corpus/templates/design/app/pages/DesignSystems.tsx +3 -2
  115. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +100 -13
  116. package/corpus/templates/design/app/pages/design-editor/cross-screen-text-color.ts +1 -1
  117. package/corpus/templates/design/app/pages/design-editor/data-operations.ts +14 -0
  118. package/corpus/templates/design/changelog/2026-08-10-marquee-selecting-layers-far-down-a-tall-screen-now-selects-.md +6 -0
  119. package/corpus/templates/design/changelog/2026-08-10-the-frame-tool-draws-a-plain-frame-by-default-with-screen-av.md +6 -0
  120. package/corpus/templates/design/e2e/global-setup.ts +27 -32
  121. package/corpus/templates/design/playwright.config.ts +3 -5
  122. package/corpus/templates/design/shared/breakpoint-media.ts +9 -2
  123. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +54 -488
  124. package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +136 -0
  125. package/corpus/templates/dispatch/.agents/skills/actions/references/examples.md +94 -0
  126. package/corpus/templates/dispatch/.agents/skills/actions/references/provider-apis.md +95 -0
  127. package/corpus/templates/dispatch/.agents/skills/create-skill/SKILL.md +9 -35
  128. package/corpus/templates/dispatch/.agents/skills/frontend-design/SKILL.md +4 -5
  129. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +18 -31
  130. package/corpus/templates/dispatch/AGENTS.md +9 -16
  131. package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  132. package/corpus/templates/factory/.agents/skills/actions/SKILL.md +54 -488
  133. package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +136 -0
  134. package/corpus/templates/factory/.agents/skills/actions/references/examples.md +94 -0
  135. package/corpus/templates/factory/.agents/skills/actions/references/provider-apis.md +95 -0
  136. package/corpus/templates/factory/.agents/skills/create-skill/SKILL.md +9 -35
  137. package/corpus/templates/factory/.agents/skills/frontend-design/SKILL.md +4 -5
  138. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +18 -31
  139. package/corpus/templates/factory/AGENTS.md +10 -24
  140. package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  141. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +54 -488
  142. package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +136 -0
  143. package/corpus/templates/forms/.agents/skills/actions/references/examples.md +94 -0
  144. package/corpus/templates/forms/.agents/skills/actions/references/provider-apis.md +95 -0
  145. package/corpus/templates/forms/.agents/skills/create-skill/SKILL.md +9 -35
  146. package/corpus/templates/forms/.agents/skills/frontend-design/SKILL.md +4 -5
  147. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +18 -31
  148. package/corpus/templates/forms/AGENTS.md +11 -17
  149. package/corpus/templates/forms/app/global.css +1 -0
  150. package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  151. package/corpus/templates/forms/changelog/2026-08-11-full-page-chat-composers-stay-at-a-focused-750px-width.md +6 -0
  152. package/corpus/templates/macros/.agents/skills/frontend-design/SKILL.md +4 -5
  153. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +18 -31
  154. package/corpus/templates/macros/AGENTS.md +8 -8
  155. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +54 -488
  156. package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +136 -0
  157. package/corpus/templates/mail/.agents/skills/actions/references/examples.md +94 -0
  158. package/corpus/templates/mail/.agents/skills/actions/references/provider-apis.md +95 -0
  159. package/corpus/templates/mail/.agents/skills/create-skill/SKILL.md +9 -35
  160. package/corpus/templates/mail/.agents/skills/frontend-design/SKILL.md +4 -5
  161. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +18 -31
  162. package/corpus/templates/mail/AGENTS.md +5 -6
  163. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +54 -488
  164. package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +136 -0
  165. package/corpus/templates/plan/.agents/skills/actions/references/examples.md +94 -0
  166. package/corpus/templates/plan/.agents/skills/actions/references/provider-apis.md +95 -0
  167. package/corpus/templates/plan/.agents/skills/create-skill/SKILL.md +9 -35
  168. package/corpus/templates/plan/.agents/skills/frontend-design/SKILL.md +4 -5
  169. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +18 -31
  170. package/corpus/templates/plan/AGENTS.md +5 -14
  171. package/corpus/templates/plan/app/global.css +1 -0
  172. package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  173. package/corpus/templates/plan/changelog/2026-08-11-plan-no-longer-reloads-when-runtime-state-changes.md +6 -0
  174. package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
  175. package/corpus/templates/plan/vite.config.ts +5 -0
  176. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +54 -488
  177. package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +136 -0
  178. package/corpus/templates/slides/.agents/skills/actions/references/examples.md +94 -0
  179. package/corpus/templates/slides/.agents/skills/actions/references/provider-apis.md +95 -0
  180. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -2
  181. package/corpus/templates/slides/.agents/skills/create-skill/SKILL.md +9 -35
  182. package/corpus/templates/slides/.agents/skills/creative-context/SKILL.md +6 -3
  183. package/corpus/templates/slides/.agents/skills/deck-management/SKILL.md +6 -1
  184. package/corpus/templates/slides/.agents/skills/frontend-design/SKILL.md +4 -5
  185. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +3 -2
  186. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +3 -0
  187. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +3 -3
  188. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +18 -31
  189. package/corpus/templates/slides/AGENTS.md +33 -35
  190. package/corpus/templates/slides/actions/add-deck.ts +2 -0
  191. package/corpus/templates/slides/actions/add-slide.ts +15 -1
  192. package/corpus/templates/slides/actions/create-deck.ts +18 -6
  193. package/corpus/templates/slides/actions/get-deck-reference-context.ts +2 -2
  194. package/corpus/templates/slides/actions/get-deck.ts +83 -7
  195. package/corpus/templates/slides/actions/import-file.ts +2 -1
  196. package/corpus/templates/slides/actions/patch-deck.ts +165 -9
  197. package/corpus/templates/slides/actions/save-deck.ts +24 -1
  198. package/corpus/templates/slides/actions/update-slide.ts +1 -1
  199. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +2 -2
  200. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +1 -23
  201. package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +2 -2
  202. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +6 -6
  203. package/corpus/templates/slides/app/components/editor/AnimationsPanel.tsx +3 -3
  204. package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +4 -4
  205. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +1 -1
  206. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +432 -668
  207. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +296 -259
  208. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
  209. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
  210. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
  211. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +241 -116
  212. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  213. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +42 -14
  214. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +6 -12
  215. package/corpus/templates/slides/app/components/editor/editor-command-model.ts +35 -0
  216. package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
  217. package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
  218. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +8 -10
  219. package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
  220. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
  221. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
  222. package/corpus/templates/slides/app/context/DeckContext.tsx +2 -1
  223. package/corpus/templates/slides/app/global.css +76 -1
  224. package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
  225. package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
  226. package/corpus/templates/slides/app/lib/create-deck-generation.ts +52 -23
  227. package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
  228. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +104 -3
  229. package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -33
  230. package/corpus/templates/slides/app/pages/DesignSystems.tsx +2 -2
  231. package/corpus/templates/slides/app/pages/Index.tsx +315 -151
  232. package/corpus/templates/slides/app/root.tsx +43 -0
  233. package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
  234. package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
  235. package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
  236. package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
  237. package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
  238. package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
  239. package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
  240. package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
  241. package/corpus/templates/slides/changelog/2026-08-11-faster-slides-chat-deck-reads.md +6 -0
  242. package/corpus/templates/slides/changelog/2026-08-11-slides-chat-can-read-one-slide-without-loading-the-full-deck.md +6 -0
  243. package/corpus/templates/slides/changelog/2026-08-11-slides-clearly-indicate-when-an-ai-agent-is-editing-alongsid.md +6 -0
  244. package/corpus/templates/slides/package.json +1 -0
  245. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  246. package/corpus/templates/slides/server/lib/validate-slide-animations.ts +163 -0
  247. package/corpus/templates/slides/server/plugins/agent-chat.ts +8 -5
  248. package/corpus/templates/slides/shared/api.ts +47 -15
  249. package/corpus/templates/slides/shared/deck-title.ts +135 -0
  250. package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +54 -488
  251. package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +136 -0
  252. package/corpus/templates/tasks/.agents/skills/actions/references/examples.md +94 -0
  253. package/corpus/templates/tasks/.agents/skills/actions/references/provider-apis.md +95 -0
  254. package/corpus/templates/tasks/.agents/skills/agent-watchdog/SKILL.md +1 -0
  255. package/corpus/templates/tasks/.agents/skills/create-skill/SKILL.md +9 -35
  256. package/corpus/templates/tasks/.agents/skills/efficient-fable/SKILL.md +1 -0
  257. package/corpus/templates/tasks/.agents/skills/efficient-frontier/SKILL.md +1 -0
  258. package/corpus/templates/tasks/.agents/skills/frontend-design/SKILL.md +4 -5
  259. package/corpus/templates/tasks/.agents/skills/plan-arbiter/SKILL.md +1 -0
  260. package/corpus/templates/tasks/.agents/skills/plow-ahead/SKILL.md +1 -0
  261. package/corpus/templates/tasks/.agents/skills/quick-recap/SKILL.md +1 -0
  262. package/corpus/templates/tasks/.agents/skills/read-the-damn-docs/SKILL.md +1 -0
  263. package/corpus/templates/tasks/.agents/skills/stay-within-limits/SKILL.md +1 -0
  264. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +18 -31
  265. package/corpus/templates/tasks/AGENTS.md +5 -20
  266. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
  267. package/dist/agent/engine/ai-sdk-engine.js +1 -1
  268. package/dist/agent/production-agent.d.ts +21 -1
  269. package/dist/agent/production-agent.js +505 -355
  270. package/dist/agent/run-manager.js +156 -14
  271. package/dist/agent/run-store.d.ts +11 -10
  272. package/dist/agent/run-store.js +17 -13
  273. package/dist/agent/tool-approval-store.d.ts +18 -0
  274. package/dist/agent/tool-approval-store.js +122 -0
  275. package/dist/agent/types.d.ts +1 -1
  276. package/dist/cli/code-agent-executor.js +11 -2
  277. package/dist/client/AgentPanel.js +31 -6
  278. package/dist/client/AssistantChat.d.ts +9 -4
  279. package/dist/client/AssistantChat.js +221 -149
  280. package/dist/client/active-run-state.d.ts +9 -1
  281. package/dist/client/active-run-state.js +34 -3
  282. package/dist/client/agent-chat/index.d.ts +1 -1
  283. package/dist/client/agent-chat-adapter.d.ts +8 -0
  284. package/dist/client/agent-chat-adapter.js +125 -28
  285. package/dist/client/agent-chat.d.ts +1 -1
  286. package/dist/client/chat/markdown-renderer.js +14 -2
  287. package/dist/client/chat/message-components.d.ts +13 -3
  288. package/dist/client/chat/message-components.js +57 -52
  289. package/dist/client/chat/run-recovery.d.ts +1 -0
  290. package/dist/client/chat/run-recovery.js +8 -0
  291. package/dist/client/chat/tool-call-display.d.ts +5 -5
  292. package/dist/client/chat/tool-call-display.js +9 -212
  293. package/dist/client/chat-first/apps-rail.js +3 -3
  294. package/dist/client/chat-first/browser-pane.js +4 -1
  295. package/dist/client/chat-first/chat-history.d.ts +3 -1
  296. package/dist/client/chat-first/chat-history.js +7 -4
  297. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  298. package/dist/client/chat-first/surface-tabs.js +52 -41
  299. package/dist/client/chat-first/types.d.ts +3 -0
  300. package/dist/client/chat-first.d.ts +10 -0
  301. package/dist/client/chat-first.js +37 -2
  302. package/dist/client/components/ui/message-scroller.js +4 -1
  303. package/dist/client/i18n.js +3 -1
  304. package/dist/client/resources/McpConnectionSuggestion.js +9 -1
  305. package/dist/client/sse-event-processor.d.ts +4 -2
  306. package/dist/client/sse-event-processor.js +46 -6
  307. package/dist/client/use-chat-models.js +156 -80
  308. package/dist/client/use-run-stuck-detection.d.ts +2 -0
  309. package/dist/client/use-run-stuck-detection.js +85 -9
  310. package/dist/collab/awareness.d.ts +2 -2
  311. package/dist/collab/routes.d.ts +1 -1
  312. package/dist/collab/struct-routes.d.ts +1 -1
  313. package/dist/integrations/adapters/slack.d.ts +0 -4
  314. package/dist/integrations/adapters/slack.js +6 -30
  315. package/dist/integrations/catalog.js +1 -1
  316. package/dist/integrations/pending-tasks-store.d.ts +0 -8
  317. package/dist/integrations/pending-tasks-store.js +0 -21
  318. package/dist/integrations/slack-manifest.d.ts +2 -2
  319. package/dist/integrations/slack-manifest.js +0 -3
  320. package/dist/integrations/webhook-handler.js +1 -1
  321. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  322. package/dist/observability/routes.d.ts +3 -3
  323. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  324. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  325. package/dist/scripts/docs/source-search.js +2 -2
  326. package/dist/server/agent-chat/framework-prompts.js +7 -2
  327. package/dist/server/agent-chat/prompt-resources.js +2 -2
  328. package/dist/server/agent-chat/skill-frontmatter.d.ts +31 -2
  329. package/dist/server/agent-chat/skill-frontmatter.js +41 -15
  330. package/dist/server/agent-chat-plugin.js +3 -3
  331. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  332. package/dist/server/agents-bundle.d.ts +16 -19
  333. package/dist/server/agents-bundle.js +19 -35
  334. package/dist/server/auth.js +262 -31
  335. package/dist/server/better-auth-instance.d.ts +1 -0
  336. package/dist/server/better-auth-instance.js +17 -2
  337. package/dist/server/onboarding-html.js +56 -9
  338. package/dist/server/prompts/framework-core-compact.d.ts +6 -1
  339. package/dist/server/prompts/framework-core-compact.js +12 -9
  340. package/dist/server/prompts/framework-core.d.ts +6 -1
  341. package/dist/server/prompts/framework-core.js +11 -9
  342. package/dist/server/prompts/index.d.ts +1 -1
  343. package/dist/server/prompts/index.js +1 -1
  344. package/dist/server/prompts/shared-rules.d.ts +15 -11
  345. package/dist/server/prompts/shared-rules.js +17 -13
  346. package/dist/server/realtime-token.d.ts +1 -1
  347. package/dist/server/transcribe-voice.d.ts +1 -1
  348. package/dist/shared/reasoning-effort.d.ts +8 -8
  349. package/dist/shared/reasoning-effort.js +10 -10
  350. package/dist/shared/streaming-text-smoothing.js +41 -11
  351. package/dist/styles/agent-native.css +19 -32
  352. package/dist/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  353. package/dist/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  354. package/dist/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  355. package/dist/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  356. package/dist/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  357. package/dist/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  358. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  359. package/dist/templates/chat/AGENTS.md +32 -80
  360. package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  361. package/dist/templates/default/.agents/skills/actions/SKILL.md +54 -488
  362. package/dist/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
  363. package/dist/templates/default/.agents/skills/actions/references/examples.md +94 -0
  364. package/dist/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
  365. package/dist/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
  366. package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
  367. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
  368. package/dist/templates/headless/.agents/skills/actions/SKILL.md +54 -488
  369. package/dist/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
  370. package/dist/templates/headless/.agents/skills/actions/references/examples.md +94 -0
  371. package/dist/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
  372. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
  373. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
  374. package/dist/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
  375. package/dist/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
  376. package/dist/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
  377. package/dist/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
  378. package/dist/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
  379. package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
  380. package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
  381. package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
  382. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
  383. package/dist/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
  384. package/dist/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
  385. package/dist/templates/workspace-core/AGENTS.md +7 -16
  386. package/docs/content/messaging.mdx +6 -7
  387. package/docs/content/template-chat.mdx +1 -1
  388. package/package.json +2 -2
  389. package/src/templates/chat/.agents/skills/actions/SKILL.md +54 -488
  390. package/src/templates/chat/.agents/skills/actions/references/action-fields.md +136 -0
  391. package/src/templates/chat/.agents/skills/actions/references/examples.md +94 -0
  392. package/src/templates/chat/.agents/skills/actions/references/provider-apis.md +95 -0
  393. package/src/templates/chat/.agents/skills/create-skill/SKILL.md +9 -35
  394. package/src/templates/chat/.agents/skills/frontend-design/SKILL.md +4 -5
  395. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +18 -31
  396. package/src/templates/chat/AGENTS.md +32 -80
  397. package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  398. package/src/templates/default/.agents/skills/actions/SKILL.md +54 -488
  399. package/src/templates/default/.agents/skills/actions/references/action-fields.md +136 -0
  400. package/src/templates/default/.agents/skills/actions/references/examples.md +94 -0
  401. package/src/templates/default/.agents/skills/actions/references/provider-apis.md +95 -0
  402. package/src/templates/default/.agents/skills/create-skill/SKILL.md +9 -35
  403. package/src/templates/default/.agents/skills/frontend-design/SKILL.md +4 -5
  404. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +18 -31
  405. package/src/templates/headless/.agents/skills/actions/SKILL.md +54 -488
  406. package/src/templates/headless/.agents/skills/actions/references/action-fields.md +136 -0
  407. package/src/templates/headless/.agents/skills/actions/references/examples.md +94 -0
  408. package/src/templates/headless/.agents/skills/actions/references/provider-apis.md +95 -0
  409. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +18 -31
  410. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +54 -488
  411. package/src/templates/workspace-core/.agents/skills/actions/references/action-fields.md +136 -0
  412. package/src/templates/workspace-core/.agents/skills/actions/references/examples.md +94 -0
  413. package/src/templates/workspace-core/.agents/skills/actions/references/provider-apis.md +95 -0
  414. package/src/templates/workspace-core/.agents/skills/adding-workspace-apps/SKILL.md +1 -0
  415. package/src/templates/workspace-core/.agents/skills/agent-page/SKILL.md +1 -0
  416. package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +9 -35
  417. package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +4 -5
  418. package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +21 -29
  419. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +18 -31
  420. package/src/templates/workspace-core/.agents/skills/workspace-conventions/SKILL.md +7 -17
  421. package/src/templates/workspace-core/.agents/skills/writing-agent-instructions/SKILL.md +3 -3
  422. package/src/templates/workspace-core/AGENTS.md +7 -16
@@ -13,39 +13,37 @@ import { PresenceBar } from "@agent-native/toolkit/collab-ui";
13
13
  import {
14
14
  IconArrowLeft,
15
15
  IconPlayerPlay,
16
- IconLayout,
17
16
  IconLayoutSidebar,
18
17
  IconPhoto,
19
18
  IconHistory,
20
19
  IconFolderOpen,
21
- IconSettings,
22
- IconSchema,
23
- IconPencil,
24
- IconTransform,
25
20
  IconMessage,
26
- IconBolt,
27
- IconAdjustments,
28
- IconPencilPlus,
29
- IconPin,
30
- IconTool,
31
21
  IconDownload,
32
22
  IconSun,
33
23
  IconMoon,
34
24
  IconDotsVertical,
35
25
  IconLoader2,
36
- IconArrowBackUp,
37
- IconArrowForwardUp,
26
+ IconBolt,
27
+ IconAdjustments,
28
+ IconPencilPlus,
29
+ IconPin,
30
+ IconBrandGoogle,
31
+ IconCode,
32
+ IconCopy,
33
+ IconFileTypePdf,
34
+ IconPlugConnected,
38
35
  } from "@tabler/icons-react";
39
36
  import { useTheme } from "next-themes";
40
- import { useState, useRef, useEffect } from "react";
41
- import { createPortal } from "react-dom";
37
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
42
38
  import { Link } from "react-router";
43
39
  import { toast } from "sonner";
44
40
 
41
+ import { Button } from "@/components/ui/button";
45
42
  import {
46
43
  DropdownMenu,
47
44
  DropdownMenuContent,
48
45
  DropdownMenuGroup,
46
+ DropdownMenuLabel,
49
47
  DropdownMenuItem,
50
48
  DropdownMenuTrigger,
51
49
  DropdownMenuSeparator,
@@ -56,25 +54,18 @@ import {
56
54
  TooltipTrigger,
57
55
  } from "@/components/ui/tooltip";
58
56
  import { SaveStatusIndicator } from "@/components/visual-editor";
59
- import type { Deck, Slide, SlideLayout } from "@/context/DeckContext";
60
- import {
61
- defaultSlideContent,
62
- useDecks,
63
- useSaveState,
64
- } from "@/context/DeckContext";
65
- import {
66
- ASPECT_RATIO_VALUES,
67
- type AspectRatio,
68
- DEFAULT_ASPECT_RATIO,
69
- } from "@/lib/aspect-ratios";
57
+ import type { Deck, Slide } from "@/context/DeckContext";
58
+ import { useSaveState } from "@/context/DeckContext";
70
59
  import { getDeckShareLinkOrder } from "@/lib/deck-share-links";
71
60
  import type { GoogleSlidesExportResult } from "@/lib/export-google-slides-client";
72
61
  import { parseUploadResponse } from "@/lib/upload-response";
73
- import { shortcutLabel } from "@/lib/utils";
74
62
 
75
- import { commitActiveEditThenRun } from "./commit-active-edit";
63
+ import {
64
+ registerEditorCommands,
65
+ type EditorCommand,
66
+ } from "./editor-command-model";
76
67
  import { EditorActionCluster } from "./EditorActionCluster";
77
- import { ExportMenu } from "./ExportMenu";
68
+ import { ExportMenu, type ExportMenuHandle } from "./ExportMenu";
78
69
  interface EditorToolbarProps {
79
70
  deck: Deck;
80
71
  deckId: string;
@@ -90,16 +81,11 @@ interface EditorToolbarProps {
90
81
  onToggleSidebar: () => void;
91
82
  onGenerateImage: () => void;
92
83
  onOpenAssetLibrary: () => void;
93
- imageGenButtonRef: React.RefObject<HTMLButtonElement | null>;
94
- assetsButtonRef: React.RefObject<HTMLButtonElement | null>;
95
- historyOpen: boolean;
96
84
  onShowHistory: () => void;
97
85
  historyButtonRef: React.RefObject<HTMLButtonElement | null>;
98
86
  currentSlide?: Slide;
99
- onUpdateSlide?: (
100
- updates: Partial<Omit<Slide, "id">>,
101
- slideIdOverride?: string,
102
- ) => void;
87
+ /** Host for the wide-layout style toolbar in this row. */
88
+ onWideContextToolbarSlotChange?: (element: HTMLDivElement | null) => void;
103
89
  /** Active users on the current slide (from collab awareness) */
104
90
  activeUsers?: CollabUser[];
105
91
  /** Whether the agent has a durable presence entry on this slide */
@@ -142,10 +128,6 @@ interface EditorToolbarProps {
142
128
  onExportPptx?: () => Promise<void> | void;
143
129
  /** Create the deck in the user's Google Drive as native Google Slides */
144
130
  onExportGoogleSlides?: () => Promise<GoogleSlidesExportResult>;
145
- /** Active deck aspect ratio (defaults to 16:9 when omitted) */
146
- aspectRatio?: AspectRatio;
147
- /** Change the deck's aspect ratio */
148
- onSetAspectRatio?: (ratio: AspectRatio) => void;
149
131
  /** Insert a blank slide after the current one */
150
132
  onAddEmptySlide?: () => void;
151
133
  /** Duplicate the current slide */
@@ -158,92 +140,9 @@ interface EditorToolbarProps {
158
140
  onAddSlideGeneratingChange?: (generating: boolean) => void;
159
141
  }
160
142
 
161
- const slideLayoutOptions: { value: SlideLayout; labelKey: string }[] = [
162
- { value: "title", labelKey: "editorToolbar.layoutTitle" },
163
- { value: "section", labelKey: "editorToolbar.layoutSection" },
164
- { value: "content", labelKey: "editorToolbar.layoutContent" },
165
- { value: "two-column", labelKey: "editorToolbar.layoutTwoColumn" },
166
- { value: "image", labelKey: "editorToolbar.layoutImage" },
167
- { value: "statement", labelKey: "editorToolbar.layoutStatement" },
168
- { value: "full-image", labelKey: "editorToolbar.layoutFullImage" },
169
- { value: "blank", labelKey: "editorToolbar.layoutBlank" },
170
- ];
171
-
172
- const backgroundOptions = [
173
- "bg-[#000000]",
174
- "bg-[#0a0a0a]",
175
- "bg-[#0f0f11]",
176
- "bg-[#111114]",
177
- "bg-[#141418]",
178
- "bg-gradient-to-br from-[#000000] to-[#0a0a14]",
179
- "bg-gradient-to-br from-[#0a0a0a] to-[#0f1a14]",
180
- "bg-[#ffffff]",
181
- ];
182
-
183
- const HEX_COLOR_PATTERN = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
184
143
  const TOOLBAR_ICON_BUTTON_CLASS =
185
144
  "inline-flex size-8 flex-shrink-0 items-center justify-center rounded-md transition-colors";
186
145
 
187
- /** Native <input type="color"> only accepts 3/6-digit hex — fall back to
188
- * black for gradients or other raw CSS values so the picker still opens. */
189
- function toColorInputValue(background: string | undefined): string {
190
- return background && HEX_COLOR_PATTERN.test(background)
191
- ? background
192
- : "#000000";
193
- }
194
-
195
- /** Popover anchored to a button ref */
196
- function ToolbarPopover({
197
- open,
198
- anchorRef,
199
- onClose,
200
- children,
201
- width = 160,
202
- align = "right",
203
- }: {
204
- open: boolean;
205
- anchorRef: React.RefObject<HTMLButtonElement | null>;
206
- onClose: () => void;
207
- children: React.ReactNode;
208
- width?: number;
209
- align?: "left" | "right";
210
- }) {
211
- const menuRef = useRef<HTMLDivElement>(null);
212
-
213
- useEffect(() => {
214
- if (!open) return;
215
- const handleClick = (e: MouseEvent) => {
216
- if (
217
- menuRef.current &&
218
- !menuRef.current.contains(e.target as Node) &&
219
- anchorRef.current &&
220
- !anchorRef.current.contains(e.target as Node)
221
- ) {
222
- onClose();
223
- }
224
- };
225
- document.addEventListener("mousedown", handleClick);
226
- return () => document.removeEventListener("mousedown", handleClick);
227
- }, [open, onClose, anchorRef]);
228
-
229
- if (!open || !anchorRef.current) return null;
230
- const rect = anchorRef.current.getBoundingClientRect();
231
- const vw = window.innerWidth;
232
- let left = align === "right" ? rect.right - width : rect.left;
233
- left = Math.max(8, Math.min(left, vw - width - 8));
234
-
235
- return createPortal(
236
- <div
237
- ref={menuRef}
238
- className="fixed rounded-lg border border-border bg-popover shadow-xl z-[200] max-h-[80vh] overflow-y-auto animate-in fade-in-0 zoom-in-95 duration-150 origin-top-left"
239
- style={{ top: rect.bottom + 4, left, width: Math.min(width, vw - 16) }}
240
- >
241
- {children}
242
- </div>,
243
- document.body,
244
- );
245
- }
246
-
247
146
  export default function EditorToolbar({
248
147
  deck,
249
148
  deckId,
@@ -255,12 +154,10 @@ export default function EditorToolbar({
255
154
  onToggleSidebar,
256
155
  onGenerateImage,
257
156
  onOpenAssetLibrary,
258
- imageGenButtonRef,
259
- assetsButtonRef,
260
157
  onShowHistory,
261
158
  historyButtonRef,
262
159
  currentSlide,
263
- onUpdateSlide,
160
+ onWideContextToolbarSlotChange,
264
161
  activeUsers,
265
162
  agentPresent,
266
163
  agentActive,
@@ -282,8 +179,6 @@ export default function EditorToolbar({
282
179
  onExportPdf,
283
180
  onExportPptx,
284
181
  onExportGoogleSlides,
285
- aspectRatio,
286
- onSetAspectRatio,
287
182
  onAddEmptySlide,
288
183
  onDuplicateCurrentSlide,
289
184
  currentSlideId,
@@ -336,43 +231,30 @@ export default function EditorToolbar({
336
231
  };
337
232
  }, []);
338
233
 
339
- const activeAspectRatio: AspectRatio = aspectRatio ?? DEFAULT_ASPECT_RATIO;
340
- const isCustomBackground =
341
- !!currentSlide?.background &&
342
- !backgroundOptions.includes(currentSlide.background);
343
- const [layoutOpen, setLayoutOpen] = useState(false);
344
- const layoutRef = useRef<HTMLButtonElement>(null);
345
-
346
- const handleLayoutSelect = (layout: SlideLayout) => {
347
- if (!currentSlide || !onUpdateSlide) return;
348
- if (currentSlide.layout !== layout) {
349
- onUpdateSlide({ layout, content: defaultSlideContent[layout] });
350
- }
351
- setLayoutOpen(false);
352
- };
353
- const [toolsOpen, setToolsOpen] = useState(false);
354
234
  // The contextual toolbar hosts the action cluster whenever it is on screen.
355
235
  // That row rides on SlideEditor, which only mounts for a real slide, so an
356
236
  // empty deck must keep this fallback or it has no way to add one.
357
237
  const contextToolbarVisible = canEdit && Boolean(currentSlide);
358
- const { undo, redo, canUndo, canRedo, getDeck } = useDecks();
359
238
  const fileInputRef = useRef<HTMLInputElement>(null);
239
+ const exportMenuRef = useRef<ExportMenuHandle>(null);
240
+ const titleMeasureRef = useRef<HTMLSpanElement>(null);
241
+ const [titleInputWidth, setTitleInputWidth] = useState(96);
360
242
  const [importing, setImporting] = useState(false);
361
243
  const { setTheme, resolvedTheme } = useTheme();
362
244
  const [themeMounted, setThemeMounted] = useState(false);
363
245
  useEffect(() => setThemeMounted(true), []);
364
246
  const isDark = themeMounted ? resolvedTheme === "dark" : false;
365
- // The secondary tools share an "active when something is on" indicator so
366
- // the dot on the consolidated button reflects any of them.
367
- const anyToolActive = Boolean(
368
- animationsOpen || tweaksOpen || drawMode || pinMode,
369
- );
370
-
371
- const closeAll = () => {
372
- setLayoutOpen(false);
373
- setToolsOpen(false);
374
- };
375
247
 
248
+ useLayoutEffect(() => {
249
+ const measuredWidth =
250
+ titleMeasureRef.current?.getBoundingClientRect().width;
251
+ if (typeof measuredWidth !== "number" || !Number.isFinite(measuredWidth)) {
252
+ return;
253
+ }
254
+ setTitleInputWidth(
255
+ Math.min(500, Math.max(96, Math.ceil(measuredWidth) + 16)),
256
+ );
257
+ }, [deckTitle]);
376
258
  const handleImportFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
377
259
  const file = e.target.files?.[0];
378
260
  if (!file) return;
@@ -449,8 +331,202 @@ export default function EditorToolbar({
449
331
  }
450
332
  };
451
333
 
334
+ const editorCommands = useMemo<EditorCommand[]>(() => {
335
+ const commands: EditorCommand[] = [];
336
+ if (canEdit) {
337
+ commands.push(
338
+ {
339
+ id: "generate-image",
340
+ group: "media",
341
+ label: t("editorToolbar.generateImage"),
342
+ keywords: ["image", "media", "ai"],
343
+ icon: IconPhoto,
344
+ run: onGenerateImage,
345
+ },
346
+ {
347
+ id: "asset-library",
348
+ group: "media",
349
+ label: t("editorToolbar.assetLibrary"),
350
+ keywords: ["image", "media", "assets"],
351
+ icon: IconFolderOpen,
352
+ run: onOpenAssetLibrary,
353
+ },
354
+ );
355
+ if (currentSlide && onToggleAnimations) {
356
+ commands.push({
357
+ id: "element-animations",
358
+ group: "slideTools",
359
+ label: t("editorToolbar.elementAnimations"),
360
+ keywords: ["animation", "motion"],
361
+ icon: IconBolt,
362
+ active: animationsOpen,
363
+ run: onToggleAnimations,
364
+ });
365
+ }
366
+ if (onToggleTweaks) {
367
+ commands.push({
368
+ id: "slide-tweaks",
369
+ group: "slideTools",
370
+ label: t("editorToolbar.tweaks"),
371
+ keywords: ["style", "inspect", "adjust"],
372
+ icon: IconAdjustments,
373
+ active: tweaksOpen,
374
+ run: onToggleTweaks,
375
+ });
376
+ }
377
+ if (onToggleDrawMode) {
378
+ commands.push({
379
+ id: "draw-on-slide",
380
+ group: "slideTools",
381
+ label: t("editorToolbar.drawOnSlide"),
382
+ keywords: ["annotate", "draw"],
383
+ icon: IconPencilPlus,
384
+ active: drawMode,
385
+ run: onToggleDrawMode,
386
+ });
387
+ }
388
+ if (onTogglePinMode) {
389
+ commands.push({
390
+ id: "pin-comments",
391
+ group: "slideTools",
392
+ label: t("editorToolbar.pinComments"),
393
+ keywords: ["comment", "pin"],
394
+ icon: IconPin,
395
+ active: pinMode,
396
+ run: onTogglePinMode,
397
+ });
398
+ }
399
+ }
400
+ if (onToggleComments) {
401
+ commands.push({
402
+ id: "comments",
403
+ group: "comments",
404
+ label: t("editorToolbar.comments"),
405
+ keywords: ["comment", "review"],
406
+ icon: IconMessage,
407
+ active: commentsOpen,
408
+ run: onToggleComments,
409
+ });
410
+ }
411
+
412
+ commands.push(
413
+ {
414
+ id: "download-html",
415
+ group: "deck",
416
+ label: t("editorExport.downloadHtml"),
417
+ keywords: ["export", "html", "download"],
418
+ icon: IconCode,
419
+ run: () => void exportMenuRef.current?.exportHtml(),
420
+ },
421
+ {
422
+ id: "export-pdf",
423
+ group: "deck",
424
+ label: t("editorExport.exportPdf"),
425
+ keywords: ["export", "pdf", "download"],
426
+ icon: IconFileTypePdf,
427
+ run: () => void onExportPdf?.(),
428
+ },
429
+ {
430
+ id: "export-pptx",
431
+ group: "deck",
432
+ label: t("editorExport.exportPptx"),
433
+ keywords: ["export", "powerpoint", "pptx", "download"],
434
+ icon: IconDownload,
435
+ run: () => void exportMenuRef.current?.exportPptx(),
436
+ },
437
+ );
438
+ if (onExportGoogleSlides) {
439
+ commands.push(
440
+ {
441
+ id: "connect-google",
442
+ group: "deck",
443
+ label: t("editorExport.connectGoogle"),
444
+ keywords: ["google", "drive", "connect"],
445
+ icon: IconPlugConnected,
446
+ run: () => void exportMenuRef.current?.connectGoogle(),
447
+ },
448
+ {
449
+ id: "open-in-google-slides",
450
+ group: "deck",
451
+ label: t("editorExport.openInGoogleSlides"),
452
+ keywords: ["google", "slides", "export"],
453
+ icon: IconBrandGoogle,
454
+ run: () => void exportMenuRef.current?.exportGoogleSlides(),
455
+ },
456
+ );
457
+ }
458
+ if (onDuplicateDeck) {
459
+ commands.push({
460
+ id: "duplicate-deck",
461
+ group: "deck",
462
+ label: t("editorExport.duplicateDeck"),
463
+ keywords: ["copy", "duplicate"],
464
+ icon: IconCopy,
465
+ run: onDuplicateDeck,
466
+ });
467
+ }
468
+ commands.push(
469
+ {
470
+ id: "import-file",
471
+ group: "other",
472
+ label: importing
473
+ ? t("editorToolbar.importing")
474
+ : t("editorToolbar.importFile"),
475
+ keywords: ["import", "pptx", "docx", "pdf"],
476
+ icon: importing ? IconLoader2 : IconDownload,
477
+ run: () => fileInputRef.current?.click(),
478
+ },
479
+ {
480
+ id: "saved-versions",
481
+ group: "other",
482
+ label: t("editorToolbar.savedVersions"),
483
+ keywords: ["history", "versions", "restore"],
484
+ icon: IconHistory,
485
+ run: onShowHistory,
486
+ },
487
+ {
488
+ id: "toggle-theme",
489
+ group: "other",
490
+ label: isDark
491
+ ? t("editorToolbar.lightTheme")
492
+ : t("editorToolbar.darkTheme"),
493
+ keywords: ["theme", "dark", "light", "mode"],
494
+ icon: isDark ? IconSun : IconMoon,
495
+ run: () => setTheme(isDark ? "light" : "dark"),
496
+ },
497
+ );
498
+ return commands;
499
+ }, [
500
+ animationsOpen,
501
+ canEdit,
502
+ commentsOpen,
503
+ currentSlide,
504
+ drawMode,
505
+ importing,
506
+ isDark,
507
+ onDuplicateDeck,
508
+ onExportGoogleSlides,
509
+ onExportPdf,
510
+ onGenerateImage,
511
+ onOpenAssetLibrary,
512
+ onShowHistory,
513
+ onToggleAnimations,
514
+ onToggleComments,
515
+ onToggleDrawMode,
516
+ onTogglePinMode,
517
+ onToggleTweaks,
518
+ pinMode,
519
+ setTheme,
520
+ t,
521
+ tweaksOpen,
522
+ ]);
523
+ const editorCommandsRef = useRef<readonly EditorCommand[]>(editorCommands);
524
+ editorCommandsRef.current = editorCommands;
525
+
526
+ useEffect(() => registerEditorCommands(() => editorCommandsRef.current), []);
527
+
452
528
  return (
453
- <div className="flex h-11 shrink-0 items-center gap-1 overflow-x-auto whitespace-nowrap border-b border-border/70 bg-background/95 px-2 shadow-[0_1px_0_hsl(var(--border)/0.35)] backdrop-blur sm:px-3">
529
+ <div className="deck-editor-toolbar flex h-11 shrink-0 items-center gap-1 overflow-x-auto whitespace-nowrap bg-background px-2 sm:px-3">
454
530
  {/* Back button */}
455
531
  <Tooltip>
456
532
  <TooltipTrigger asChild>
@@ -502,22 +578,32 @@ export default function EditorToolbar({
502
578
  )}
503
579
 
504
580
  {/* Deck title */}
581
+ <span
582
+ ref={titleMeasureRef}
583
+ aria-hidden="true"
584
+ className="pointer-events-none fixed -left-[9999px] whitespace-pre text-sm font-medium opacity-0"
585
+ >
586
+ {deckTitle || " "}
587
+ </span>
505
588
  <input
506
589
  type="text"
507
590
  value={deckTitle}
508
591
  onChange={(e) => onTitleChange(e.target.value)}
509
- className="bg-transparent text-sm font-medium text-foreground/90 border-none outline-none focus:text-foreground min-w-0 w-24 sm:w-auto flex-shrink"
592
+ style={{ width: `${titleInputWidth}px` }}
593
+ className="min-w-0 max-w-[500px] flex-shrink bg-transparent text-sm font-medium text-foreground/90 outline-none focus:text-foreground"
510
594
  spellCheck={false}
511
595
  />
512
596
 
513
- {/* Slide counter */}
514
- <span className="text-xs text-muted-foreground/70 flex-shrink-0 hidden sm:inline">
515
- {currentSlideIndex + 1}/{slideCount}
516
- </span>
517
-
518
597
  {/* Spacer */}
519
598
  <div className="flex-1 min-w-2" />
520
599
 
600
+ <div
601
+ ref={onWideContextToolbarSlotChange}
602
+ data-context-toolbar-host="wide"
603
+ data-context-toolbar-visible={contextToolbarVisible ? "true" : "false"}
604
+ className="deck-editor-context-toolbar-host deck-editor-context-toolbar-host--wide"
605
+ />
606
+
521
607
  {/* "View only" badge — mirrors Google Slides' viewer chrome */}
522
608
  {!canEdit && (
523
609
  <span className="flex-shrink-0 inline-flex items-center gap-1 rounded-full border border-border bg-muted/50 px-2.5 py-1 text-xs font-medium text-muted-foreground">
@@ -525,407 +611,6 @@ export default function EditorToolbar({
525
611
  </span>
526
612
  )}
527
613
 
528
- {/* Slide settings cog menu */}
529
- {canEdit && currentSlide && onUpdateSlide && (
530
- <>
531
- <Tooltip>
532
- <TooltipTrigger asChild>
533
- <button
534
- ref={layoutRef}
535
- onClick={() => {
536
- closeAll();
537
- setLayoutOpen(!layoutOpen);
538
- }}
539
- className={`${TOOLBAR_ICON_BUTTON_CLASS} ${
540
- layoutOpen
541
- ? "text-foreground/90 bg-accent"
542
- : "text-muted-foreground hover:text-foreground/70 hover:bg-accent"
543
- }`}
544
- aria-label={t("editorToolbar.slideSettings")}
545
- >
546
- <IconSettings className="size-4" />
547
- </button>
548
- </TooltipTrigger>
549
- <TooltipContent>{t("editorToolbar.slideSettings")}</TooltipContent>
550
- </Tooltip>
551
- <ToolbarPopover
552
- open={layoutOpen}
553
- anchorRef={layoutRef}
554
- onClose={() => setLayoutOpen(false)}
555
- width={220}
556
- >
557
- <div className="py-1.5">
558
- {/* Layout section */}
559
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
560
- {t("editorToolbar.layout")}
561
- </div>
562
- {slideLayoutOptions.map((opt) => (
563
- <button
564
- key={opt.value}
565
- onClick={() => handleLayoutSelect(opt.value)}
566
- className={`flex items-center gap-2 w-full px-3 py-1.5 text-xs transition-colors ${
567
- currentSlide.layout === opt.value
568
- ? "text-[#609FF8] bg-accent/50"
569
- : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
570
- }`}
571
- >
572
- <IconLayout className="w-3 h-3" />
573
- {t(opt.labelKey)}
574
- </button>
575
- ))}
576
-
577
- {/* Background section */}
578
- <div className="mx-2 my-1.5 border-t border-border" />
579
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
580
- {t("editorToolbar.background")}
581
- </div>
582
- <div className="px-3 pb-2">
583
- <div className="grid grid-cols-4 gap-2">
584
- {backgroundOptions.map((bg, i) => (
585
- <button
586
- key={i}
587
- onClick={() => {
588
- onUpdateSlide!({ background: bg });
589
- }}
590
- className={`w-10 h-7 rounded-md border transition-all ${bg} ${
591
- currentSlide.background === bg
592
- ? "border-[#609FF8] ring-1 ring-[#609FF8]/30"
593
- : "border-border hover:border-foreground/20"
594
- }`}
595
- />
596
- ))}
597
- <label
598
- className={`relative w-10 h-7 rounded-md border cursor-pointer overflow-hidden transition-all ${
599
- isCustomBackground
600
- ? "border-[#609FF8] ring-1 ring-[#609FF8]/30"
601
- : "border-border hover:border-foreground/20"
602
- }`}
603
- style={{
604
- background: isCustomBackground
605
- ? currentSlide.background
606
- : "conic-gradient(from 180deg, #ff0000, #ffff00, #00ff00, #00ffff, #0000ff, #ff00ff, #ff0000)",
607
- }}
608
- title={t("editorToolbar.customColor")}
609
- >
610
- <input
611
- type="color"
612
- value={toColorInputValue(currentSlide.background)}
613
- onChange={(e) => {
614
- onUpdateSlide!({ background: e.target.value });
615
- }}
616
- className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
617
- aria-label={t("editorToolbar.customColor")}
618
- />
619
- </label>
620
- </div>
621
- </div>
622
-
623
- {/* Image & Assets section */}
624
- <div className="mx-2 my-1.5 border-t border-border" />
625
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
626
- {t("editorToolbar.media")}
627
- </div>
628
- <button
629
- ref={imageGenButtonRef}
630
- onClick={() => {
631
- onGenerateImage();
632
- setLayoutOpen(false);
633
- }}
634
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
635
- >
636
- <IconPhoto className="w-3 h-3" />
637
- {t("editorToolbar.generateImage")}
638
- </button>
639
- <button
640
- ref={assetsButtonRef}
641
- onClick={() => {
642
- onOpenAssetLibrary();
643
- setLayoutOpen(false);
644
- }}
645
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
646
- >
647
- <IconFolderOpen className="w-3 h-3" />
648
- {t("editorToolbar.assetLibrary")}
649
- </button>
650
-
651
- {/* Diagrams section */}
652
- <div className="mx-2 my-1.5 border-t border-border" />
653
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
654
- {t("editorToolbar.diagrams")}
655
- </div>
656
- <button
657
- onClick={async () => {
658
- if (!onUpdateSlide || !currentSlide) return;
659
- // Pin the slide this insert targets before the async
660
- // import/parse below, so navigating to another slide in the
661
- // meantime can't redirect where the diagram lands.
662
- const targetSlideId = currentSlide.id;
663
- const defaultMermaidDefinition = `graph TD
664
- A[Start] --> B{Decision}
665
- B -->|Yes| C[Action A]
666
- B -->|No| D[Action B]
667
- C --> E[End]
668
- D --> E`;
669
- try {
670
- const { convertMermaidToExcalidraw } =
671
- await import("./MermaidToExcalidrawPanel");
672
- const data = await convertMermaidToExcalidraw(
673
- defaultMermaidDefinition,
674
- );
675
- // The target slide may have been deleted while the import/
676
- // parse above was in flight — patch-deck rejects the whole
677
- // batch for a missing slide id, so re-check with the live
678
- // deck (not the stale `currentSlide` this closure captured)
679
- // and drop the result instead of enqueueing a dead op.
680
- const stillExists = getDeck(deckId)?.slides.some(
681
- (s) => s.id === targetSlideId,
682
- );
683
- if (!stillExists) return;
684
- onUpdateSlide({ excalidrawData: data }, targetSlideId);
685
- setLayoutOpen(false);
686
- } catch (err) {
687
- console.error("Insert Mermaid diagram failed:", err);
688
- toast.error(t("editorToolbar.insertMermaidFailed"), {
689
- description:
690
- err instanceof Error ? err.message : undefined,
691
- });
692
- }
693
- }}
694
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
695
- >
696
- <IconSchema className="w-3 h-3" />
697
- {t("editorToolbar.insertMermaidDiagram")}
698
- </button>
699
- <button
700
- onClick={() => {
701
- if (!onUpdateSlide) return;
702
- onUpdateSlide({
703
- excalidrawData: JSON.stringify({
704
- elements: [],
705
- appState: { viewBackgroundColor: "transparent" },
706
- files: {},
707
- }),
708
- });
709
- setLayoutOpen(false);
710
- }}
711
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
712
- >
713
- <IconPencil className="w-3 h-3" />
714
- {t("editorToolbar.excalidrawCanvas")}
715
- </button>
716
- {typeof currentSlide?.content === "string" &&
717
- currentSlide.content.includes('class="mermaid"') && (
718
- <button
719
- onClick={async () => {
720
- if (!onUpdateSlide || !currentSlide) return;
721
- // Same pinning as the insert handler above.
722
- const targetSlideId = currentSlide.id;
723
- try {
724
- const match = currentSlide.content.match(
725
- /<div\s+class="mermaid"[^>]*>([\s\S]*?)<\/div>/i,
726
- );
727
- if (!match) return;
728
- const { convertMermaidToExcalidraw } =
729
- await import("./MermaidToExcalidrawPanel");
730
- const data = await convertMermaidToExcalidraw(
731
- match[1].trim(),
732
- );
733
- // Same stale-target guard as the insert handler above.
734
- const stillExists = getDeck(deckId)?.slides.some(
735
- (s) => s.id === targetSlideId,
736
- );
737
- if (!stillExists) return;
738
- onUpdateSlide({ excalidrawData: data }, targetSlideId);
739
- setLayoutOpen(false);
740
- } catch (err) {
741
- console.error("Mermaid to Excalidraw failed:", err);
742
- toast.error(t("editorToolbar.convertMermaidFailed"), {
743
- description:
744
- err instanceof Error ? err.message : undefined,
745
- });
746
- }
747
- }}
748
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-[hsl(var(--accent-cyan))] hover:bg-accent/50 transition-colors"
749
- >
750
- <IconTransform className="w-3 h-3" />
751
- {t("editorToolbar.convertMermaidToExcalidraw")}
752
- </button>
753
- )}
754
- {currentSlide?.excalidrawData && (
755
- <button
756
- onClick={() => {
757
- if (!onUpdateSlide) return;
758
- onUpdateSlide({ excalidrawData: undefined });
759
- setLayoutOpen(false);
760
- }}
761
- className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:text-muted-foreground hover:bg-accent/50 transition-colors"
762
- >
763
- <IconPencil className="w-3 h-3" />
764
- {t("editorToolbar.removeExcalidrawCanvas")}
765
- </button>
766
- )}
767
-
768
- {/* Transitions section */}
769
- <div className="mx-2 my-1.5 border-t border-border" />
770
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
771
- {t("editorToolbar.transition")}
772
- </div>
773
- <div className="px-3 pb-2.5 grid grid-cols-4 gap-1">
774
- {(["instant", "fade", "slide", "zoom"] as const).map(
775
- (transition) => {
776
- const active =
777
- transition === "instant"
778
- ? !currentSlide.transition ||
779
- currentSlide.transition === "instant" ||
780
- currentSlide.transition === "none"
781
- : currentSlide.transition === transition;
782
- return (
783
- <button
784
- key={transition}
785
- onClick={() => onUpdateSlide!({ transition })}
786
- className={`px-1.5 py-1 rounded text-[10px] font-medium capitalize border ${
787
- active
788
- ? "bg-[#609FF8]/20 text-[#609FF8] border-[#609FF8]/30"
789
- : "text-muted-foreground hover:text-foreground/70 hover:bg-accent/50 border-transparent"
790
- }`}
791
- >
792
- {t(`editorToolbar.transition_${transition}`)}
793
- </button>
794
- );
795
- },
796
- )}
797
- </div>
798
-
799
- {/* Aspect Ratio section (deck-level) */}
800
- {onSetAspectRatio && (
801
- <>
802
- <div className="mx-2 my-1.5 border-t border-border" />
803
- <div className="px-3 py-1.5 text-[10px] font-medium text-muted-foreground uppercase tracking-wider">
804
- {t("editorToolbar.aspectRatio")}
805
- </div>
806
- <div className="px-3 pb-2.5 grid grid-cols-4 gap-1">
807
- {ASPECT_RATIO_VALUES.map((r) => {
808
- const active = activeAspectRatio === r;
809
- return (
810
- <Tooltip key={r}>
811
- <TooltipTrigger asChild>
812
- <button
813
- onClick={() => onSetAspectRatio(r)}
814
- className={`px-1.5 py-1 rounded text-[10px] font-medium border ${
815
- active
816
- ? "bg-[#609FF8]/20 text-[#609FF8] border-[#609FF8]/30"
817
- : "text-muted-foreground hover:text-foreground hover:bg-accent/50 border-transparent"
818
- }`}
819
- >
820
- {r}
821
- </button>
822
- </TooltipTrigger>
823
- <TooltipContent>
824
- {t("editorToolbar.setAspectRatio", { ratio: r })}
825
- </TooltipContent>
826
- </Tooltip>
827
- );
828
- })}
829
- </div>
830
- </>
831
- )}
832
- </div>
833
- </ToolbarPopover>
834
- </>
835
- )}
836
-
837
- {/* Slide tools palette — animations, tweaks, draw, comment-pin all live
838
- * inside one popover so the toolbar doesn't drown in icons. Hidden in
839
- * view-only mode since none of these affordances apply. */}
840
- {canEdit &&
841
- (onToggleAnimations ||
842
- onToggleTweaks ||
843
- onToggleDrawMode ||
844
- onTogglePinMode) && (
845
- <DropdownMenu
846
- open={toolsOpen}
847
- onOpenChange={(open) => {
848
- if (open) setLayoutOpen(false);
849
- setToolsOpen(open);
850
- }}
851
- >
852
- <Tooltip>
853
- <TooltipTrigger asChild>
854
- <DropdownMenuTrigger asChild>
855
- <button
856
- className={`${TOOLBAR_ICON_BUTTON_CLASS} relative cursor-pointer ${
857
- anyToolActive || toolsOpen
858
- ? "bg-accent text-foreground"
859
- : "text-muted-foreground hover:text-foreground/70 hover:bg-accent"
860
- }`}
861
- aria-label={t("editorToolbar.slideTools")}
862
- >
863
- <IconTool className="size-4" />
864
- {anyToolActive && !toolsOpen && (
865
- <span className="absolute top-1 right-1 w-1.5 h-1.5 rounded-full bg-[#609FF8]" />
866
- )}
867
- </button>
868
- </DropdownMenuTrigger>
869
- </TooltipTrigger>
870
- <TooltipContent>{t("editorToolbar.slideTools")}</TooltipContent>
871
- </Tooltip>
872
- <DropdownMenuContent align="end" className="w-52">
873
- <DropdownMenuGroup>
874
- {currentSlide && onToggleAnimations && (
875
- <DropdownMenuItem
876
- onSelect={onToggleAnimations}
877
- className={
878
- animationsOpen
879
- ? "bg-accent text-accent-foreground"
880
- : undefined
881
- }
882
- >
883
- <IconBolt className="size-4" />
884
- {t("editorToolbar.elementAnimations")}
885
- </DropdownMenuItem>
886
- )}
887
- {onToggleTweaks && (
888
- <DropdownMenuItem
889
- onSelect={onToggleTweaks}
890
- className={
891
- tweaksOpen
892
- ? "bg-accent text-accent-foreground"
893
- : undefined
894
- }
895
- >
896
- <IconAdjustments className="size-4" />
897
- {t("editorToolbar.tweaks")}
898
- </DropdownMenuItem>
899
- )}
900
- {onToggleDrawMode && (
901
- <DropdownMenuItem
902
- onSelect={onToggleDrawMode}
903
- data-toolbar-draw-button
904
- className={
905
- drawMode ? "bg-accent text-accent-foreground" : undefined
906
- }
907
- >
908
- <IconPencilPlus className="size-4" />
909
- {t("editorToolbar.drawOnSlide")}
910
- </DropdownMenuItem>
911
- )}
912
- {onTogglePinMode && (
913
- <DropdownMenuItem
914
- onSelect={onTogglePinMode}
915
- data-toolbar-pin-button
916
- className={
917
- pinMode ? "bg-accent text-accent-foreground" : undefined
918
- }
919
- >
920
- <IconPin className="size-4" />
921
- {t("editorToolbar.pinComments")}
922
- </DropdownMenuItem>
923
- )}
924
- </DropdownMenuGroup>
925
- </DropdownMenuContent>
926
- </DropdownMenu>
927
- )}
928
-
929
614
  {/* Save status — subtle "Saving…" / "Saved" / offline pill. Renders
930
615
  nothing when idle. Only meaningful for editors. */}
931
616
  {canEdit && (
@@ -941,45 +626,193 @@ export default function EditorToolbar({
941
626
  activeUsers={activeUsers ?? []}
942
627
  agentPresent={agentPresent}
943
628
  agentActive={agentActive}
629
+ showAgentEditingDot={false}
944
630
  currentUserEmail={currentUserEmail}
945
631
  className="flex-shrink-0 mr-0.5"
946
632
  />
947
633
 
948
- {/* Comments toggle */}
949
- {onToggleComments && (
950
- <Tooltip>
951
- <TooltipTrigger asChild>
952
- <button
953
- onClick={onToggleComments}
954
- className={`${TOOLBAR_ICON_BUTTON_CLASS} relative ${
955
- commentsOpen
956
- ? "text-foreground bg-accent"
957
- : "text-muted-foreground hover:text-foreground/70 hover:bg-accent"
958
- }`}
959
- aria-label={t("editorToolbar.comments")}
960
- >
961
- <IconMessage className="size-4" />
962
- {unresolvedCommentCount > 0 && (
963
- <span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-[#609FF8] text-[8px] font-bold text-black flex items-center justify-center leading-none">
964
- {unresolvedCommentCount > 9 ? "9+" : unresolvedCommentCount}
965
- </span>
634
+ {/* Consolidated editor menu */}
635
+ <div className="flex-shrink-0">
636
+ <DropdownMenu>
637
+ <Tooltip>
638
+ <TooltipTrigger asChild>
639
+ <DropdownMenuTrigger asChild>
640
+ <Button
641
+ type="button"
642
+ variant="ghost"
643
+ size="icon"
644
+ ref={historyButtonRef}
645
+ className={`${TOOLBAR_ICON_BUTTON_CLASS} cursor-pointer text-muted-foreground hover:bg-accent hover:text-foreground/70`}
646
+ aria-label={t("editorToolbar.more")}
647
+ >
648
+ <IconDotsVertical className="size-4" />
649
+ </Button>
650
+ </DropdownMenuTrigger>
651
+ </TooltipTrigger>
652
+ <TooltipContent>{t("editorToolbar.more")}</TooltipContent>
653
+ </Tooltip>
654
+ <DropdownMenuContent
655
+ align="end"
656
+ className="max-h-[min(80vh,32rem)] w-64 overflow-y-auto"
657
+ >
658
+ {canEdit && (
659
+ <>
660
+ <DropdownMenuLabel>
661
+ {t("editorToolbar.media")}
662
+ </DropdownMenuLabel>
663
+ <DropdownMenuGroup>
664
+ <DropdownMenuItem onSelect={onGenerateImage}>
665
+ <IconPhoto className="size-4" />
666
+ {t("editorToolbar.generateImage")}
667
+ </DropdownMenuItem>
668
+ <DropdownMenuItem onSelect={onOpenAssetLibrary}>
669
+ <IconFolderOpen className="size-4" />
670
+ {t("editorToolbar.assetLibrary")}
671
+ </DropdownMenuItem>
672
+ </DropdownMenuGroup>
673
+ </>
674
+ )}
675
+
676
+ {canEdit &&
677
+ (onToggleAnimations ||
678
+ onToggleTweaks ||
679
+ onToggleDrawMode ||
680
+ onTogglePinMode) && (
681
+ <>
682
+ <DropdownMenuSeparator />
683
+ <DropdownMenuLabel>
684
+ {t("editorToolbar.slideTools")}
685
+ </DropdownMenuLabel>
686
+ <DropdownMenuGroup>
687
+ {currentSlide && onToggleAnimations && (
688
+ <DropdownMenuItem
689
+ onSelect={onToggleAnimations}
690
+ className={
691
+ animationsOpen
692
+ ? "bg-accent text-accent-foreground"
693
+ : undefined
694
+ }
695
+ >
696
+ <IconBolt className="size-4" />
697
+ {t("editorToolbar.elementAnimations")}
698
+ </DropdownMenuItem>
699
+ )}
700
+ {onToggleTweaks && (
701
+ <DropdownMenuItem
702
+ onSelect={onToggleTweaks}
703
+ className={
704
+ tweaksOpen
705
+ ? "bg-accent text-accent-foreground"
706
+ : undefined
707
+ }
708
+ >
709
+ <IconAdjustments className="size-4" />
710
+ {t("editorToolbar.tweaks")}
711
+ </DropdownMenuItem>
712
+ )}
713
+ {onToggleDrawMode && (
714
+ <DropdownMenuItem
715
+ onSelect={onToggleDrawMode}
716
+ data-toolbar-draw-button
717
+ className={
718
+ drawMode
719
+ ? "bg-accent text-accent-foreground"
720
+ : undefined
721
+ }
722
+ >
723
+ <IconPencilPlus className="size-4" />
724
+ {t("editorToolbar.drawOnSlide")}
725
+ </DropdownMenuItem>
726
+ )}
727
+ {onTogglePinMode && (
728
+ <DropdownMenuItem
729
+ onSelect={onTogglePinMode}
730
+ data-toolbar-pin-button
731
+ className={
732
+ pinMode
733
+ ? "bg-accent text-accent-foreground"
734
+ : undefined
735
+ }
736
+ >
737
+ <IconPin className="size-4" />
738
+ {t("editorToolbar.pinComments")}
739
+ </DropdownMenuItem>
740
+ )}
741
+ </DropdownMenuGroup>
742
+ </>
966
743
  )}
967
- </button>
968
- </TooltipTrigger>
969
- <TooltipContent>{t("editorToolbar.comments")}</TooltipContent>
970
- </Tooltip>
971
- )}
972
744
 
973
- {/* Export / Share menu (export, duplicate, share) */}
974
- <div className="flex-shrink-0">
975
- <ExportMenu
976
- deckId={deckId}
977
- deckTitle={deckTitle}
978
- onDuplicate={onDuplicateDeck ?? (() => {})}
979
- onExportPdf={onExportPdf ?? (() => {})}
980
- onExportPptx={onExportPptx ?? (() => {})}
981
- onExportGoogleSlides={onExportGoogleSlides}
982
- />
745
+ {onToggleComments && (
746
+ <>
747
+ <DropdownMenuSeparator />
748
+ <DropdownMenuLabel>
749
+ {t("editorToolbar.comments")}
750
+ </DropdownMenuLabel>
751
+ <DropdownMenuItem
752
+ onSelect={onToggleComments}
753
+ className={
754
+ commentsOpen
755
+ ? "bg-accent text-accent-foreground"
756
+ : undefined
757
+ }
758
+ >
759
+ <IconMessage className="size-4" />
760
+ {t("editorToolbar.comments")}
761
+ {unresolvedCommentCount > 0 && (
762
+ <span className="ml-auto rounded-full bg-primary px-1.5 text-[10px] font-semibold text-primary-foreground">
763
+ {unresolvedCommentCount > 9
764
+ ? "9+"
765
+ : unresolvedCommentCount}
766
+ </span>
767
+ )}
768
+ </DropdownMenuItem>
769
+ </>
770
+ )}
771
+
772
+ <DropdownMenuSeparator />
773
+ <ExportMenu
774
+ ref={exportMenuRef}
775
+ inline
776
+ deckId={deckId}
777
+ deckTitle={deckTitle}
778
+ onDuplicate={onDuplicateDeck ?? (() => {})}
779
+ onExportPdf={onExportPdf ?? (() => {})}
780
+ onExportPptx={onExportPptx ?? (() => {})}
781
+ onExportGoogleSlides={onExportGoogleSlides}
782
+ />
783
+ <DropdownMenuSeparator />
784
+ <DropdownMenuItem
785
+ disabled={importing}
786
+ onSelect={() => fileInputRef.current?.click()}
787
+ >
788
+ {importing ? (
789
+ <IconLoader2 className="size-4 animate-spin" />
790
+ ) : (
791
+ <IconDownload className="size-4" />
792
+ )}
793
+ {importing
794
+ ? t("editorToolbar.importing")
795
+ : t("editorToolbar.importFile")}
796
+ </DropdownMenuItem>
797
+ <DropdownMenuItem onSelect={onShowHistory}>
798
+ <IconHistory className="size-4" />
799
+ {t("editorToolbar.savedVersions")}
800
+ </DropdownMenuItem>
801
+ <DropdownMenuSeparator />
802
+ <DropdownMenuItem
803
+ onSelect={() => setTheme(isDark ? "light" : "dark")}
804
+ >
805
+ {isDark ? (
806
+ <IconSun className="size-4" />
807
+ ) : (
808
+ <IconMoon className="size-4" />
809
+ )}
810
+ {isDark
811
+ ? t("editorToolbar.lightTheme")
812
+ : t("editorToolbar.darkTheme")}
813
+ </DropdownMenuItem>
814
+ </DropdownMenuContent>
815
+ </DropdownMenu>
983
816
  </div>
984
817
 
985
818
  {/* Framework share (ownership, per-user/org grants, visibility) */}
@@ -1018,7 +851,7 @@ export default function EditorToolbar({
1018
851
  </div>
1019
852
  {/* Present button — matches Share trigger height (h-9) */}
1020
853
  <Link
1021
- to={`/deck/${deckId}/present`}
854
+ to={`/deck/${deckId}/present?slide=${currentSlideIndex + 1}`}
1022
855
  className="inline-flex h-9 flex-shrink-0 items-center justify-center gap-1.5 rounded-md border border-border bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
1023
856
  >
1024
857
  <IconPlayerPlay className="w-3.5 h-3.5" />
@@ -1034,79 +867,10 @@ export default function EditorToolbar({
1034
867
  className="hidden"
1035
868
  />
1036
869
 
1037
- {/* Overflow — Import / History / Theme tucked away to clean the bar. */}
1038
- <DropdownMenu>
1039
- <Tooltip>
1040
- <TooltipTrigger asChild>
1041
- <DropdownMenuTrigger asChild>
1042
- <button
1043
- ref={historyButtonRef}
1044
- className={`${TOOLBAR_ICON_BUTTON_CLASS} cursor-pointer text-muted-foreground hover:bg-accent hover:text-foreground/70`}
1045
- aria-label={t("editorToolbar.more")}
1046
- >
1047
- <IconDotsVertical className="size-4" />
1048
- </button>
1049
- </DropdownMenuTrigger>
1050
- </TooltipTrigger>
1051
- <TooltipContent>{t("editorToolbar.more")}</TooltipContent>
1052
- </Tooltip>
1053
- <DropdownMenuContent align="end" className="w-48">
1054
- {canEdit && (
1055
- <>
1056
- <DropdownMenuItem
1057
- disabled={!canUndo}
1058
- onSelect={() => commitActiveEditThenRun(undo)}
1059
- >
1060
- <IconArrowBackUp className="w-4 h-4 mr-2" />
1061
- {t("editorToolbar.undoWithShortcut", {
1062
- shortcut: shortcutLabel("Cmd+Z"),
1063
- })}
1064
- </DropdownMenuItem>
1065
- <DropdownMenuItem
1066
- disabled={!canRedo}
1067
- onSelect={() => commitActiveEditThenRun(redo)}
1068
- >
1069
- <IconArrowForwardUp className="w-4 h-4 mr-2" />
1070
- {t("editorToolbar.redoWithShortcut", {
1071
- shortcut: shortcutLabel("Cmd+Shift+Z"),
1072
- })}
1073
- </DropdownMenuItem>
1074
- <DropdownMenuSeparator />
1075
- </>
1076
- )}
1077
- <DropdownMenuItem
1078
- disabled={importing}
1079
- onSelect={() => fileInputRef.current?.click()}
1080
- >
1081
- {importing ? (
1082
- <IconLoader2 className="w-4 h-4 mr-2 animate-spin" />
1083
- ) : (
1084
- <IconDownload className="w-4 h-4 mr-2" />
1085
- )}
1086
- {importing
1087
- ? t("editorToolbar.importing")
1088
- : t("editorToolbar.importFile")}
1089
- </DropdownMenuItem>
1090
- <DropdownMenuItem onClick={onShowHistory}>
1091
- <IconHistory className="w-4 h-4 mr-2" />
1092
- {t("editorToolbar.savedVersions")}
1093
- </DropdownMenuItem>
1094
- <DropdownMenuSeparator />
1095
- <DropdownMenuItem onClick={() => setTheme(isDark ? "light" : "dark")}>
1096
- {isDark ? (
1097
- <IconSun className="w-4 h-4 mr-2" />
1098
- ) : (
1099
- <IconMoon className="w-4 h-4 mr-2" />
1100
- )}
1101
- {isDark
1102
- ? t("editorToolbar.lightTheme")
1103
- : t("editorToolbar.darkTheme")}
1104
- </DropdownMenuItem>
1105
- </DropdownMenuContent>
1106
- </DropdownMenu>
1107
-
1108
- <RunsTray pollMs={0} />
1109
- <AgentToggleButton />
870
+ <div className="flex items-center gap-1">
871
+ <RunsTray pollMs={0} />
872
+ <AgentToggleButton />
873
+ </div>
1110
874
  </div>
1111
875
  );
1112
876
  }