@agent-native/core 0.95.0 → 0.96.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 (316) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +35 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +13 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +18 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +16 -0
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +17 -0
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +16 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +17 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +15 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +16 -0
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +11 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +12 -0
  13. package/corpus/core/docs/content/template-design.mdx +16 -0
  14. package/corpus/core/package.json +4 -2
  15. package/corpus/core/src/a2a/client.ts +11 -1
  16. package/corpus/core/src/agent/production-agent.ts +107 -27
  17. package/corpus/core/src/agent/thread-data-builder.ts +48 -22
  18. package/corpus/core/src/chat-threads/store.ts +20 -1
  19. package/corpus/core/src/cli/code-agent-executor.ts +1 -0
  20. package/corpus/core/src/cli/code-agent-runs.ts +14 -0
  21. package/corpus/core/src/client/AgentPanel.tsx +32 -25
  22. package/corpus/core/src/client/AssistantChat.tsx +286 -45
  23. package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
  24. package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
  25. package/corpus/core/src/client/chat/index.ts +6 -0
  26. package/corpus/core/src/client/chat/tool-call-display.tsx +38 -4
  27. package/corpus/core/src/client/chat/use-reconnect-reader-owner.ts +28 -0
  28. package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
  29. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
  30. package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
  31. package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
  32. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
  33. package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
  34. package/corpus/core/src/client/guided-questions.tsx +11 -2
  35. package/corpus/core/src/client/index.ts +11 -0
  36. package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +6 -7
  37. package/corpus/core/src/client/sse-event-processor.ts +90 -21
  38. package/corpus/core/src/client/use-chat-threads.ts +10 -1
  39. package/corpus/core/src/client/voice-provider-status.ts +44 -0
  40. package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
  41. package/corpus/core/src/connections/catalog.ts +2 -2
  42. package/corpus/core/src/deploy/build.ts +12 -8
  43. package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
  44. package/corpus/core/src/integrations/adapters/slack.ts +74 -21
  45. package/corpus/core/src/integrations/catalog.ts +2 -2
  46. package/corpus/core/src/integrations/webhook-handler.ts +14 -0
  47. package/corpus/core/src/localization/default-messages.ts +6 -0
  48. package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
  49. package/corpus/core/src/observability/routes.ts +17 -0
  50. package/corpus/core/src/observability/store.ts +4 -1
  51. package/corpus/core/src/observability/traces.ts +41 -2
  52. package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
  53. package/corpus/core/src/scripts/call-agent.ts +53 -3
  54. package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
  55. package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
  56. package/corpus/core/src/server/agent-chat-plugin.ts +41 -6
  57. package/corpus/core/src/server/auth.ts +1 -1
  58. package/corpus/core/src/server/core-routes-plugin.ts +15 -0
  59. package/corpus/core/src/server/credential-provider.ts +7 -3
  60. package/corpus/core/src/server/realtime-voice.ts +71 -24
  61. package/corpus/core/src/server/voice-providers-status.ts +4 -3
  62. package/corpus/core/src/shared/cache-control.ts +4 -4
  63. package/corpus/core/src/styles/agent-native.css +1 -0
  64. package/corpus/core/src/styles/chat-history-list.css +313 -0
  65. package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  66. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  67. package/corpus/core/src/vite/client.ts +1 -0
  68. package/corpus/templates/analytics/AGENTS.md +7 -0
  69. package/corpus/templates/analytics/actions/bigquery.ts +26 -2
  70. package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
  71. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
  72. package/corpus/templates/analytics/app/global.css +10 -0
  73. package/corpus/templates/analytics/app/lib/data-sources.ts +5 -4
  74. package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
  75. package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
  76. package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
  77. package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
  78. package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
  79. package/corpus/templates/analytics/changelog/2026-07-10-slack-token-setup-marked-legacy.md +6 -0
  80. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
  81. package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
  82. package/corpus/templates/analytics/server/lib/bigquery.ts +90 -14
  83. package/corpus/templates/analytics/server/lib/credential-keys.ts +6 -2
  84. package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
  85. package/corpus/templates/brain/changelog/2026-07-10-slack-backfill-token-marked-legacy.md +6 -0
  86. package/corpus/templates/brain/server/register-secrets.ts +2 -2
  87. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -1
  88. package/corpus/templates/clips/app/components/library/page-header.tsx +19 -3
  89. package/corpus/templates/clips/app/routes/_app.library._index.tsx +16 -12
  90. package/corpus/templates/clips/changelog/2026-07-10-new-recording-appears-once-when-the-library-sidebar-is-open.md +6 -0
  91. package/corpus/templates/clips/changelog/2026-07-10-slack-token-fallback-guidance.md +6 -0
  92. package/corpus/templates/clips/server/register-secrets.ts +1 -1
  93. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +0 -66
  94. package/corpus/templates/content/app/routes/_app.local-files.tsx +7 -47
  95. package/corpus/templates/content/changelog/2026-07-10-local-folders-now-wait-for-an-explicit-pull-before-content-r.md +6 -0
  96. package/corpus/templates/content/package.json +1 -1
  97. package/corpus/templates/design/actions/generate-design.ts +128 -31
  98. package/corpus/templates/design/actions/present-design-variants.ts +37 -9
  99. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +9 -3
  100. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +32 -10
  101. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +2 -2
  102. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -1
  103. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -5
  104. package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
  105. package/corpus/templates/design/app/global.css +10 -0
  106. package/corpus/templates/design/app/hooks/use-question-flow.ts +9 -2
  107. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  108. package/corpus/templates/design/app/i18n-data.ts +21 -11
  109. package/corpus/templates/design/changelog/2026-07-10-editor-panels-are-easier-to-distinguish-and-design-intake-qu.md +6 -0
  110. package/corpus/templates/design/changelog/2026-07-10-interact-mode-now-stays-on-the-full-canvas-matches-edit-visu.md +6 -0
  111. package/corpus/templates/design/changelog/2026-07-10-new-web-designs-open-at-desktop-size-and-adapt-across-mobile.md +6 -0
  112. package/corpus/templates/design/changelog/2026-07-10-tweaks-now-link-to-full-documentation.md +6 -0
  113. package/corpus/templates/design/server/plugins/agent-chat.ts +2 -0
  114. package/corpus/templates/mail/app/i18n/ar-SA.ts +3 -3
  115. package/corpus/templates/mail/app/i18n/de-DE.ts +3 -3
  116. package/corpus/templates/mail/app/i18n/en-US.ts +4 -3
  117. package/corpus/templates/mail/app/i18n/es-ES.ts +3 -3
  118. package/corpus/templates/mail/app/i18n/fr-FR.ts +3 -3
  119. package/corpus/templates/mail/app/i18n/hi-IN.ts +3 -3
  120. package/corpus/templates/mail/app/i18n/ja-JP.ts +3 -3
  121. package/corpus/templates/mail/app/i18n/ko-KR.ts +3 -3
  122. package/corpus/templates/mail/app/i18n/pt-BR.ts +3 -3
  123. package/corpus/templates/mail/app/i18n/zh-CN.ts +3 -3
  124. package/corpus/templates/mail/app/i18n/zh-TW.ts +3 -3
  125. package/corpus/templates/mail/changelog/2026-07-10-slack-intake-marked-legacy.md +6 -0
  126. package/corpus/templates/mail/server/lib/env-config.ts +5 -4
  127. package/corpus/templates/plan/package.json +1 -1
  128. package/dist/a2a/client.d.ts +7 -0
  129. package/dist/a2a/client.d.ts.map +1 -1
  130. package/dist/a2a/client.js +2 -1
  131. package/dist/a2a/client.js.map +1 -1
  132. package/dist/agent/production-agent.d.ts +6 -0
  133. package/dist/agent/production-agent.d.ts.map +1 -1
  134. package/dist/agent/production-agent.js +83 -26
  135. package/dist/agent/production-agent.js.map +1 -1
  136. package/dist/agent/thread-data-builder.d.ts +1 -0
  137. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  138. package/dist/agent/thread-data-builder.js +42 -23
  139. package/dist/agent/thread-data-builder.js.map +1 -1
  140. package/dist/chat-threads/store.d.ts +10 -0
  141. package/dist/chat-threads/store.d.ts.map +1 -1
  142. package/dist/chat-threads/store.js +6 -0
  143. package/dist/chat-threads/store.js.map +1 -1
  144. package/dist/cli/code-agent-executor.d.ts.map +1 -1
  145. package/dist/cli/code-agent-executor.js +1 -0
  146. package/dist/cli/code-agent-executor.js.map +1 -1
  147. package/dist/cli/code-agent-runs.d.ts +12 -0
  148. package/dist/cli/code-agent-runs.d.ts.map +1 -1
  149. package/dist/cli/code-agent-runs.js +1 -0
  150. package/dist/cli/code-agent-runs.js.map +1 -1
  151. package/dist/client/AgentPanel.d.ts.map +1 -1
  152. package/dist/client/AgentPanel.js +3 -3
  153. package/dist/client/AgentPanel.js.map +1 -1
  154. package/dist/client/AssistantChat.d.ts +14 -1
  155. package/dist/client/AssistantChat.d.ts.map +1 -1
  156. package/dist/client/AssistantChat.js +207 -32
  157. package/dist/client/AssistantChat.js.map +1 -1
  158. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  159. package/dist/client/MultiTabAssistantChat.js +83 -10
  160. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  161. package/dist/client/chat/ChatHistoryList.d.ts +88 -0
  162. package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
  163. package/dist/client/chat/ChatHistoryList.js +83 -0
  164. package/dist/client/chat/ChatHistoryList.js.map +1 -0
  165. package/dist/client/chat/index.d.ts +1 -0
  166. package/dist/client/chat/index.d.ts.map +1 -1
  167. package/dist/client/chat/index.js +1 -0
  168. package/dist/client/chat/index.js.map +1 -1
  169. package/dist/client/chat/tool-call-display.d.ts +13 -1
  170. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  171. package/dist/client/chat/tool-call-display.js +11 -3
  172. package/dist/client/chat/tool-call-display.js.map +1 -1
  173. package/dist/client/chat/use-reconnect-reader-owner.d.ts +11 -0
  174. package/dist/client/chat/use-reconnect-reader-owner.d.ts.map +1 -0
  175. package/dist/client/chat/use-reconnect-reader-owner.js +21 -0
  176. package/dist/client/chat/use-reconnect-reader-owner.js.map +1 -0
  177. package/dist/client/code-agent-chat-adapter.d.ts +17 -1
  178. package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
  179. package/dist/client/code-agent-chat-adapter.js +47 -3
  180. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  181. package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
  182. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  183. package/dist/client/composer/RealtimeVoiceMode.js +5 -3
  184. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  185. package/dist/client/composer/TiptapComposer.d.ts +25 -2
  186. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  187. package/dist/client/composer/TiptapComposer.js +96 -5
  188. package/dist/client/composer/TiptapComposer.js.map +1 -1
  189. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  190. package/dist/client/composer/VoiceButton.js +21 -1
  191. package/dist/client/composer/VoiceButton.js.map +1 -1
  192. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  193. package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
  194. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  195. package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
  196. package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
  197. package/dist/client/conversation/code-agent-transcript.js +3 -5
  198. package/dist/client/conversation/code-agent-transcript.js.map +1 -1
  199. package/dist/client/guided-questions.d.ts.map +1 -1
  200. package/dist/client/guided-questions.js +7 -2
  201. package/dist/client/guided-questions.js.map +1 -1
  202. package/dist/client/index.d.ts +3 -1
  203. package/dist/client/index.d.ts.map +1 -1
  204. package/dist/client/index.js +3 -1
  205. package/dist/client/index.js.map +1 -1
  206. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  207. package/dist/client/integrations/IntegrationsPanel.js +5 -7
  208. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  209. package/dist/client/sse-event-processor.d.ts.map +1 -1
  210. package/dist/client/sse-event-processor.js +67 -20
  211. package/dist/client/sse-event-processor.js.map +1 -1
  212. package/dist/client/use-chat-threads.d.ts.map +1 -1
  213. package/dist/client/use-chat-threads.js +9 -1
  214. package/dist/client/use-chat-threads.js.map +1 -1
  215. package/dist/client/voice-provider-status.d.ts +10 -0
  216. package/dist/client/voice-provider-status.d.ts.map +1 -0
  217. package/dist/client/voice-provider-status.js +32 -0
  218. package/dist/client/voice-provider-status.js.map +1 -0
  219. package/dist/code-agents/transcript-normalizer.d.ts +29 -0
  220. package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
  221. package/dist/code-agents/transcript-normalizer.js +55 -0
  222. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  223. package/dist/collab/struct-routes.d.ts +1 -1
  224. package/dist/connections/catalog.d.ts +2 -2
  225. package/dist/connections/catalog.js +2 -2
  226. package/dist/connections/catalog.js.map +1 -1
  227. package/dist/deploy/build.d.ts.map +1 -1
  228. package/dist/deploy/build.js +12 -8
  229. package/dist/deploy/build.js.map +1 -1
  230. package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
  231. package/dist/integrations/a2a-continuation-processor.js +25 -5
  232. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  233. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  234. package/dist/integrations/adapters/slack.js +55 -21
  235. package/dist/integrations/adapters/slack.js.map +1 -1
  236. package/dist/integrations/catalog.js +2 -2
  237. package/dist/integrations/catalog.js.map +1 -1
  238. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  239. package/dist/integrations/webhook-handler.js +11 -0
  240. package/dist/integrations/webhook-handler.js.map +1 -1
  241. package/dist/localization/default-messages.d.ts +5 -0
  242. package/dist/localization/default-messages.d.ts.map +1 -1
  243. package/dist/localization/default-messages.js +5 -0
  244. package/dist/localization/default-messages.js.map +1 -1
  245. package/dist/observability/hosted-model-experiment.d.ts +39 -0
  246. package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
  247. package/dist/observability/hosted-model-experiment.js +90 -0
  248. package/dist/observability/hosted-model-experiment.js.map +1 -0
  249. package/dist/observability/routes.d.ts.map +1 -1
  250. package/dist/observability/routes.js +16 -0
  251. package/dist/observability/routes.js.map +1 -1
  252. package/dist/observability/store.d.ts.map +1 -1
  253. package/dist/observability/store.js +4 -1
  254. package/dist/observability/store.js.map +1 -1
  255. package/dist/observability/traces.d.ts +6 -0
  256. package/dist/observability/traces.d.ts.map +1 -1
  257. package/dist/observability/traces.js +28 -2
  258. package/dist/observability/traces.js.map +1 -1
  259. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  260. package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
  261. package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
  262. package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
  263. package/dist/scripts/call-agent.d.ts.map +1 -1
  264. package/dist/scripts/call-agent.js +43 -3
  265. package/dist/scripts/call-agent.js.map +1 -1
  266. package/dist/scripts/chat/search-chats.d.ts +1 -1
  267. package/dist/scripts/chat/search-chats.d.ts.map +1 -1
  268. package/dist/scripts/chat/search-chats.js +11 -8
  269. package/dist/scripts/chat/search-chats.js.map +1 -1
  270. package/dist/secrets/register-framework-secrets.js +1 -1
  271. package/dist/secrets/register-framework-secrets.js.map +1 -1
  272. package/dist/server/agent-chat-plugin.d.ts +11 -0
  273. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  274. package/dist/server/agent-chat-plugin.js +27 -6
  275. package/dist/server/agent-chat-plugin.js.map +1 -1
  276. package/dist/server/auth.js +1 -1
  277. package/dist/server/auth.js.map +1 -1
  278. package/dist/server/core-routes-plugin.d.ts +3 -0
  279. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  280. package/dist/server/core-routes-plugin.js +13 -0
  281. package/dist/server/core-routes-plugin.js.map +1 -1
  282. package/dist/server/credential-provider.d.ts +5 -3
  283. package/dist/server/credential-provider.d.ts.map +1 -1
  284. package/dist/server/credential-provider.js +7 -3
  285. package/dist/server/credential-provider.js.map +1 -1
  286. package/dist/server/realtime-voice.d.ts.map +1 -1
  287. package/dist/server/realtime-voice.js +55 -21
  288. package/dist/server/realtime-voice.js.map +1 -1
  289. package/dist/server/voice-providers-status.d.ts.map +1 -1
  290. package/dist/server/voice-providers-status.js +2 -3
  291. package/dist/server/voice-providers-status.js.map +1 -1
  292. package/dist/shared/cache-control.d.ts +6 -6
  293. package/dist/shared/cache-control.d.ts.map +1 -1
  294. package/dist/shared/cache-control.js +3 -3
  295. package/dist/shared/cache-control.js.map +1 -1
  296. package/dist/styles/agent-native.css +1 -0
  297. package/dist/styles/chat-history-list.css +313 -0
  298. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  299. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
  300. package/dist/vite/client.d.ts.map +1 -1
  301. package/dist/vite/client.js +1 -0
  302. package/dist/vite/client.js.map +1 -1
  303. package/docs/content/locales/ar-SA/template-design.mdx +13 -0
  304. package/docs/content/locales/de-DE/template-design.mdx +18 -0
  305. package/docs/content/locales/es-ES/template-design.mdx +16 -0
  306. package/docs/content/locales/fr-FR/template-design.mdx +17 -0
  307. package/docs/content/locales/hi-IN/template-design.mdx +16 -0
  308. package/docs/content/locales/ja-JP/template-design.mdx +17 -0
  309. package/docs/content/locales/ko-KR/template-design.mdx +15 -0
  310. package/docs/content/locales/pt-BR/template-design.mdx +16 -0
  311. package/docs/content/locales/zh-CN/template-design.mdx +11 -0
  312. package/docs/content/locales/zh-TW/template-design.mdx +12 -0
  313. package/docs/content/template-design.mdx +16 -0
  314. package/package.json +4 -2
  315. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
  316. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
@@ -50,7 +50,6 @@ import {
50
50
  } from "@/hooks/use-notion";
51
51
  import {
52
52
  canWriteLinkedLocalSource,
53
- readDocumentFromLinkedLocalSource,
54
53
  writeDocumentToLinkedLocalSource,
55
54
  } from "@/lib/local-content-source-files";
56
55
  import { cn } from "@/lib/utils";
@@ -538,71 +537,6 @@ function DocumentEditorBody({ documentId, document }: DocumentEditorBodyProps) {
538
537
  }
539
538
  }, [document, documentId]);
540
539
 
541
- useEffect(() => {
542
- if (!isInitializedRef.current || !isLinkedLocalSourceDocument) {
543
- return;
544
- }
545
-
546
- let cancelled = false;
547
- readDocumentFromLinkedLocalSource(document)
548
- .then((result) => {
549
- if (cancelled) return;
550
- if (!result.ok) {
551
- toast.error(t("editor.couldNotReadLocalSourceFile"), {
552
- description: result.error,
553
- });
554
- return;
555
- }
556
-
557
- const fileDocument = result.document;
558
- setLocalTitle(fileDocument.title);
559
- setLocalContent(fileDocument.content);
560
- setLocalContentUpdatedAt(result.updatedAt);
561
- lastSavedTitleRef.current = {
562
- title: fileDocument.title,
563
- updatedAt: result.updatedAt,
564
- };
565
- lastSavedContentRef.current = {
566
- content: fileDocument.content,
567
- updatedAt: result.updatedAt,
568
- };
569
- queryClient.setQueryData(
570
- ["action", "get-document", { id: documentId }],
571
- (old: Document | undefined) =>
572
- old && typeof old === "object" ? { ...old, ...fileDocument } : old,
573
- );
574
- queryClient.setQueryData(
575
- ["action", "list-documents", undefined],
576
- (old: any) => {
577
- const docs = old?.documents ?? (Array.isArray(old) ? old : null);
578
- if (!Array.isArray(docs)) return old;
579
- const nextDocs = docs.map((doc: Document) =>
580
- doc.id === documentId ? { ...doc, ...fileDocument } : doc,
581
- );
582
- return Array.isArray(old)
583
- ? nextDocs
584
- : { ...old, documents: nextDocs };
585
- },
586
- );
587
- })
588
- .catch((error) => {
589
- if (cancelled) return;
590
- toast.error(t("editor.couldNotReadLocalSourceFile"), {
591
- description:
592
- error instanceof Error ? error.message : t("empty.genericError"),
593
- });
594
- });
595
-
596
- return () => {
597
- cancelled = true;
598
- };
599
- }, [
600
- documentId,
601
- document.source?.path,
602
- isLinkedLocalSourceDocument,
603
- queryClient,
604
- ]);
605
-
606
540
  // NOTE: External body changes (agent edit, Notion pull, update-document) are
607
541
  // reconciled into the editor by VisualEditor via its content prop + the
608
542
  // updatedAt gate. The effects below keep DocumentEditor's own mirror
@@ -814,13 +814,6 @@ export default function LocalFilesRoute() {
814
814
  count: restoredDirectories.length,
815
815
  }),
816
816
  });
817
- await pullDirectories(restoredDirectories, {
818
- showToast: false,
819
- syncControlResources: false,
820
- });
821
- await connectLocalComponentWorkspaces(restoredDirectories, {
822
- showToast: false,
823
- });
824
817
  return;
825
818
  }
826
819
 
@@ -835,13 +828,6 @@ export default function LocalFilesRoute() {
835
828
  count: restoredDirectories.length,
836
829
  }),
837
830
  });
838
- await pullDirectories(restoredDirectories, {
839
- showToast: false,
840
- syncControlResources: false,
841
- });
842
- await connectLocalComponentWorkspaces(restoredDirectories, {
843
- showToast: false,
844
- });
845
831
  };
846
832
  restoreDirectories()
847
833
  .catch((err) => {
@@ -943,32 +929,6 @@ export default function LocalFilesRoute() {
943
929
  return { directories: nextDirectories, result };
944
930
  }
945
931
 
946
- async function pullDirectories(
947
- selectedDirectories: SelectedDirectory[],
948
- {
949
- showToast = true,
950
- syncControlResources = true,
951
- }: { showToast?: boolean; syncControlResources?: boolean } = {},
952
- ) {
953
- let activeDirectories = selectedDirectories;
954
- let latestResult: ImportContentSourceResult | null = null;
955
- for (const directory of selectedDirectories) {
956
- const pulled = await pullDirectoryFiles(directory, activeDirectories, {
957
- syncControlResources,
958
- });
959
- latestResult = pulled.result;
960
- activeDirectories = pulled.directories;
961
- }
962
- if (latestResult) {
963
- setStatus({
964
- kind: "success",
965
- title: t("localFiles.foldersPulled"),
966
- detail: resultSummary(latestResult, t),
967
- });
968
- if (showToast) toast.success(t("localFiles.pulledLocalFiles"));
969
- }
970
- }
971
-
972
932
  async function connectLocalComponentWorkspaces(
973
933
  selectedDirectories: SelectedDirectory[],
974
934
  { showToast = true }: { showToast?: boolean } = {},
@@ -1029,7 +989,13 @@ export default function LocalFilesRoute() {
1029
989
  });
1030
990
 
1031
991
  setBusy(`pull:${selected.id}`);
1032
- await pullDirectories(nextDirectories);
992
+ const { result } = await pullDirectoryFiles(selected, nextDirectories);
993
+ setStatus({
994
+ kind: "success",
995
+ title: t("localFiles.foldersPulled"),
996
+ detail: resultSummary(result, t),
997
+ });
998
+ toast.success(t("localFiles.pulledLocalFiles"));
1033
999
  await connectLocalComponentWorkspaces([selected]);
1034
1000
  } catch (err) {
1035
1001
  setStatus({
@@ -1171,12 +1137,6 @@ export default function LocalFilesRoute() {
1171
1137
  title: t("localFiles.folderRemoved"),
1172
1138
  detail: directory.name,
1173
1139
  });
1174
- if (nextDirectories.length === 1) {
1175
- await pullDirectories(nextDirectories, {
1176
- showToast: false,
1177
- syncControlResources: false,
1178
- });
1179
- }
1180
1140
  } catch (err) {
1181
1141
  setStatus({
1182
1142
  kind: "error",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-10
4
+ ---
5
+
6
+ Local folders now wait for an explicit pull before Content reads them
@@ -81,7 +81,7 @@
81
81
  "@swc/core": "^1.13.3",
82
82
  "@tailwindcss/typography": "^0.5.16",
83
83
  "@tailwindcss/vite": "catalog:",
84
- "@tanstack/react-query": "^5.99.2",
84
+ "@tanstack/react-query": "5.101.2",
85
85
  "@types/node": "^24.2.1",
86
86
  "@types/react": "catalog:",
87
87
  "@types/react-dom": "catalog:",
@@ -33,6 +33,7 @@ import {
33
33
  updateGenerationSessionWithSavedFiles,
34
34
  } from "../shared/generation-session.js";
35
35
  import { assertLockedLayersPreserved } from "../shared/locked-layers.js";
36
+ import { widthToPrefix } from "../shared/responsive-classes.js";
36
37
  import { annotateScreenHtmlForPersist } from "../shared/screen-annotation.js";
37
38
 
38
39
  /** Editor deep link so external agents can surface "Open design". */
@@ -54,6 +55,43 @@ function isRenderableDesignFile(file: {
54
55
  );
55
56
  }
56
57
 
58
+ type GenerationViewport = "mobile" | "tablet" | "desktop";
59
+
60
+ const DEFAULT_GENERATION_VIEWPORT: GenerationViewport = "desktop";
61
+ const GENERATION_VIEWPORT_SIZES: Record<
62
+ GenerationViewport,
63
+ { width: number; height: number }
64
+ > = {
65
+ mobile: { width: 390, height: 844 },
66
+ tablet: { width: 768, height: 1024 },
67
+ desktop: { width: 1440, height: 1024 },
68
+ };
69
+ const GENERATED_FRAME_GAP = 96;
70
+ const DEFAULT_RESPONSIVE_BREAKPOINTS = [390, 768, 1440].map((widthPx) => ({
71
+ id: `generated-${widthPx}`,
72
+ label: widthPx === 390 ? "Mobile" : widthPx === 768 ? "Tablet" : "Desktop",
73
+ widthPx,
74
+ prefix: widthToPrefix(widthPx),
75
+ }));
76
+
77
+ function hasBreakpointSet(value: unknown): boolean {
78
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
79
+ return false;
80
+ }
81
+ const breakpoints = (value as { breakpoints?: unknown }).breakpoints;
82
+ return Array.isArray(breakpoints) && breakpoints.length > 0;
83
+ }
84
+
85
+ function nextGeneratedFrameX(
86
+ frames: ReturnType<typeof parseCanvasFrameGeometryById>,
87
+ ): number {
88
+ return Object.values(frames).reduce((furthestRight, frame) => {
89
+ const x = frame.x ?? 0;
90
+ const width = frame.width ?? 0;
91
+ return Math.max(furthestRight, x + width + GENERATED_FRAME_GAP);
92
+ }, 0);
93
+ }
94
+
57
95
  function jsonValuesEqual(left: unknown, right: unknown): boolean {
58
96
  return JSON.stringify(left) === JSON.stringify(right);
59
97
  }
@@ -149,7 +187,15 @@ const generateDesignAgentParameters = {
149
187
  canvasFrames: {
150
188
  type: "string",
151
189
  description:
152
- "Optional JSON array of overview-canvas placements keyed by filename or fileId.",
190
+ "Optional JSON array of overview-canvas placements keyed by filename or fileId. " +
191
+ "Pass explicit x/y/width/height for every generated screen; desktop is 1440x1024.",
192
+ },
193
+ primaryViewport: {
194
+ type: "string",
195
+ enum: ["mobile", "tablet", "desktop"],
196
+ description:
197
+ "The requested primary form factor. Defaults to desktop (1440x1024). " +
198
+ "Set this from the intake answer when no explicit canvasFrames placement is supplied.",
153
199
  },
154
200
  },
155
201
  required: ["designId", "prompt", "files"],
@@ -171,6 +217,9 @@ const generateDesignAction = defineAction({
171
217
  "When `designSystemId` is provided, first use `get-design-system` and apply " +
172
218
  "its `agentContext` tokens/docs before writing the file content; do not " +
173
219
  "treat the id alone as enough design-system context. " +
220
+ "Every web design must be responsive. Use a desktop 1440x1024 primary " +
221
+ "canvas by default, or set `primaryViewport` for an explicitly mobile- or " +
222
+ "tablet-primary design; this action adds responsive editor breakpoints. " +
174
223
  "Do not report a design as ready until this action succeeds. " +
175
224
  "When adding multiple screens or states, pass canvasFrames with filenames " +
176
225
  "and x/y/width/height so the new screens appear placed on the overview canvas.",
@@ -304,6 +353,14 @@ const generateDesignAction = defineAction({
304
353
  "Reference each screen by filename or fileId and include x/y/width/height " +
305
354
  "from generate-screens regions or your planned canvas layout.",
306
355
  ),
356
+ primaryViewport: z
357
+ .enum(["mobile", "tablet", "desktop"])
358
+ .optional()
359
+ .default(DEFAULT_GENERATION_VIEWPORT)
360
+ .describe(
361
+ "Primary generated viewport. Defaults to desktop (1440x1024); set " +
362
+ "mobile or tablet only when that is the requested form factor.",
363
+ ),
307
364
  }),
308
365
  mcpApp: {
309
366
  compactCatalog: true,
@@ -323,6 +380,7 @@ const generateDesignAction = defineAction({
323
380
  projectType,
324
381
  tweaks,
325
382
  canvasFrames,
383
+ primaryViewport,
326
384
  }) => {
327
385
  await assertAccess("design", designId, "editor");
328
386
  if (designSystemId) {
@@ -515,7 +573,7 @@ const generateDesignAction = defineAction({
515
573
  }));
516
574
  await mutateDesignData({
517
575
  designId,
518
- mutate: (prevData) => {
576
+ mutate: (prevData, { updatedAt }) => {
519
577
  const mergedData: Record<string, unknown> = {
520
578
  ...prevData,
521
579
  lastPrompt: prompt,
@@ -525,34 +583,74 @@ const generateDesignAction = defineAction({
525
583
  if (normalizedTweaks !== undefined) {
526
584
  mergedData.tweaks = normalizedTweaks;
527
585
  }
528
- if (canvasFrames !== undefined) {
529
- const savedByFileId = new Map(
530
- savedFiles.map((file) => [file.id, file]),
531
- );
532
- const savedByFilename = new Map(
533
- savedFiles.map((file) => [file.filename, file]),
534
- );
535
- const existingByFileId = new Map(
536
- existingFiles.map((file) => [file.id, file]),
537
- );
538
- const merged = mergeCanvasFramePlacements({
539
- existing: prevData.canvasFrames,
540
- placements: canvasFrames,
541
- resolveFileId: (placement) => {
542
- if (placement.fileId) {
543
- return savedByFileId.has(placement.fileId) ||
544
- existingByFileId.has(placement.fileId)
545
- ? placement.fileId
546
- : undefined;
547
- }
548
- return placement.filename
549
- ? (savedByFilename.get(placement.filename)?.id ??
550
- existingByName.get(placement.filename)?.id)
586
+ const savedByFileId = new Map(
587
+ savedFiles.map((file) => [file.id, file]),
588
+ );
589
+ const savedByFilename = new Map(
590
+ savedFiles.map((file) => [file.filename, file]),
591
+ );
592
+ const existingByFileId = new Map(
593
+ existingFiles.map((file) => [file.id, file]),
594
+ );
595
+ const merged = mergeCanvasFramePlacements({
596
+ existing: prevData.canvasFrames,
597
+ placements: canvasFrames ?? [],
598
+ resolveFileId: (placement) => {
599
+ if (placement.fileId) {
600
+ return savedByFileId.has(placement.fileId) ||
601
+ existingByFileId.has(placement.fileId)
602
+ ? placement.fileId
551
603
  : undefined;
552
- },
604
+ }
605
+ return placement.filename
606
+ ? (savedByFilename.get(placement.filename)?.id ??
607
+ existingByName.get(placement.filename)?.id)
608
+ : undefined;
609
+ },
610
+ });
611
+ const viewport = GENERATION_VIEWPORT_SIZES[primaryViewport];
612
+ let nextX = nextGeneratedFrameX(merged.canvasFrames);
613
+ const generationFrames = [...merged.placedFrames];
614
+ for (const file of savedFiles) {
615
+ const source = files.find(
616
+ (candidate) => candidate.filename === file.filename,
617
+ );
618
+ if (!source || !isRenderableDesignFile(source)) continue;
619
+ const current = merged.canvasFrames[file.id] ?? {};
620
+ if (
621
+ current.x !== undefined &&
622
+ current.y !== undefined &&
623
+ current.width !== undefined &&
624
+ current.height !== undefined
625
+ ) {
626
+ continue;
627
+ }
628
+ const frame = {
629
+ x: current.x ?? nextX,
630
+ y: current.y ?? 0,
631
+ width: current.width ?? viewport.width,
632
+ height: current.height ?? viewport.height,
633
+ z: current.z ?? generationFrames.length,
634
+ ...(current.rotation === undefined
635
+ ? {}
636
+ : { rotation: current.rotation }),
637
+ };
638
+ merged.canvasFrames[file.id] = frame;
639
+ generationFrames.push({
640
+ fileId: file.id,
641
+ filename: file.filename,
642
+ frame,
553
643
  });
554
- mergedData.canvasFrames = merged.canvasFrames;
555
- placedFrames = merged.placedFrames;
644
+ nextX = frame.x + frame.width + GENERATED_FRAME_GAP;
645
+ }
646
+ mergedData.canvasFrames = merged.canvasFrames;
647
+ placedFrames = generationFrames;
648
+ if (!hasBreakpointSet(mergedData.breakpointSet)) {
649
+ mergedData.breakpointSet = {
650
+ id: "generated-responsive",
651
+ breakpoints: DEFAULT_RESPONSIVE_BREAKPOINTS,
652
+ };
653
+ mergedData.breakpointSetUpdatedAt = updatedAt;
556
654
  }
557
655
  return mergedData;
558
656
  },
@@ -566,12 +664,10 @@ const generateDesignAction = defineAction({
566
664
  ) {
567
665
  return false;
568
666
  }
569
- if (canvasFrames === undefined) return true;
570
-
571
667
  const currentFrames = parseCanvasFrameGeometryById(
572
668
  current.canvasFrames,
573
669
  );
574
- return Boolean(
670
+ const framesApplied = Boolean(
575
671
  placedFrames?.every(({ fileId, frame }) => {
576
672
  const currentFrame = currentFrames[fileId];
577
673
  return (
@@ -583,6 +679,7 @@ const generateDesignAction = defineAction({
583
679
  );
584
680
  }),
585
681
  );
682
+ return framesApplied && hasBreakpointSet(current.breakpointSet);
586
683
  },
587
684
  });
588
685
 
@@ -19,6 +19,7 @@ import {
19
19
  type CanvasFramePlacement,
20
20
  } from "../shared/canvas-frames.js";
21
21
  import { isUniqueConstraintViolation } from "../shared/db-conflict.js";
22
+ import { widthToPrefix } from "../shared/responsive-classes.js";
22
23
  import { annotateScreenHtmlForPersist } from "../shared/screen-annotation.js";
23
24
 
24
25
  const VARIANT_GAP = 96;
@@ -27,8 +28,22 @@ const MOBILE_WIDTH = 390;
27
28
  const MOBILE_HEIGHT = 844;
28
29
  const TABLET_WIDTH = 768;
29
30
  const TABLET_HEIGHT = 1024;
30
- const DESKTOP_WIDTH = 1280;
31
- const DESKTOP_HEIGHT = 900;
31
+ const DESKTOP_WIDTH = 1440;
32
+ const DESKTOP_HEIGHT = 1024;
33
+ const DEFAULT_RESPONSIVE_BREAKPOINTS = [390, 768, 1440].map((widthPx) => ({
34
+ id: `generated-${widthPx}`,
35
+ label: widthPx === 390 ? "Mobile" : widthPx === 768 ? "Tablet" : "Desktop",
36
+ widthPx,
37
+ prefix: widthToPrefix(widthPx),
38
+ }));
39
+
40
+ function hasBreakpointSet(value: unknown): boolean {
41
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
42
+ return false;
43
+ }
44
+ const breakpoints = (value as { breakpoints?: unknown }).breakpoints;
45
+ return Array.isArray(breakpoints) && breakpoints.length > 0;
46
+ }
32
47
 
33
48
  function designDeepLink(designId: string): string {
34
49
  return buildDeepLink({
@@ -951,6 +966,15 @@ export default defineAction({
951
966
  canvasFrames: mergedFrames.canvasFrames,
952
967
  screenMetadata: previousMetadata,
953
968
  designVariantSets: previousVariantSets,
969
+ ...(hasBreakpointSet(current.breakpointSet)
970
+ ? {}
971
+ : {
972
+ breakpointSet: {
973
+ id: "generated-responsive",
974
+ breakpoints: DEFAULT_RESPONSIVE_BREAKPOINTS,
975
+ },
976
+ breakpointSetUpdatedAt: updatedAt,
977
+ }),
954
978
  updatedAt,
955
979
  };
956
980
  },
@@ -968,13 +992,17 @@ export default defineAction({
968
992
  ? variantSets[variantSetId]
969
993
  : null;
970
994
  const persistedScreens = Array.isArray(set?.screens) ? set.screens : [];
971
- return screens.every(
972
- (screen) =>
973
- isRecord(canvasFrames[screen.id]) &&
974
- isRecord(metadata[screen.id]) &&
975
- persistedScreens.some(
976
- (persisted) => isRecord(persisted) && persisted.id === screen.id,
977
- ),
995
+ return (
996
+ hasBreakpointSet(current.breakpointSet) &&
997
+ screens.every(
998
+ (screen) =>
999
+ isRecord(canvasFrames[screen.id]) &&
1000
+ isRecord(metadata[screen.id]) &&
1001
+ persistedScreens.some(
1002
+ (persisted) =>
1003
+ isRecord(persisted) && persisted.id === screen.id,
1004
+ ),
1005
+ )
978
1006
  );
979
1007
  },
980
1008
  });
@@ -249,9 +249,6 @@ const EDITOR_BRIDGE_VAR_NAMES = [
249
249
  "--design-editor-component-strong-color",
250
250
  "--design-editor-component-contrast-color",
251
251
  "--design-editor-measure-color",
252
- "--background",
253
- "--foreground",
254
- "--border",
255
252
  ];
256
253
 
257
254
  function readEditorBridgeThemeVars(): Record<string, string> {
@@ -1949,6 +1946,15 @@ export function DesignCanvas({
1949
1946
  // iframe document here; the bridge replays inspector state after that load.
1950
1947
  }, [content, contentKey, runtimeReplacementContent, runtimeReplacementKey]);
1951
1948
 
1949
+ useEffect(() => {
1950
+ if (!interactMode) return;
1951
+ // Interact renders the authoritative persisted source because the editor
1952
+ // bridge is intentionally absent. Keep that same source as the next
1953
+ // edit-mode baseline so leaving Interact cannot resurrect the older
1954
+ // bridge-managed snapshot and make the design visibly jump backward.
1955
+ setRenderedContent(content);
1956
+ }, [content, interactMode]);
1957
+
1952
1958
  usePinchZoom({
1953
1959
  containerRef: scrollContainerRef,
1954
1960
  zoom,
@@ -42,6 +42,7 @@ import {
42
42
  type PenPath,
43
43
  } from "@shared/pen-path";
44
44
  import {
45
+ IconArrowsMaximize,
45
46
  IconCopy,
46
47
  IconDots,
47
48
  IconHandClick,
@@ -393,6 +394,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
393
394
  hiddenScreenIds = EMPTY_SCREEN_IDS,
394
395
  lockedScreenIds = EMPTY_SCREEN_IDS,
395
396
  fullViewScreenIds,
397
+ interactMode = false,
396
398
  activeScreenHasHoveredChild = false,
397
399
  hoveredChildScreenId,
398
400
  directlyHoveredScreenId,
@@ -7531,8 +7533,8 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
7531
7533
  }}
7532
7534
  editorChromeScaleX={canvasZoom / 100}
7533
7535
  editorChromeScaleY={canvasZoom / 100}
7534
- editMode={boardEditMode}
7535
- interactMode={false}
7536
+ editMode={boardEditMode && !interactMode}
7537
+ interactMode={interactMode}
7536
7538
  scaleMode={boardIsActive && effectiveTool === "scale"}
7537
7539
  clearSelectionRequest={boardClearSelectionRequest}
7538
7540
  selectedSelector={
@@ -7602,6 +7604,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
7602
7604
  !isBreakpointSelectionTarget(screen)
7603
7605
  }
7604
7606
  showFullView={fullViewIdSet.has(screen.id)}
7607
+ interactMode={interactMode}
7605
7608
  isDirectlyHovered={screen.id === directlyHoveredScreenId}
7606
7609
  isFileDragOver={
7607
7610
  fileDragOverFrameId !== null &&
@@ -8859,6 +8862,7 @@ interface ScreenProps {
8859
8862
  isSelected: boolean;
8860
8863
  isTopScreen: boolean;
8861
8864
  showFullView: boolean;
8865
+ interactMode: boolean;
8862
8866
  isDirectlyHovered: boolean;
8863
8867
  /** True while a native OS file drag is hovering this frame (Figma parity §1). */
8864
8868
  isFileDragOver: boolean;
@@ -8926,6 +8930,7 @@ const Screen = memo(function Screen({
8926
8930
  isSelected,
8927
8931
  isTopScreen,
8928
8932
  showFullView,
8933
+ interactMode,
8929
8934
  isDirectlyHovered,
8930
8935
  isFileDragOver,
8931
8936
  hasHoveredChild,
@@ -9022,9 +9027,12 @@ const Screen = memo(function Screen({
9022
9027
  const frameLabelHeight = FRAME_LABEL_HEIGHT * chromeScale;
9023
9028
  const frameScreenWidth = geometry.width / Math.max(chromeScale, 0.001);
9024
9029
  // Keep frame actions inside their own frame so closely spaced screens cannot
9025
- // cover one another. Narrow frames collapse Interact to its familiar icon;
9030
+ // cover one another. Narrow frames collapse the action to its familiar icon;
9026
9031
  // the accessible name and native tooltip preserve the action's meaning.
9027
9032
  const compactFullView = frameScreenWidth < FRAME_HEADER_BUTTON_COMPACT_WIDTH;
9033
+ const frameActionLabel = interactMode
9034
+ ? t("multiScreenCanvas.fullView")
9035
+ : t("designEditor.modes.interact");
9028
9036
  const labelInfoMaxWidth = Math.max(
9029
9037
  64,
9030
9038
  frameScreenWidth -
@@ -9157,8 +9165,8 @@ const Screen = memo(function Screen({
9157
9165
  transformOrigin: "right center",
9158
9166
  transition: getChromeLabelTransition(chromeSettling),
9159
9167
  }}
9160
- aria-label={t("designEditor.modes.interact")}
9161
- title={t("designEditor.modes.interact")}
9168
+ aria-label={frameActionLabel}
9169
+ title={frameActionLabel}
9162
9170
  onClick={(event) => onEdit(screen.id, event)}
9163
9171
  onMouseDown={(event) => {
9164
9172
  event.preventDefault();
@@ -9167,9 +9175,13 @@ const Screen = memo(function Screen({
9167
9175
  onMouseEnter={() => updateDirectHover(true)}
9168
9176
  onMouseLeave={() => updateDirectHover(false)}
9169
9177
  >
9170
- <IconHandClick className="size-3 shrink-0" />
9178
+ {interactMode ? (
9179
+ <IconArrowsMaximize className="size-3 shrink-0" />
9180
+ ) : (
9181
+ <IconHandClick className="size-3 shrink-0" />
9182
+ )}
9171
9183
  <span className={cn("truncate", compactFullView && "sr-only")}>
9172
- {t("designEditor.modes.interact")}
9184
+ {frameActionLabel}
9173
9185
  </span>
9174
9186
  </button>
9175
9187
  </div>
@@ -9394,6 +9406,7 @@ const Screen = memo(function Screen({
9394
9406
  renderBreakpointContent={renderBreakpointContent}
9395
9407
  activeBreakpointWidth={screen.activeBreakpointWidth}
9396
9408
  isScreenSelected={isSelected}
9409
+ interactMode={interactMode}
9397
9410
  penActive={penActive}
9398
9411
  creationToolActive={creationToolActive}
9399
9412
  cullTier={cullTier}
@@ -9518,6 +9531,7 @@ function BreakpointPreviewRow({
9518
9531
  renderBreakpointContent,
9519
9532
  activeBreakpointWidth,
9520
9533
  isScreenSelected,
9534
+ interactMode,
9521
9535
  penActive,
9522
9536
  creationToolActive,
9523
9537
  cullTier,
@@ -9559,6 +9573,7 @@ function BreakpointPreviewRow({
9559
9573
  * mirrors `Screen`'s own `isSelected`, used so a breakpoint frame's chrome
9560
9574
  * reads as "part of a selected group" the same way the base frame does. */
9561
9575
  isScreenSelected: boolean;
9576
+ interactMode: boolean;
9562
9577
  penActive: boolean;
9563
9578
  creationToolActive: boolean;
9564
9579
  /** Uses the owning screen's exact culling lifecycle: never-seen/evicted
@@ -9595,6 +9610,9 @@ function BreakpointPreviewRow({
9595
9610
  canEdit?: boolean;
9596
9611
  }) {
9597
9612
  const t = useT();
9613
+ const frameActionLabel = interactMode
9614
+ ? t("multiScreenCanvas.fullView")
9615
+ : t("designEditor.modes.interact");
9598
9616
  const breakpointWidths = screen.breakpointWidths ?? [];
9599
9617
  // Place additional frames to the right of the primary, starting after the gap
9600
9618
  let offsetX = primaryGeometry.width + BREAKPOINT_FRAME_GAP;
@@ -9884,8 +9902,8 @@ function BreakpointPreviewRow({
9884
9902
  transform: `scale(${chromeScale})`,
9885
9903
  transformOrigin: "right center",
9886
9904
  }}
9887
- aria-label={t("designEditor.modes.interact")}
9888
- title={t("designEditor.modes.interact")}
9905
+ aria-label={frameActionLabel}
9906
+ title={frameActionLabel}
9889
9907
  onClick={(e) => {
9890
9908
  e.stopPropagation();
9891
9909
  activateThisFrame(e);
@@ -9896,7 +9914,11 @@ function BreakpointPreviewRow({
9896
9914
  e.stopPropagation();
9897
9915
  }}
9898
9916
  >
9899
- <IconHandClick className="size-3" />
9917
+ {interactMode ? (
9918
+ <IconArrowsMaximize className="size-3" />
9919
+ ) : (
9920
+ <IconHandClick className="size-3" />
9921
+ )}
9900
9922
  </button>
9901
9923
  ) : null}
9902
9924
  <span
@@ -340,7 +340,7 @@ function OptionButton({
340
340
  compact ? "px-2.5 py-1.5" : "px-3 py-2",
341
341
  selected
342
342
  ? "border-[var(--design-editor-accent-color)] bg-[var(--design-editor-selection-color)] text-foreground"
343
- : "border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] text-foreground hover:bg-[var(--design-editor-panel-raised-bg)]",
343
+ : "border-[var(--design-editor-control-border)] bg-[var(--design-editor-question-option-bg)] text-foreground hover:bg-[var(--design-editor-control-bg)]",
344
344
  )}
345
345
  >
346
346
  <span
@@ -418,7 +418,7 @@ function ColorOptions({
418
418
  "group inline-flex min-h-8 min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-md border px-2.5 py-1.5 text-start transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)] focus-visible:ring-offset-0",
419
419
  selected
420
420
  ? "border-[var(--design-editor-accent-color)] bg-[var(--design-editor-selection-color)] text-foreground"
421
- : "border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] text-foreground hover:bg-[var(--design-editor-panel-raised-bg)]",
421
+ : "border-[var(--design-editor-control-border)] bg-[var(--design-editor-question-option-bg)] text-foreground hover:bg-[var(--design-editor-control-bg)]",
422
422
  )}
423
423
  >
424
424
  <span