@agent-native/core 0.90.1 → 0.90.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 (714) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +26 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +87 -80
  4. package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
  5. package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
  6. package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
  7. package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
  8. package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
  9. package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
  10. package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
  11. package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
  12. package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
  13. package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
  14. package/corpus/core/docs/content/toolkit-agent-ux.mdx +43 -0
  15. package/corpus/core/docs/content/toolkit-app-adapters.mdx +9 -0
  16. package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +12 -0
  17. package/corpus/core/docs/content/toolkit-collaboration.mdx +74 -0
  18. package/corpus/core/docs/content/toolkit-command-navigation.mdx +106 -0
  19. package/corpus/core/docs/content/toolkit-comments-review.mdx +98 -0
  20. package/corpus/core/docs/content/toolkit-history.mdx +95 -0
  21. package/corpus/core/docs/content/toolkit-observability.mdx +41 -0
  22. package/corpus/core/docs/content/toolkit-org-team.mdx +80 -0
  23. package/corpus/core/docs/content/toolkit-resources.mdx +57 -0
  24. package/corpus/core/docs/content/toolkit-settings.mdx +65 -0
  25. package/corpus/core/docs/content/toolkit-setup-connections.mdx +109 -0
  26. package/corpus/core/docs/content/toolkit-sharing-ui.mdx +18 -6
  27. package/corpus/core/docs/content/toolkit-sharing.mdx +81 -0
  28. package/corpus/core/docs/content/toolkit-shell-hooks.mdx +9 -0
  29. package/corpus/core/docs/content/toolkit-ui.mdx +9 -0
  30. package/corpus/core/package.json +35 -1
  31. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +19 -1
  32. package/corpus/core/src/agent/engine/anthropic-engine.ts +25 -1
  33. package/corpus/core/src/agent/engine/registry.ts +23 -16
  34. package/corpus/core/src/agent/production-agent.ts +37 -6
  35. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  36. package/corpus/core/src/client/command-navigation/index.ts +15 -0
  37. package/corpus/core/src/client/comments-review/index.ts +1 -0
  38. package/corpus/core/src/client/history/VersionHistoryPanel.tsx +145 -0
  39. package/corpus/core/src/client/history/index.ts +18 -0
  40. package/corpus/core/src/client/history/use-history.ts +86 -0
  41. package/corpus/core/src/client/index.ts +76 -9
  42. package/corpus/core/src/client/org/OrgSwitcher.tsx +6 -6
  43. package/corpus/core/src/client/org/RequireActiveOrg.tsx +2 -2
  44. package/corpus/core/src/client/org/TeamPage.tsx +3 -3
  45. package/corpus/core/src/client/org/hooks.ts +32 -0
  46. package/corpus/core/src/client/org/index.ts +30 -1
  47. package/corpus/core/src/client/org-team/index.ts +1 -0
  48. package/corpus/core/src/client/review/ReviewStatusBadge.tsx +39 -0
  49. package/corpus/core/src/client/review/ReviewThreadPanel.tsx +278 -0
  50. package/corpus/core/src/client/review/index.ts +25 -0
  51. package/corpus/core/src/client/review/use-review.ts +114 -0
  52. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
  53. package/corpus/core/src/client/settings/index.ts +11 -1
  54. package/corpus/core/src/client/setup-connections/BuilderConnectCard.tsx +94 -0
  55. package/corpus/core/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
  56. package/corpus/core/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
  57. package/corpus/core/src/client/setup-connections/index.ts +46 -0
  58. package/corpus/core/src/client/sharing/ShareButton.tsx +20 -14
  59. package/corpus/core/src/client/sharing/ShareDialog.tsx +3 -3
  60. package/corpus/core/src/client/use-action.ts +39 -2
  61. package/corpus/core/src/client/use-db-sync.ts +44 -18
  62. package/corpus/core/src/command-navigation/actions.ts +100 -0
  63. package/corpus/core/src/command-navigation/index.ts +12 -0
  64. package/corpus/core/src/comments-review/index.ts +1 -0
  65. package/corpus/core/src/db/create-get-db.ts +32 -2
  66. package/corpus/core/src/file-upload/builder.ts +24 -3
  67. package/corpus/core/src/file-upload/types.ts +3 -1
  68. package/corpus/core/src/history/actions/create-resource-version.ts +89 -0
  69. package/corpus/core/src/history/actions/get-resource-version.ts +60 -0
  70. package/corpus/core/src/history/actions/list-resource-history.ts +53 -0
  71. package/corpus/core/src/history/actions/list-resource-versions.ts +43 -0
  72. package/corpus/core/src/history/actions/restore-resource-version.ts +96 -0
  73. package/corpus/core/src/history/index.ts +27 -0
  74. package/corpus/core/src/history/registry.ts +99 -0
  75. package/corpus/core/src/history/store.ts +348 -0
  76. package/corpus/core/src/history/types.ts +69 -0
  77. package/corpus/core/src/index.ts +10 -0
  78. package/corpus/core/src/navigation/index.ts +170 -0
  79. package/corpus/core/src/org/index.ts +8 -0
  80. package/corpus/core/src/org/permissions.ts +30 -0
  81. package/corpus/core/src/org-team/index.ts +1 -0
  82. package/corpus/core/src/private-blob/registry.ts +1 -0
  83. package/corpus/core/src/review/actions/consume-review-feedback.ts +38 -0
  84. package/corpus/core/src/review/actions/create-review-comment.ts +107 -0
  85. package/corpus/core/src/review/actions/delete-review-comment.ts +55 -0
  86. package/corpus/core/src/review/actions/get-review-feedback.ts +54 -0
  87. package/corpus/core/src/review/actions/list-review-comments.ts +53 -0
  88. package/corpus/core/src/review/actions/reply-review-comment.ts +107 -0
  89. package/corpus/core/src/review/actions/resolve-review-thread.ts +60 -0
  90. package/corpus/core/src/review/actions/set-review-status.ts +58 -0
  91. package/corpus/core/src/review/index.ts +36 -0
  92. package/corpus/core/src/review/mentions.ts +44 -0
  93. package/corpus/core/src/review/registry.ts +99 -0
  94. package/corpus/core/src/review/store.ts +687 -0
  95. package/corpus/core/src/review/types.ts +97 -0
  96. package/corpus/core/src/server/action-discovery.ts +54 -0
  97. package/corpus/core/src/server/agent-engine-api-key-route.ts +5 -0
  98. package/corpus/core/src/server/credential-provider.ts +108 -0
  99. package/corpus/core/src/server/framework-request-handler.ts +52 -4
  100. package/corpus/core/src/setup-connections/index.ts +89 -0
  101. package/corpus/core/src/setup-connections/onboarding.ts +47 -0
  102. package/corpus/core/src/transcription/builder-transcription.ts +9 -2
  103. package/corpus/core/src/vite/action-types-plugin.ts +52 -0
  104. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -1
  105. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +2 -2
  106. package/corpus/templates/analytics/app/global.css +22 -10
  107. package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -1
  108. package/corpus/templates/analytics/app/i18n-data.ts +81 -13
  109. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
  110. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +2 -2
  111. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +528 -108
  112. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +466 -183
  113. package/corpus/templates/analytics/changelog/2026-07-07-session-replays-load-sooner-show-clearer-activity-timelines-.md +6 -0
  114. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
  115. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
  116. package/corpus/templates/assets/AGENTS.md +23 -4
  117. package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
  118. package/corpus/templates/assets/actions/_helpers.ts +8 -1
  119. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
  120. package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
  121. package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
  122. package/corpus/templates/assets/actions/generate-image.ts +342 -25
  123. package/corpus/templates/assets/actions/get-library.ts +4 -3
  124. package/corpus/templates/assets/actions/navigate.ts +2 -1
  125. package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
  126. package/corpus/templates/assets/actions/update-asset.ts +1 -0
  127. package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
  128. package/corpus/templates/assets/actions/view-screen.ts +8 -1
  129. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
  130. package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
  131. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
  132. package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
  133. package/corpus/templates/assets/app/i18n-data.ts +650 -0
  134. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
  135. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
  136. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
  137. package/corpus/templates/assets/app/routes/library.tsx +43 -1
  138. package/corpus/templates/assets/server/lib/generation.ts +366 -44
  139. package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
  140. package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
  141. package/corpus/templates/assets/shared/api.ts +47 -0
  142. package/corpus/templates/brain/app/routes/settings.tsx +2 -2
  143. package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +32 -6
  144. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +120 -40
  145. package/corpus/templates/calendar/app/hooks/use-people.ts +55 -0
  146. package/corpus/templates/calendar/changelog/2026-07-07-guest-suggestions-now-find-coworkers-from-your-google-worksp.md +6 -0
  147. package/corpus/templates/calendar/changelog/2026-07-07-zoom-stays-selected-after-connecting-it-from-a-draft-event.md +6 -0
  148. package/corpus/templates/clips/actions/create-recording.ts +5 -1
  149. package/corpus/templates/clips/actions/finalize-recording.ts +140 -1
  150. package/corpus/templates/clips/actions/import-loom-recording.ts +1 -0
  151. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +1 -0
  152. package/corpus/templates/clips/actions/request-transcript.ts +31 -0
  153. package/corpus/templates/clips/actions/set-thumbnail.ts +2 -0
  154. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
  155. package/corpus/templates/clips/app/components/library/organization-switcher.tsx +2 -2
  156. package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
  157. package/corpus/templates/clips/app/components/player/scrubber-position.ts +9 -0
  158. package/corpus/templates/clips/app/components/player/scrubber.tsx +5 -2
  159. package/corpus/templates/clips/app/components/player/share-dialog.tsx +69 -41
  160. package/corpus/templates/clips/app/components/player/video-player.tsx +38 -1
  161. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
  162. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +3 -3
  163. package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +9 -19
  164. package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
  165. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +9 -3
  166. package/corpus/templates/clips/app/routes/record.tsx +178 -80
  167. package/corpus/templates/clips/changelog/2026-07-02-recordings-now-wait-for-storage-media-to-serve-before-finishing.md +6 -0
  168. package/corpus/templates/clips/changelog/2026-07-07-a-pending-app-update-no-longer-blocks-screen-recording-updat.md +6 -0
  169. package/corpus/templates/clips/changelog/2026-07-07-chrome-extension-recordings-keep-audio-after-refreshing-tab.md +6 -0
  170. package/corpus/templates/clips/changelog/2026-07-07-clip-playback-progress-now-starts-and-finishes-at-the-right-times.md +6 -0
  171. package/corpus/templates/clips/changelog/2026-07-07-sharing-public-clips-now-stays-focused-on-the-normal-link-wi.md +6 -0
  172. package/corpus/templates/clips/chrome-extension/src/background.ts +18 -0
  173. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +23 -8
  174. package/corpus/templates/clips/desktop/src/app.tsx +40 -3
  175. package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +19 -7
  176. package/corpus/templates/clips/desktop/src/lib/permissions.ts +2 -0
  177. package/corpus/templates/clips/desktop/src/lib/updater.ts +25 -2
  178. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +7 -0
  179. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +1 -0
  180. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +23 -6
  181. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +28 -3
  182. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +13 -6
  183. package/corpus/templates/clips/shared/finalize-recovery.ts +35 -3
  184. package/corpus/templates/content/AGENTS.md +52 -52
  185. package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
  186. package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
  187. package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
  188. package/corpus/templates/content/actions/_database-utils.ts +44 -4
  189. package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
  190. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
  191. package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
  192. package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
  193. package/corpus/templates/content/actions/delete-content-database.ts +1 -0
  194. package/corpus/templates/content/actions/delete-document.ts +337 -52
  195. package/corpus/templates/content/actions/get-document.ts +11 -2
  196. package/corpus/templates/content/actions/list-content-databases.ts +57 -9
  197. package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
  198. package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
  199. package/corpus/templates/content/actions/restore-content-database.ts +1 -0
  200. package/corpus/templates/content/actions/update-document.ts +43 -0
  201. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
  202. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
  203. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
  204. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
  205. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
  206. package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
  207. package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
  208. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
  209. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
  210. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
  211. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
  212. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
  213. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
  214. package/corpus/templates/content/app/global.css +41 -0
  215. package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
  216. package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
  217. package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
  218. package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
  219. package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
  220. package/corpus/templates/content/app/i18n-data.ts +355 -0
  221. package/corpus/templates/content/app/root.tsx +22 -0
  222. package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
  223. package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
  224. package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
  225. package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
  226. package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
  227. package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
  228. package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
  229. package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
  230. package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
  231. package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
  232. package/corpus/templates/content/changelog/2026-07-07-permanently-deleting-trashed-databases-now-finishes-reliably.md +6 -0
  233. package/corpus/templates/content/shared/api.ts +13 -0
  234. package/corpus/templates/content/shared/builder-mdx.ts +16 -3
  235. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
  236. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
  237. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
  238. package/corpus/templates/design/AGENTS.md +9 -2
  239. package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
  240. package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
  241. package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
  242. package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
  243. package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
  244. package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
  245. package/corpus/templates/design/actions/create-component.ts +29 -60
  246. package/corpus/templates/design/actions/create-file.ts +9 -2
  247. package/corpus/templates/design/actions/duplicate-design.ts +31 -2
  248. package/corpus/templates/design/actions/edit-design.ts +36 -38
  249. package/corpus/templates/design/actions/generate-design-title.ts +87 -0
  250. package/corpus/templates/design/actions/generate-design.ts +53 -18
  251. package/corpus/templates/design/actions/generate-screens.ts +108 -4
  252. package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
  253. package/corpus/templates/design/actions/insert-asset.ts +34 -29
  254. package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
  255. package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
  256. package/corpus/templates/design/actions/present-design-variants.ts +7 -2
  257. package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
  258. package/corpus/templates/design/actions/update-file.ts +190 -75
  259. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
  260. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
  261. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
  262. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
  263. package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
  264. package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
  265. package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
  266. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
  267. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
  268. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
  269. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
  270. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
  271. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
  272. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
  273. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
  274. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
  275. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
  276. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
  277. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
  278. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
  279. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
  280. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
  281. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
  282. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
  283. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
  284. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
  285. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
  286. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
  287. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
  288. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
  289. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
  290. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
  291. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
  292. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
  293. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
  294. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
  295. package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
  296. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
  297. package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
  298. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
  299. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
  300. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
  301. package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
  302. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
  303. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
  304. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
  305. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
  306. package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
  307. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
  308. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
  309. package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
  310. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
  311. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
  312. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
  313. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
  314. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
  315. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
  316. package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
  317. package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
  318. package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
  319. package/corpus/templates/design/app/components/design/types.ts +15 -0
  320. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
  321. package/corpus/templates/design/app/global.css +8 -8
  322. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
  323. package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
  324. package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
  325. package/corpus/templates/design/app/i18n-data.ts +1664 -152
  326. package/corpus/templates/design/app/i18n-motion.ts +563 -0
  327. package/corpus/templates/design/app/lib/design-import.ts +73 -0
  328. package/corpus/templates/design/app/pages/Index.tsx +41 -22
  329. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
  330. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
  331. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
  332. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
  333. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
  334. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
  335. package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
  336. package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
  337. package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
  338. package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
  339. package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
  340. package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
  341. package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
  342. package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
  343. package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
  344. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
  345. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
  346. package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
  347. package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
  348. package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
  349. package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
  350. package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
  351. package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
  352. package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
  353. package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
  354. package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
  355. package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
  356. package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
  357. package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
  358. package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
  359. package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
  360. package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
  361. package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
  362. package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
  363. package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
  364. package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
  365. package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
  366. package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
  367. package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
  368. package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
  369. package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
  370. package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
  371. package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
  372. package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
  373. package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
  374. package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
  375. package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
  376. package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
  377. package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
  378. package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
  379. package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
  380. package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
  381. package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
  382. package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
  383. package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
  384. package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
  385. package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
  386. package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
  387. package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
  388. package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
  389. package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
  390. package/corpus/templates/design/server/source-workspace.ts +139 -59
  391. package/corpus/templates/design/shared/board-file.ts +11 -2
  392. package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
  393. package/corpus/templates/design/shared/canvas-math.ts +354 -35
  394. package/corpus/templates/design/shared/code-layer.ts +277 -8
  395. package/corpus/templates/design/shared/design-state.ts +11 -4
  396. package/corpus/templates/design/shared/interaction-states.ts +548 -0
  397. package/corpus/templates/design/shared/motion-compiler.ts +186 -23
  398. package/corpus/templates/design/shared/motion-easing.ts +294 -0
  399. package/corpus/templates/design/shared/motion-timeline.ts +400 -34
  400. package/corpus/templates/design/shared/prompt-title.ts +95 -0
  401. package/corpus/templates/design/shared/responsive-classes.ts +448 -10
  402. package/corpus/templates/design/shared/screen-annotation.ts +46 -0
  403. package/corpus/templates/design/shared/shader-fills.ts +1009 -0
  404. package/corpus/templates/design/shared/shader-presets.ts +852 -4
  405. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
  406. package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
  407. package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
  408. package/corpus/templates/slides/app/pages/DeckEditor.tsx +2 -2
  409. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  410. package/dist/agent/engine/ai-sdk-engine.js +15 -1
  411. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  412. package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
  413. package/dist/agent/engine/anthropic-engine.js +20 -1
  414. package/dist/agent/engine/anthropic-engine.js.map +1 -1
  415. package/dist/agent/engine/registry.d.ts.map +1 -1
  416. package/dist/agent/engine/registry.js +22 -14
  417. package/dist/agent/engine/registry.js.map +1 -1
  418. package/dist/agent/production-agent.d.ts.map +1 -1
  419. package/dist/agent/production-agent.js +27 -7
  420. package/dist/agent/production-agent.js.map +1 -1
  421. package/dist/client/AgentPanel.js +1 -1
  422. package/dist/client/AgentPanel.js.map +1 -1
  423. package/dist/client/command-navigation/index.d.ts +4 -0
  424. package/dist/client/command-navigation/index.d.ts.map +1 -0
  425. package/dist/client/command-navigation/index.js +4 -0
  426. package/dist/client/command-navigation/index.js.map +1 -0
  427. package/dist/client/comments-review/index.d.ts +2 -0
  428. package/dist/client/comments-review/index.d.ts.map +1 -0
  429. package/dist/client/comments-review/index.js +2 -0
  430. package/dist/client/comments-review/index.js.map +1 -0
  431. package/dist/client/history/VersionHistoryPanel.d.ts +18 -0
  432. package/dist/client/history/VersionHistoryPanel.d.ts.map +1 -0
  433. package/dist/client/history/VersionHistoryPanel.js +24 -0
  434. package/dist/client/history/VersionHistoryPanel.js.map +1 -0
  435. package/dist/client/history/index.d.ts +3 -0
  436. package/dist/client/history/index.d.ts.map +1 -0
  437. package/dist/client/history/index.js +3 -0
  438. package/dist/client/history/index.js.map +1 -0
  439. package/dist/client/history/use-history.d.ts +46 -0
  440. package/dist/client/history/use-history.d.ts.map +1 -0
  441. package/dist/client/history/use-history.js +18 -0
  442. package/dist/client/history/use-history.js.map +1 -0
  443. package/dist/client/index.d.ts +6 -4
  444. package/dist/client/index.d.ts.map +1 -1
  445. package/dist/client/index.js +6 -4
  446. package/dist/client/index.js.map +1 -1
  447. package/dist/client/org/OrgSwitcher.js +5 -5
  448. package/dist/client/org/OrgSwitcher.js.map +1 -1
  449. package/dist/client/org/RequireActiveOrg.js +2 -2
  450. package/dist/client/org/RequireActiveOrg.js.map +1 -1
  451. package/dist/client/org/TeamPage.js +3 -3
  452. package/dist/client/org/TeamPage.js.map +1 -1
  453. package/dist/client/org/hooks.d.ts +12 -1
  454. package/dist/client/org/hooks.d.ts.map +1 -1
  455. package/dist/client/org/hooks.js +15 -0
  456. package/dist/client/org/hooks.js.map +1 -1
  457. package/dist/client/org/index.d.ts +4 -2
  458. package/dist/client/org/index.d.ts.map +1 -1
  459. package/dist/client/org/index.js +3 -1
  460. package/dist/client/org/index.js.map +1 -1
  461. package/dist/client/org-team/index.d.ts +2 -0
  462. package/dist/client/org-team/index.d.ts.map +1 -0
  463. package/dist/client/org-team/index.js +2 -0
  464. package/dist/client/org-team/index.js.map +1 -0
  465. package/dist/client/review/ReviewStatusBadge.d.ts +7 -0
  466. package/dist/client/review/ReviewStatusBadge.d.ts.map +1 -0
  467. package/dist/client/review/ReviewStatusBadge.js +19 -0
  468. package/dist/client/review/ReviewStatusBadge.js.map +1 -0
  469. package/dist/client/review/ReviewThreadPanel.d.ts +20 -0
  470. package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -0
  471. package/dist/client/review/ReviewThreadPanel.js +84 -0
  472. package/dist/client/review/ReviewThreadPanel.js.map +1 -0
  473. package/dist/client/review/index.d.ts +4 -0
  474. package/dist/client/review/index.d.ts.map +1 -0
  475. package/dist/client/review/index.js +4 -0
  476. package/dist/client/review/index.js.map +1 -0
  477. package/dist/client/review/use-review.d.ts +68 -0
  478. package/dist/client/review/use-review.d.ts.map +1 -0
  479. package/dist/client/review/use-review.js +22 -0
  480. package/dist/client/review/use-review.js.map +1 -0
  481. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  482. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
  483. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  484. package/dist/client/settings/index.d.ts +1 -1
  485. package/dist/client/settings/index.d.ts.map +1 -1
  486. package/dist/client/settings/index.js +1 -1
  487. package/dist/client/settings/index.js.map +1 -1
  488. package/dist/client/setup-connections/BuilderConnectCard.d.ts +9 -0
  489. package/dist/client/setup-connections/BuilderConnectCard.d.ts.map +1 -0
  490. package/dist/client/setup-connections/BuilderConnectCard.js +25 -0
  491. package/dist/client/setup-connections/BuilderConnectCard.js.map +1 -0
  492. package/dist/client/setup-connections/ProviderReadinessBadge.d.ts +8 -0
  493. package/dist/client/setup-connections/ProviderReadinessBadge.d.ts.map +1 -0
  494. package/dist/client/setup-connections/ProviderReadinessBadge.js +32 -0
  495. package/dist/client/setup-connections/ProviderReadinessBadge.js.map +1 -0
  496. package/dist/client/setup-connections/SetupConnectionsPage.d.ts +17 -0
  497. package/dist/client/setup-connections/SetupConnectionsPage.d.ts.map +1 -0
  498. package/dist/client/setup-connections/SetupConnectionsPage.js +10 -0
  499. package/dist/client/setup-connections/SetupConnectionsPage.js.map +1 -0
  500. package/dist/client/setup-connections/index.d.ts +7 -0
  501. package/dist/client/setup-connections/index.d.ts.map +1 -0
  502. package/dist/client/setup-connections/index.js +7 -0
  503. package/dist/client/setup-connections/index.js.map +1 -0
  504. package/dist/client/sharing/ShareButton.d.ts +4 -4
  505. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  506. package/dist/client/sharing/ShareButton.js +13 -7
  507. package/dist/client/sharing/ShareButton.js.map +1 -1
  508. package/dist/client/sharing/ShareDialog.js +3 -3
  509. package/dist/client/sharing/ShareDialog.js.map +1 -1
  510. package/dist/client/use-action.d.ts +3 -0
  511. package/dist/client/use-action.d.ts.map +1 -1
  512. package/dist/client/use-action.js +34 -3
  513. package/dist/client/use-action.js.map +1 -1
  514. package/dist/client/use-db-sync.d.ts +4 -0
  515. package/dist/client/use-db-sync.d.ts.map +1 -1
  516. package/dist/client/use-db-sync.js +37 -18
  517. package/dist/client/use-db-sync.js.map +1 -1
  518. package/dist/collab/routes.d.ts +1 -1
  519. package/dist/command-navigation/actions.d.ts +25 -0
  520. package/dist/command-navigation/actions.d.ts.map +1 -0
  521. package/dist/command-navigation/actions.js +64 -0
  522. package/dist/command-navigation/actions.js.map +1 -0
  523. package/dist/command-navigation/index.d.ts +4 -0
  524. package/dist/command-navigation/index.d.ts.map +1 -0
  525. package/dist/command-navigation/index.js +4 -0
  526. package/dist/command-navigation/index.js.map +1 -0
  527. package/dist/comments-review/index.d.ts +2 -0
  528. package/dist/comments-review/index.d.ts.map +1 -0
  529. package/dist/comments-review/index.js +2 -0
  530. package/dist/comments-review/index.js.map +1 -0
  531. package/dist/db/create-get-db.d.ts +25 -0
  532. package/dist/db/create-get-db.d.ts.map +1 -1
  533. package/dist/db/create-get-db.js +25 -2
  534. package/dist/db/create-get-db.js.map +1 -1
  535. package/dist/file-upload/builder.d.ts.map +1 -1
  536. package/dist/file-upload/builder.js +18 -2
  537. package/dist/file-upload/builder.js.map +1 -1
  538. package/dist/file-upload/types.d.ts +3 -0
  539. package/dist/file-upload/types.d.ts.map +1 -1
  540. package/dist/file-upload/types.js.map +1 -1
  541. package/dist/history/actions/create-resource-version.d.ts +10 -0
  542. package/dist/history/actions/create-resource-version.d.ts.map +1 -0
  543. package/dist/history/actions/create-resource-version.js +65 -0
  544. package/dist/history/actions/create-resource-version.js.map +1 -0
  545. package/dist/history/actions/get-resource-version.d.ts +10 -0
  546. package/dist/history/actions/get-resource-version.d.ts.map +1 -0
  547. package/dist/history/actions/get-resource-version.js +38 -0
  548. package/dist/history/actions/get-resource-version.js.map +1 -0
  549. package/dist/history/actions/list-resource-history.d.ts +10 -0
  550. package/dist/history/actions/list-resource-history.d.ts.map +1 -0
  551. package/dist/history/actions/list-resource-history.js +45 -0
  552. package/dist/history/actions/list-resource-history.js.map +1 -0
  553. package/dist/history/actions/list-resource-versions.d.ts +10 -0
  554. package/dist/history/actions/list-resource-versions.d.ts.map +1 -0
  555. package/dist/history/actions/list-resource-versions.js +35 -0
  556. package/dist/history/actions/list-resource-versions.js.map +1 -0
  557. package/dist/history/actions/restore-resource-version.d.ts +11 -0
  558. package/dist/history/actions/restore-resource-version.d.ts.map +1 -0
  559. package/dist/history/actions/restore-resource-version.js +59 -0
  560. package/dist/history/actions/restore-resource-version.js.map +1 -0
  561. package/dist/history/index.d.ts +4 -0
  562. package/dist/history/index.d.ts.map +1 -0
  563. package/dist/history/index.js +3 -0
  564. package/dist/history/index.js.map +1 -0
  565. package/dist/history/registry.d.ts +10 -0
  566. package/dist/history/registry.d.ts.map +1 -0
  567. package/dist/history/registry.js +61 -0
  568. package/dist/history/registry.js.map +1 -0
  569. package/dist/history/store.d.ts +34 -0
  570. package/dist/history/store.d.ts.map +1 -0
  571. package/dist/history/store.js +265 -0
  572. package/dist/history/store.js.map +1 -0
  573. package/dist/history/types.d.ts +55 -0
  574. package/dist/history/types.d.ts.map +1 -0
  575. package/dist/history/types.js +2 -0
  576. package/dist/history/types.js.map +1 -0
  577. package/dist/index.d.ts +6 -0
  578. package/dist/index.d.ts.map +1 -1
  579. package/dist/index.js +6 -0
  580. package/dist/index.js.map +1 -1
  581. package/dist/navigation/index.d.ts +53 -0
  582. package/dist/navigation/index.d.ts.map +1 -0
  583. package/dist/navigation/index.js +105 -0
  584. package/dist/navigation/index.js.map +1 -0
  585. package/dist/observability/routes.d.ts +3 -3
  586. package/dist/org/index.d.ts +1 -0
  587. package/dist/org/index.d.ts.map +1 -1
  588. package/dist/org/index.js +1 -0
  589. package/dist/org/index.js.map +1 -1
  590. package/dist/org/permissions.d.ts +7 -0
  591. package/dist/org/permissions.d.ts.map +1 -0
  592. package/dist/org/permissions.js +21 -0
  593. package/dist/org/permissions.js.map +1 -0
  594. package/dist/org-team/index.d.ts +2 -0
  595. package/dist/org-team/index.d.ts.map +1 -0
  596. package/dist/org-team/index.js +2 -0
  597. package/dist/org-team/index.js.map +1 -0
  598. package/dist/private-blob/registry.d.ts.map +1 -1
  599. package/dist/private-blob/registry.js +1 -0
  600. package/dist/private-blob/registry.js.map +1 -1
  601. package/dist/resources/handlers.d.ts +2 -2
  602. package/dist/review/actions/consume-review-feedback.d.ts +9 -0
  603. package/dist/review/actions/consume-review-feedback.d.ts.map +1 -0
  604. package/dist/review/actions/consume-review-feedback.js +29 -0
  605. package/dist/review/actions/consume-review-feedback.js.map +1 -0
  606. package/dist/review/actions/create-review-comment.d.ts +18 -0
  607. package/dist/review/actions/create-review-comment.d.ts.map +1 -0
  608. package/dist/review/actions/create-review-comment.js +81 -0
  609. package/dist/review/actions/create-review-comment.js.map +1 -0
  610. package/dist/review/actions/delete-review-comment.d.ts +10 -0
  611. package/dist/review/actions/delete-review-comment.d.ts.map +1 -0
  612. package/dist/review/actions/delete-review-comment.js +45 -0
  613. package/dist/review/actions/delete-review-comment.js.map +1 -0
  614. package/dist/review/actions/get-review-feedback.d.ts +10 -0
  615. package/dist/review/actions/get-review-feedback.d.ts.map +1 -0
  616. package/dist/review/actions/get-review-feedback.js +45 -0
  617. package/dist/review/actions/get-review-feedback.js.map +1 -0
  618. package/dist/review/actions/list-review-comments.d.ts +13 -0
  619. package/dist/review/actions/list-review-comments.d.ts.map +1 -0
  620. package/dist/review/actions/list-review-comments.js +44 -0
  621. package/dist/review/actions/list-review-comments.js.map +1 -0
  622. package/dist/review/actions/reply-review-comment.d.ts +16 -0
  623. package/dist/review/actions/reply-review-comment.d.ts.map +1 -0
  624. package/dist/review/actions/reply-review-comment.js +84 -0
  625. package/dist/review/actions/reply-review-comment.js.map +1 -0
  626. package/dist/review/actions/resolve-review-thread.d.ts +11 -0
  627. package/dist/review/actions/resolve-review-thread.d.ts.map +1 -0
  628. package/dist/review/actions/resolve-review-thread.js +46 -0
  629. package/dist/review/actions/resolve-review-thread.js.map +1 -0
  630. package/dist/review/actions/set-review-status.d.ts +9 -0
  631. package/dist/review/actions/set-review-status.d.ts.map +1 -0
  632. package/dist/review/actions/set-review-status.js +43 -0
  633. package/dist/review/actions/set-review-status.js.map +1 -0
  634. package/dist/review/index.d.ts +5 -0
  635. package/dist/review/index.d.ts.map +1 -0
  636. package/dist/review/index.js +4 -0
  637. package/dist/review/index.js.map +1 -0
  638. package/dist/review/mentions.d.ts +4 -0
  639. package/dist/review/mentions.d.ts.map +1 -0
  640. package/dist/review/mentions.js +39 -0
  641. package/dist/review/mentions.js.map +1 -0
  642. package/dist/review/registry.d.ts +10 -0
  643. package/dist/review/registry.d.ts.map +1 -0
  644. package/dist/review/registry.js +61 -0
  645. package/dist/review/registry.js.map +1 -0
  646. package/dist/review/store.d.ts +63 -0
  647. package/dist/review/store.d.ts.map +1 -0
  648. package/dist/review/store.js +558 -0
  649. package/dist/review/store.js.map +1 -0
  650. package/dist/review/types.d.ts +77 -0
  651. package/dist/review/types.d.ts.map +1 -0
  652. package/dist/review/types.js +2 -0
  653. package/dist/review/types.js.map +1 -0
  654. package/dist/secrets/routes.d.ts +9 -9
  655. package/dist/server/action-discovery.d.ts.map +1 -1
  656. package/dist/server/action-discovery.js +54 -0
  657. package/dist/server/action-discovery.js.map +1 -1
  658. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  659. package/dist/server/agent-engine-api-key-route.d.ts.map +1 -1
  660. package/dist/server/agent-engine-api-key-route.js +5 -0
  661. package/dist/server/agent-engine-api-key-route.js.map +1 -1
  662. package/dist/server/credential-provider.d.ts +26 -0
  663. package/dist/server/credential-provider.d.ts.map +1 -1
  664. package/dist/server/credential-provider.js +82 -0
  665. package/dist/server/credential-provider.js.map +1 -1
  666. package/dist/server/framework-request-handler.d.ts.map +1 -1
  667. package/dist/server/framework-request-handler.js +37 -1
  668. package/dist/server/framework-request-handler.js.map +1 -1
  669. package/dist/setup-connections/index.d.ts +6 -0
  670. package/dist/setup-connections/index.d.ts.map +1 -0
  671. package/dist/setup-connections/index.js +6 -0
  672. package/dist/setup-connections/index.js.map +1 -0
  673. package/dist/setup-connections/onboarding.d.ts +14 -0
  674. package/dist/setup-connections/onboarding.d.ts.map +1 -0
  675. package/dist/setup-connections/onboarding.js +23 -0
  676. package/dist/setup-connections/onboarding.js.map +1 -0
  677. package/dist/transcription/builder-transcription.d.ts +1 -0
  678. package/dist/transcription/builder-transcription.d.ts.map +1 -1
  679. package/dist/transcription/builder-transcription.js +5 -2
  680. package/dist/transcription/builder-transcription.js.map +1 -1
  681. package/dist/vite/action-types-plugin.d.ts.map +1 -1
  682. package/dist/vite/action-types-plugin.js +52 -0
  683. package/dist/vite/action-types-plugin.js.map +1 -1
  684. package/docs/content/agent-native-toolkit.mdx +87 -80
  685. package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +89 -80
  686. package/docs/content/locales/de-DE/agent-native-toolkit.mdx +89 -80
  687. package/docs/content/locales/es-ES/agent-native-toolkit.mdx +89 -80
  688. package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +89 -80
  689. package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +89 -80
  690. package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +89 -80
  691. package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +89 -80
  692. package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +89 -80
  693. package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +89 -80
  694. package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +89 -80
  695. package/docs/content/toolkit-agent-ux.mdx +43 -0
  696. package/docs/content/toolkit-app-adapters.mdx +9 -0
  697. package/docs/content/toolkit-collaboration-ui.mdx +12 -0
  698. package/docs/content/toolkit-collaboration.mdx +74 -0
  699. package/docs/content/toolkit-command-navigation.mdx +106 -0
  700. package/docs/content/toolkit-comments-review.mdx +98 -0
  701. package/docs/content/toolkit-history.mdx +95 -0
  702. package/docs/content/toolkit-observability.mdx +41 -0
  703. package/docs/content/toolkit-org-team.mdx +80 -0
  704. package/docs/content/toolkit-resources.mdx +57 -0
  705. package/docs/content/toolkit-settings.mdx +65 -0
  706. package/docs/content/toolkit-setup-connections.mdx +109 -0
  707. package/docs/content/toolkit-sharing-ui.mdx +18 -6
  708. package/docs/content/toolkit-sharing.mdx +81 -0
  709. package/docs/content/toolkit-shell-hooks.mdx +9 -0
  710. package/docs/content/toolkit-ui.mdx +9 -0
  711. package/package.json +36 -2
  712. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
  713. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
  714. package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
@@ -1,8 +1,14 @@
1
1
  import { usePinchZoom, useT } from "@agent-native/core/client";
2
- import { getDraftGeometryFromPoints } from "@shared/canvas-math";
2
+ import {
3
+ DEFAULT_CANVAS_MAX_ZOOM,
4
+ DEFAULT_CANVAS_MIN_ZOOM,
5
+ getDraftGeometryFromPoints,
6
+ } from "@shared/canvas-math";
3
7
  import { ensureCodeLayerNodeIdsInHtml } from "@shared/code-layer";
8
+ import { IconPlugConnectedX, IconRefresh } from "@tabler/icons-react";
4
9
  import { useRef, useEffect, useCallback, useMemo, useState } from "react";
5
10
 
11
+ import { Button } from "@/components/ui/button";
6
12
  // NOTE: This wires up the NEW shared visual-editor DrawOverlay + comment-pin
7
13
  // components from `@/components/visual-editor`. The legacy iframe-only
8
14
  // DrawOverlay at `./DrawOverlay.tsx` is intentionally NOT used here — both
@@ -21,6 +27,7 @@ import { hitTestBridgeScript } from "../../../.generated/bridge/hit-test.generat
21
27
  import { motionPreviewBridgeScript } from "../../../.generated/bridge/motion-preview.generated";
22
28
  import { navBridgeScript } from "../../../.generated/bridge/nav.generated";
23
29
  import { shaderFillPreviewBridgeScript } from "../../../.generated/bridge/shader-fill-preview.generated";
30
+ import { shaderRuntimeBridgeScript } from "../../../.generated/bridge/shader-runtime.generated";
24
31
  import { tweakBridgeScript } from "../../../.generated/bridge/tweak.generated";
25
32
  import { zoomBridgeScript } from "../../../.generated/bridge/zoom.generated";
26
33
  import { isTrustedCanvasBridgeMessage } from "./bridge-security";
@@ -78,7 +85,15 @@ function isAllowedFusionOrigin(
78
85
  export interface MotionTrackWire {
79
86
  targetNodeId: string;
80
87
  property: string;
88
+ /**
89
+ * Keyframes normalised to the TRACK's own span; `ease` accepts CSS timing
90
+ * functions plus `spring(bounce[, settle])` tokens and `linear(...)` lists.
91
+ */
81
92
  keyframes: Array<{ t: number; value: string; ease?: string }>;
93
+ /** Track start offset within the timeline, ms (staggering). */
94
+ delayMs?: number;
95
+ /** Per-track animation span, ms. Omitted = whole timeline. */
96
+ durationMs?: number;
82
97
  }
83
98
 
84
99
  /**
@@ -96,14 +111,28 @@ ${motionPreviewBridgeScript}
96
111
  `;
97
112
 
98
113
  /**
99
- * Shader-fill preview bridge. ALWAYS injected alongside the other bridge
100
- * scripts so the parent can apply a CSS gradient approximation of a shader
101
- * fill to the currently-selected element without persisting anything.
114
+ * Shader scripts. ALWAYS injected alongside the other bridge scripts:
115
+ *
116
+ * 1. The code-backed GLSL shader RUNTIME (window.__anShaders) — renders
117
+ * persisted `<script type="application/x-agent-native-shader">` blocks
118
+ * onto annotated elements via WebGL, and powers live GLSL previews.
119
+ * Persisted screens embed their own copy for standalone export; the
120
+ * runtime is idempotent so double-injection is safe. Source of truth:
121
+ * app/components/design/bridge/shader-runtime.bridge.ts (also embedded
122
+ * into saved HTML by shared/shader-fills.ts ensureShaderRuntime()).
123
+ *
124
+ * 2. The shader-fill preview BRIDGE — postMessage protocol handler. Legacy
125
+ * messages apply a CSS gradient approximation of a shader fill to the
126
+ * selected element; `glsl-shader-*` messages delegate to the runtime for
127
+ * live WebGL previews, knob scrubbing, and rescans. Nothing persists.
102
128
  *
103
129
  * Source: app/components/design/bridge/shader-fill-preview.bridge.ts
104
- * Compiled: .generated/bridge/shader-fill-preview.generated.ts (run bridge/codegen.ts to update)
130
+ * Compiled: .generated/bridge/*.generated.ts (run bridge/codegen.ts to update)
105
131
  */
106
132
  const SHADER_FILL_PREVIEW_BRIDGE_SCRIPT = `
133
+ <script data-agent-native-shader-runtime data-runtime-version="1">
134
+ ${shaderRuntimeBridgeScript}
135
+ </script>
107
136
  <script data-agent-native-shader-fill-preview-bridge>
108
137
  ${shaderFillPreviewBridgeScript}
109
138
  </script>
@@ -410,6 +439,13 @@ interface DesignCanvasProps {
410
439
  onExitDrawMode?: () => void;
411
440
  /** Whether comment-pin drop mode is active. */
412
441
  pinMode?: boolean;
442
+ /**
443
+ * When true, suppresses rendering of comment pins on this canvas (e.g. the
444
+ * Figma-style Shift+C "hide comments" toggle in single-screen mode). Only
445
+ * affects pin *visibility* — pin drop mode (`pinMode`) still behaves
446
+ * normally underneath so toggling this back off doesn't lose anything.
447
+ */
448
+ commentPinsHidden?: boolean;
413
449
  selectedSelector?: string | null;
414
450
  selectedSelectorCandidates?: string[];
415
451
  selectedSelectorGroups?: string[][];
@@ -457,6 +493,14 @@ interface DesignCanvasProps {
457
493
  * When omitted the bridge falls back to "ease".
458
494
  */
459
495
  motionDefaultEase?: string;
496
+ /**
497
+ * Timeline duration in ms, threaded to the motion-preview bridge with the
498
+ * tracks so per-track `delayMs`/`durationMs` offsets can be mapped before
499
+ * the first scrub tick arrives (ticks also carry it). Optional — without
500
+ * it, offset tracks preview correctly from the first `motion-preview`
501
+ * message onward.
502
+ */
503
+ motionDurationMs?: number;
460
504
  /**
461
505
  * Explicit iframe width in pixels. When provided it overrides the width
462
506
  * derived from `deviceFrame`, enabling per-breakpoint preview (e.g. Mobile
@@ -486,6 +530,52 @@ interface DesignCanvasProps {
486
530
  /** The CSS `background` value returned by preview-shader-fill. */
487
531
  css: string;
488
532
  } | null;
533
+ /**
534
+ * On-canvas gradient-edit handles (Figma parity) for a gradient fill on an
535
+ * element INSIDE this screen's iframe content (as opposed to a board/draft
536
+ * primitive or the screen-frame's own background, which MultiScreenCanvas
537
+ * already draws chrome for directly — see DesignEditor's gradientEditTarget
538
+ * derivation). When set, posts `{ type: "gradient-edit-target", nodeId,
539
+ * cssValue }` into the iframe so the editor-chrome bridge renders drag
540
+ * handles on that element; `null`/`undefined` posts `{ type:
541
+ * "gradient-edit-clear" }`. `nodeId` must be a `data-agent-native-node-id`
542
+ * value. See editor-chrome.bridge.ts's gradientEditTarget doc comment for
543
+ * the full contract this implements.
544
+ */
545
+ gradientEditTarget?: {
546
+ nodeId: string;
547
+ cssValue: string;
548
+ } | null;
549
+ /**
550
+ * Called with the bridge's `gradient-edit-change` postMessages while a
551
+ * gradient-edit drag on an in-screen element (gradientEditTarget above) is
552
+ * live — `phase: "preview"` on every drag tick, `"commit"` once on
553
+ * pointerup, mirroring GradientEditOverlayTarget's onChange contract in
554
+ * MultiScreenCanvas.tsx so the parent can route both through the same
555
+ * style-apply path `visual-style-change` uses.
556
+ */
557
+ onGradientEditChange?: (
558
+ nodeId: string,
559
+ cssValue: string,
560
+ phase: "preview" | "commit",
561
+ ) => void;
562
+ /**
563
+ * Interaction-state forced preview (Figma/Webflow parity, phase 2 — see
564
+ * `shared/interaction-states.ts`'s "Forced-preview mechanism" doc comment).
565
+ * When set, posts `{ type: "state-preview", nodeId, state }` into the
566
+ * iframe so the editor-chrome bridge sets `data-an-state-preview="<state>"`
567
+ * on the matching `[data-agent-native-node-id]` element — the persisted
568
+ * twin CSS rule (written by `duplicateStatePreviewRules`) does the actual
569
+ * styling, so the bridge does zero CSS work of its own.
570
+ * `null`/`undefined` posts `{ type: "state-preview", nodeId: null, state:
571
+ * null }`, clearing whichever element is currently force-previewing.
572
+ * Mirrors `gradientEditTarget`'s postMessage-sync-effect pattern exactly —
573
+ * see that prop's doc comment above.
574
+ */
575
+ statePreviewTarget?: {
576
+ nodeId: string;
577
+ state: string;
578
+ } | null;
489
579
  /**
490
580
  * Called when the user clicks the component-instance source tag (the
491
581
  * "ComponentName →" pill that floats above a selected component root).
@@ -514,6 +604,52 @@ interface DesignCanvasProps {
514
604
  * `draftPrimitiveToInsert` on the overview side).
515
605
  */
516
606
  onCreatePrimitive?: (spec: CreatePrimitiveSpec) => void;
607
+ /**
608
+ * OS file drag-and-drop (Figma parity): fired when the user drops native
609
+ * OS files (e.g. images dragged from Finder/Explorer) onto this single-
610
+ * screen canvas. `target.screenContentPoint` is the drop point converted to
611
+ * SCREEN-CONTENT coordinates via `getScreenContentPointFromClient` — the
612
+ * same space `onCreatePrimitive` geometry and `draftPrimitiveToInsert` use
613
+ * — NOT viewport pixels. `target.screenId` echoes this canvas's own
614
+ * `screenId` prop (when set) so a caller wiring both MultiScreenCanvas and
615
+ * DesignCanvas can use one shared handler keyed on screen id. This
616
+ * component only resolves WHERE the drop landed — it does NOT read file
617
+ * contents, upload, or insert anything; that remains the caller's job
618
+ * (DesignEditor's paste-image pipeline), matching `onCreatePrimitive`'s own
619
+ * division of responsibility.
620
+ */
621
+ onDropFiles?: (
622
+ files: File[],
623
+ target: {
624
+ screenContentPoint: { x: number; y: number };
625
+ screenId?: string;
626
+ },
627
+ ) => void;
628
+ /**
629
+ * Contract for DesignEditor: when true, the Figma-style "hand" tool is
630
+ * armed, so a plain left-button drag anywhere on this canvas (over the
631
+ * scroll-container background OR over the iframe/creation-overlay area)
632
+ * pans instead of selecting/drawing. Mirrors MultiScreenCanvas's own
633
+ * `tool === "hand"` → `beginPan` gating (see `e.button === 0 && tool ===
634
+ * "hand"` in MultiScreenCanvas). Middle-mouse-button drag always pans
635
+ * regardless of this prop, matching MultiScreenCanvas's unconditional
636
+ * `e.button === 1` branch. Defaults to `false` (no behavior change when
637
+ * omitted) — DesignEditor should pass `activeTool === "hand"` (or its
638
+ * equivalent tool-state) once wired.
639
+ */
640
+ handToolActive?: boolean;
641
+ /**
642
+ * Contract for DesignEditor: when true, the spacebar is currently held
643
+ * down, which — Figma-style — temporarily activates the same pan-on-drag
644
+ * behavior as `handToolActive` without changing the active tool. This
645
+ * component does not listen for the spacebar itself (keydown/keyup would
646
+ * need to be captured at a level that already reaches this component
647
+ * reliably, e.g. a window-level listener in DesignEditor gated on not
648
+ * being in a text-editing context); DesignEditor should track pressed
649
+ * state there and pass it through here. Defaults to `false` (no behavior
650
+ * change when omitted).
651
+ */
652
+ spacePanActive?: boolean;
517
653
  }
518
654
 
519
655
  /** Creation tools supported by the single-screen click-to-place overlay. */
@@ -523,7 +659,11 @@ export type CreationTool =
523
659
  | "line"
524
660
  | "arrow"
525
661
  | "text"
526
- | "pen";
662
+ | "pen"
663
+ // Figma parity: the frame tool (F/A) used inside a focused screen places a
664
+ // bare container <div> (a nested "frame") instead of forcing a jump out to
665
+ // overview to draw a new screen.
666
+ | "frame";
527
667
 
528
668
  /**
529
669
  * Geometry payload emitted by the single-screen creation overlay. Coordinates
@@ -579,6 +719,71 @@ export function getScreenContentPointFromClient(
579
719
  };
580
720
  }
581
721
 
722
+ /**
723
+ * Computes the scroll-container delta needed to keep a given viewport
724
+ * (client) point anchored to the same content underneath it while zooming.
725
+ *
726
+ * This is the cursor-anchor math used by the `pinch-zoom-wheel` bridge
727
+ * handler (forwarded from inside the iframe, since wheel events don't bubble
728
+ * out of iframes — see `zoom.bridge.ts`) to mirror `usePinchZoom`'s own
729
+ * zoom-to-cursor behavior for gestures that occur over the iframe itself.
730
+ *
731
+ * IMPORTANT — this math is only correct when the scaled layer's
732
+ * `transform-origin` is `top left` (or equivalent), so that the layer's own
733
+ * top-left corner is a fixed point in scroll-content space regardless of
734
+ * zoom. If the scaled layer is instead centered via `transform-origin:
735
+ * center center` plus flex/grid centering, the *painted* box re-centers
736
+ * around the container's center on every zoom change — the flex-centering
737
+ * repositions the *layout* box so its center lands at the container's
738
+ * center, and `transform-origin: center` then scales around that already-
739
+ * centered point. That is a well-defined anchor too, but it is NOT the one
740
+ * this function (or usePinchZoom) computes, so cursor-following would drift.
741
+ * See the long comment on the "none"-mode zoom layer in the component render
742
+ * for how DesignCanvas keeps this invariant true while still looking
743
+ * centered at rest.
744
+ *
745
+ * @param anchorClient The viewport point to keep stationary (e.g. cursor
746
+ * position converted from iframe-document space to viewport space).
747
+ * @param containerRect The scroll container's `getBoundingClientRect()`.
748
+ * @param scrollOffset The scroll container's current `{ scrollLeft,
749
+ * scrollTop }`.
750
+ * @param zoomRatio `nextZoom / currentZoom` — how much the zoom is about to
751
+ * change by by (>1 zooming in, <1 zooming out).
752
+ */
753
+ export function getZoomToCursorScrollDelta(
754
+ anchorClient: { x: number; y: number },
755
+ containerRect: { left: number; top: number },
756
+ scrollOffset: { scrollLeft: number; scrollTop: number },
757
+ zoomRatio: number,
758
+ ): { dx: number; dy: number } {
759
+ const cx = anchorClient.x - containerRect.left + scrollOffset.scrollLeft;
760
+ const cy = anchorClient.y - containerRect.top + scrollOffset.scrollTop;
761
+ return {
762
+ dx: cx * (zoomRatio - 1),
763
+ dy: cy * (zoomRatio - 1),
764
+ };
765
+ }
766
+
767
+ /**
768
+ * True when a drag event is carrying native OS files (e.g. dragged in from
769
+ * Finder/Explorer) rather than an internal HTML5 DOM drag (element reorder,
770
+ * the extensions-panel native-asset drag, etc). A real OS file drag always
771
+ * includes the "Files" type in `dataTransfer.types` — reliably present during
772
+ * dragenter/dragover even before drop, when `dataTransfer.files` itself is
773
+ * empty for security reasons — so this is the correct check to gate
774
+ * preventDefault()/overlay logic on, not `files.length`.
775
+ */
776
+ export function isOsFileDragEvent(event: {
777
+ dataTransfer: { types: readonly string[] | DOMStringList } | null;
778
+ }): boolean {
779
+ const types = event.dataTransfer?.types;
780
+ if (!types) return false;
781
+ for (let i = 0; i < types.length; i += 1) {
782
+ if (types[i] === "Files") return true;
783
+ }
784
+ return false;
785
+ }
786
+
582
787
  function getExternalPreviewUrl(content: string): string | null {
583
788
  const trimmed = content.trim();
584
789
  if (!/^https?:\/\//i.test(trimmed)) return null;
@@ -632,6 +837,21 @@ function snapshotEndpointUrl(bridgeUrl: string, previewUrl: string): string {
632
837
  return endpoint.toString();
633
838
  }
634
839
 
840
+ /**
841
+ * Exponential backoff delay (ms) for the localhost bridge snapshot auto-retry
842
+ * loop, keyed by the zero-based retry attempt number (0 = first retry after
843
+ * the initial failed fetch). Doubles each attempt starting at 1.5s, capped at
844
+ * ~15s so a genuinely-down dev server settles into a slow, low-noise poll
845
+ * instead of hammering the bridge every 1.5s forever.
846
+ */
847
+ const SNAPSHOT_RETRY_BASE_DELAY_MS = 1500;
848
+ const SNAPSHOT_RETRY_MAX_DELAY_MS = 15000;
849
+ export function getSnapshotRetryDelayMs(attempt: number): number {
850
+ const safeAttempt = Number.isFinite(attempt) ? Math.max(0, attempt) : 0;
851
+ const delay = SNAPSHOT_RETRY_BASE_DELAY_MS * 2 ** safeAttempt;
852
+ return Math.min(SNAPSHOT_RETRY_MAX_DELAY_MS, delay);
853
+ }
854
+
635
855
  const TRANSPARENT_EMBEDDED_FRAME_STYLE =
636
856
  "<style data-agent-native-transparent-frame>html,body{background:transparent!important;}body{background-color:transparent!important;}</style>";
637
857
 
@@ -759,6 +979,7 @@ export function DesignCanvas({
759
979
  drawMode,
760
980
  onExitDrawMode,
761
981
  pinMode,
982
+ commentPinsHidden,
762
983
  selectedSelector,
763
984
  selectedSelectorCandidates = [],
764
985
  selectedSelectorGroups = [],
@@ -776,16 +997,41 @@ export function DesignCanvas({
776
997
  onPrototypeNavigate,
777
998
  motionTracks,
778
999
  motionDefaultEase,
1000
+ motionDurationMs,
779
1001
  previewWidthPx,
780
1002
  onComponentSourceJump,
781
1003
  shaderFillPreview,
1004
+ gradientEditTarget,
1005
+ onGradientEditChange,
1006
+ statePreviewTarget,
782
1007
  activeCreationTool = null,
783
1008
  onCreatePrimitive,
1009
+ onDropFiles,
1010
+ handToolActive = false,
1011
+ spacePanActive = false,
784
1012
  }: DesignCanvasProps) {
785
1013
  const t = useT();
786
1014
  const iframeRef = useRef<HTMLIFrameElement>(null);
787
1015
  const scrollContainerRef = useRef<HTMLDivElement>(null);
1016
+ const zoomLayerRef = useRef<HTMLDivElement>(null);
788
1017
  const zoomRef = useRef(zoom);
1018
+ // Zoom-invariant chrome: the non-embedded-frame render path below wraps the
1019
+ // iframe in its own CSS `transform: scale(zoom / 100)` (see the
1020
+ // `deviceFrame === "none"` and framed branches further down) — a purely
1021
+ // visual, OUTER scale the bridge running INSIDE the iframe has no way to
1022
+ // observe. `editorChromeScaleX/Y` is the only channel that tells the bridge
1023
+ // what scale its own chrome (selection borders, resize handles, spacing
1024
+ // overlays) must counter-scale by to stay a constant on-screen size, Figma-
1025
+ // style, instead of visually shrinking/growing with content as the user
1026
+ // zooms. The overview caller already folds its own zoom into the
1027
+ // editorChromeScaleX/Y it passes down for exactly this reason; this
1028
+ // component must do the same with its OWN `zoom` prop for single-view,
1029
+ // multiplying in whatever scale the caller passed (default 1) rather than
1030
+ // assuming the caller already accounted for it — single-view's caller
1031
+ // historically didn't pass either prop at all, so the bridge always
1032
+ // computed with scale=1 and chrome scaled with content on every zoom.
1033
+ const effectiveEditorChromeScaleX = (zoom / 100) * editorChromeScaleX;
1034
+ const effectiveEditorChromeScaleY = (zoom / 100) * editorChromeScaleY;
789
1035
  const previousContentKeyRef = useRef(contentKey);
790
1036
  const runtimeReplacementContentRef = useRef(runtimeReplacementContent);
791
1037
  const runtimeReplacementKeyRef = useRef(runtimeReplacementKey);
@@ -838,6 +1084,12 @@ export function DesignCanvas({
838
1084
  } | null>(null);
839
1085
  const [externalSnapshotRetryNonce, setExternalSnapshotRetryNonce] =
840
1086
  useState(0);
1087
+ // Exponential backoff for the auto-retry loop below: 1.5s → 3s → 6s →
1088
+ // capped at ~15s, so a genuinely-down dev server doesn't get hammered with
1089
+ // a fixed 1.5s-forever retry loop. Resets to 0 on a successful fetch or a
1090
+ // manual retry click (see handleManualSnapshotRetry) so the next failure
1091
+ // starts the backoff over rather than continuing to climb.
1092
+ const snapshotRetryAttemptRef = useRef(0);
841
1093
  const onExternalContentSnapshotRef = useRef(onExternalContentSnapshot);
842
1094
  const isEmbeddedFrame = Boolean(embeddedFrame);
843
1095
  // Resolve the URL to render in the iframe:
@@ -889,6 +1141,7 @@ export function DesignCanvas({
889
1141
  useEffect(() => {
890
1142
  const previewUrl = rawExternalPreviewUrl;
891
1143
  if (sourceType !== "localhost" || !bridgeUrl || !previewUrl) {
1144
+ snapshotRetryAttemptRef.current = 0;
892
1145
  setFetchedExternalSnapshot((current) =>
893
1146
  current?.url === previewUrl ? current : null,
894
1147
  );
@@ -896,6 +1149,7 @@ export function DesignCanvas({
896
1149
  return;
897
1150
  }
898
1151
  if (externalSnapshotHtml) {
1152
+ snapshotRetryAttemptRef.current = 0;
899
1153
  setExternalSnapshotState(null);
900
1154
  return;
901
1155
  }
@@ -905,9 +1159,11 @@ export function DesignCanvas({
905
1159
  const endpoint = snapshotEndpointUrl(bridgeUrl, previewUrl);
906
1160
  const scheduleRetry = () => {
907
1161
  if (!requiresEditableExternalSnapshot || cancelled) return;
1162
+ const delay = getSnapshotRetryDelayMs(snapshotRetryAttemptRef.current);
1163
+ snapshotRetryAttemptRef.current += 1;
908
1164
  retryTimer = window.setTimeout(() => {
909
1165
  setExternalSnapshotRetryNonce((nonce) => nonce + 1);
910
- }, 1500);
1166
+ }, delay);
911
1167
  };
912
1168
  setExternalSnapshotState({ url: previewUrl, status: "loading" });
913
1169
  void (async () => {
@@ -948,6 +1204,7 @@ export function DesignCanvas({
948
1204
  },
949
1205
  }).content;
950
1206
  if (cancelled) return;
1207
+ snapshotRetryAttemptRef.current = 0;
951
1208
  setFetchedExternalSnapshot({ url: previewUrl, html: stamped });
952
1209
  setExternalSnapshotState(null);
953
1210
  onExternalContentSnapshotRef.current?.({
@@ -982,6 +1239,15 @@ export function DesignCanvas({
982
1239
  sourceType,
983
1240
  ]);
984
1241
 
1242
+ // Manual retry (offline-state "Retry" button): reset the backoff so the
1243
+ // user-initiated attempt fires immediately rather than waiting out
1244
+ // whatever delay the auto-retry loop had climbed to, then trigger the
1245
+ // fetch effect above via the nonce dependency.
1246
+ const handleManualSnapshotRetry = useCallback(() => {
1247
+ snapshotRetryAttemptRef.current = 0;
1248
+ setExternalSnapshotRetryNonce((nonce) => nonce + 1);
1249
+ }, []);
1250
+
985
1251
  const queuedAnnotationPins = useMemo(
986
1252
  () =>
987
1253
  annotationPins.filter(
@@ -1016,12 +1282,68 @@ export function DesignCanvas({
1016
1282
  containerRef: scrollContainerRef,
1017
1283
  zoom,
1018
1284
  setZoom: onZoomChange ?? (() => {}),
1019
- min: 10,
1020
- max: 500,
1285
+ // Match the overview's Figma-parity zoom range (2%–25600%) rather than a
1286
+ // narrower single-screen-only clamp. Both the "none" (fills-canvas) and
1287
+ // framed (desktop/tablet/mobile DeviceFrame) render paths share this same
1288
+ // `zoom` prop/state and hook call today — there's no separate framed-mode
1289
+ // clamp to preserve. DeviceFrame chrome (title bars, home indicator) and
1290
+ // the iframe both scale proportionally via the outer `transform: scale()`
1291
+ // wrapper, so extreme zoom just renders very small/very large, it doesn't
1292
+ // break layout or clip content (fixed-px chrome scales with everything
1293
+ // else). Verified 1280px-wide desktop frame at 256x (25600%) renders at
1294
+ // ~327,680px, comfortably under browser max-element-size limits.
1295
+ min: DEFAULT_CANVAS_MIN_ZOOM,
1296
+ max: DEFAULT_CANVAS_MAX_ZOOM,
1021
1297
  zoomToCursor: deviceFrame === "none",
1022
1298
  enabled: Boolean(onZoomChange),
1023
1299
  });
1024
1300
 
1301
+ // T-zoom-anchor: the "none"-mode zoom layer now uses `transform-origin: top
1302
+ // left` (see the render below) so Cmd/Ctrl+wheel and pinch zoom correctly
1303
+ // keep the point under the cursor stationary — usePinchZoom's own
1304
+ // zoomToCursor math and the pinch-zoom-wheel handler above both assume that
1305
+ // invariant. That means the layout is no longer flex-centered, so on first
1306
+ // mount (and whenever the content is re-keyed, e.g. a screen switch) we
1307
+ // imperatively center the scroll position once here — mirroring what the
1308
+ // flex-centering used to give us "for free" at rest — without touching
1309
+ // scroll position during an actual zoom gesture (those already apply their
1310
+ // own precise scroll deltas above).
1311
+ //
1312
+ // BP-DEEP item 2: also re-center when `previewWidthPx` changes (a
1313
+ // breakpoint chip is clicked). Without this, switching to a narrower
1314
+ // breakpoint shrinks the wrapper's rendered width in place — the scroll
1315
+ // container's existing scrollLeft (from whatever it was showing at the
1316
+ // WIDER content's width) is left untouched, so the narrower frame renders
1317
+ // pinned to the left edge of the visible area instead of centered. This is
1318
+ // the single-screen equivalent of the device-preview control's centered
1319
+ // `deviceFrame !== "none"` branch below, without giving up the top-left
1320
+ // transform-origin the zoom-anchor math above depends on.
1321
+ useEffect(() => {
1322
+ if (deviceFrame !== "none") return;
1323
+ const scroll = scrollContainerRef.current;
1324
+ const layer = zoomLayerRef.current;
1325
+ if (!scroll || !layer) return;
1326
+ // Read the SCROLL CONTAINER's own scrollWidth/scrollHeight, not the zoom
1327
+ // layer's: `transform: scale()` never changes an element's own layout
1328
+ // size (scrollWidth/scrollHeight of the transformed element itself stay
1329
+ // at its pre-transform size), but it DOES change how much scrollable
1330
+ // overflow it contributes to its ancestor — which is exactly the
1331
+ // post-transform visual bounds we need here.
1332
+ scroll.scrollLeft = Math.max(
1333
+ 0,
1334
+ (scroll.scrollWidth - scroll.clientWidth) / 2,
1335
+ );
1336
+ scroll.scrollTop = Math.max(
1337
+ 0,
1338
+ (scroll.scrollHeight - scroll.clientHeight) / 2,
1339
+ );
1340
+ // Only re-center on a genuine content swap (screen switch / remount) or a
1341
+ // breakpoint width change, not on every zoom change — an active zoom
1342
+ // gesture manages its own scroll delta and would otherwise be fought by
1343
+ // this effect on every render.
1344
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1345
+ }, [deviceFrame, contentKey, previewWidthPx]);
1346
+
1025
1347
  // Build the srcdoc. The tweak bridge ALWAYS goes in so the panel works
1026
1348
  // outside Edit mode. The editor chrome bridge is omitted for Interact mode
1027
1349
  // so preview/app users can interact with the app normally.
@@ -1044,8 +1366,14 @@ export function DesignCanvas({
1044
1366
  readOnly ? "true" : "false",
1045
1367
  )
1046
1368
  .replace("__TEXT_EDITING_ENABLED__", editMode ? "true" : "false")
1047
- .replace("__EDITOR_CHROME_SCALE_X__", String(editorChromeScaleX))
1048
- .replace("__EDITOR_CHROME_SCALE_Y__", String(editorChromeScaleY))
1369
+ .replace(
1370
+ "__EDITOR_CHROME_SCALE_X__",
1371
+ String(effectiveEditorChromeScaleX),
1372
+ )
1373
+ .replace(
1374
+ "__EDITOR_CHROME_SCALE_Y__",
1375
+ String(effectiveEditorChromeScaleY),
1376
+ )
1049
1377
  .replace(
1050
1378
  "__DESIGN_CANVAS_SCREEN_ID__",
1051
1379
  JSON.stringify(screenId ?? contentKey ?? ""),
@@ -1186,6 +1514,15 @@ export function DesignCanvas({
1186
1514
  }
1187
1515
  return;
1188
1516
  }
1517
+ if (e.data.type === "gradient-edit-change") {
1518
+ const nodeId = String(e.data.nodeId || "");
1519
+ const cssValue = String(e.data.cssValue || "");
1520
+ const phase = e.data.phase === "commit" ? "commit" : "preview";
1521
+ if (nodeId && cssValue) {
1522
+ onGradientEditChange?.(nodeId, cssValue, phase);
1523
+ }
1524
+ return;
1525
+ }
1189
1526
  if (e.data.type === "text-content-change") {
1190
1527
  const selector = String(e.data.selector || "");
1191
1528
  const value = String(e.data.value ?? "");
@@ -1421,11 +1758,15 @@ export function DesignCanvas({
1421
1758
  // a synthetic WheelEvent to trigger the hook's listener — untrusted
1422
1759
  // events are inconsistent across browsers — so just compute the
1423
1760
  // next zoom directly using the same exponential factor + cursor-anchor
1424
- // math. Clamp range matches the usePinchZoom call above (10–500).
1761
+ // math. Clamp range matches the usePinchZoom call above
1762
+ // (DEFAULT_CANVAS_MIN_ZOOM–DEFAULT_CANVAS_MAX_ZOOM).
1425
1763
  const currentZoom = zoomRef.current;
1426
1764
  const clampedDelta = Math.max(-50, Math.min(50, rawDeltaY));
1427
1765
  const factor = Math.exp(-clampedDelta * 0.01);
1428
- const nextZoom = Math.max(10, Math.min(500, currentZoom * factor));
1766
+ const nextZoom = Math.max(
1767
+ DEFAULT_CANVAS_MIN_ZOOM,
1768
+ Math.min(DEFAULT_CANVAS_MAX_ZOOM, currentZoom * factor),
1769
+ );
1429
1770
  if (!Number.isFinite(nextZoom) || nextZoom === currentZoom) return;
1430
1771
  if (deviceFrame === "none") {
1431
1772
  const rawClientX = Number(e.data.clientX);
@@ -1436,18 +1777,23 @@ export function DesignCanvas({
1436
1777
  }
1437
1778
  // The iframe lives inside a `transform: scale(zoom/100)` wrapper, so
1438
1779
  // its visual scale relative to viewport is currentZoom / 100. Convert
1439
- // the iframe-document point under the cursor → viewport point
1440
- // scroll-content point, then preserve cursor anchoring while zooming.
1780
+ // the iframe-document point under the cursor → viewport point, then
1781
+ // preserve cursor anchoring while zooming via
1782
+ // getZoomToCursorScrollDelta (requires the zoom layer's
1783
+ // transform-origin to be "top left" — see that function's doc
1784
+ // comment and the "none"-mode zoom layer render comment).
1441
1785
  const iframeRect = iframe.getBoundingClientRect();
1442
1786
  const scrollRect = scroll.getBoundingClientRect();
1443
1787
  const scale = currentZoom / 100;
1444
1788
  const viewportX = iframeRect.left + rawClientX * scale;
1445
1789
  const viewportY = iframeRect.top + rawClientY * scale;
1446
- const cx = viewportX - scrollRect.left + scroll.scrollLeft;
1447
- const cy = viewportY - scrollRect.top + scroll.scrollTop;
1448
1790
  const ratio = nextZoom / currentZoom;
1449
- const dx = cx * (ratio - 1);
1450
- const dy = cy * (ratio - 1);
1791
+ const { dx, dy } = getZoomToCursorScrollDelta(
1792
+ { x: viewportX, y: viewportY },
1793
+ scrollRect,
1794
+ scroll,
1795
+ ratio,
1796
+ );
1451
1797
  onZoomChange(nextZoom);
1452
1798
  requestAnimationFrame(() => {
1453
1799
  scroll.scrollLeft += dx;
@@ -1466,6 +1812,7 @@ export function DesignCanvas({
1466
1812
  onElementHover,
1467
1813
  onClearSelection,
1468
1814
  onVisualStyleChange,
1815
+ onGradientEditChange,
1469
1816
  onTextContentChange,
1470
1817
  onTextEditingStateChange,
1471
1818
  onElementDblClickText,
@@ -1534,6 +1881,7 @@ export function DesignCanvas({
1534
1881
  type: "motion-load-tracks",
1535
1882
  tracks: motionTracks,
1536
1883
  defaultEase: motionDefaultEase,
1884
+ durationMs: motionDurationMs,
1537
1885
  },
1538
1886
  "*",
1539
1887
  );
@@ -1565,6 +1913,7 @@ export function DesignCanvas({
1565
1913
  lockedSelectors,
1566
1914
  motionTracks,
1567
1915
  motionDefaultEase,
1916
+ motionDurationMs,
1568
1917
  scaleMode,
1569
1918
  selectedSelector,
1570
1919
  selectedSelectorCandidates,
@@ -1627,11 +1976,12 @@ export function DesignCanvas({
1627
1976
  type: "motion-load-tracks",
1628
1977
  tracks: motionTracks,
1629
1978
  defaultEase: motionDefaultEase,
1979
+ durationMs: motionDurationMs,
1630
1980
  },
1631
1981
  "*",
1632
1982
  );
1633
1983
  }
1634
- }, [motionTracks, motionDefaultEase]);
1984
+ }, [motionTracks, motionDefaultEase, motionDurationMs]);
1635
1985
 
1636
1986
  // Sync shader-fill preview to the iframe whenever the prop changes.
1637
1987
  // When cleared (null / undefined) send a clear message so the bridge
@@ -1655,6 +2005,47 @@ export function DesignCanvas({
1655
2005
  }
1656
2006
  }, [shaderFillPreview]);
1657
2007
 
2008
+ // Item 8 — sync the in-screen gradient-edit target to the iframe whenever
2009
+ // the prop changes. Mirrors shaderFillPreview's pattern exactly: cleared
2010
+ // (null/undefined) posts gradient-edit-clear, set posts gradient-edit-target
2011
+ // so the editor-chrome bridge renders drag handles on that node.
2012
+ useEffect(() => {
2013
+ const win = iframeRef.current?.contentWindow;
2014
+ if (!win) return;
2015
+ if (!gradientEditTarget) {
2016
+ win.postMessage({ type: "gradient-edit-clear" }, "*");
2017
+ } else {
2018
+ win.postMessage(
2019
+ {
2020
+ type: "gradient-edit-target",
2021
+ nodeId: gradientEditTarget.nodeId,
2022
+ cssValue: gradientEditTarget.cssValue,
2023
+ },
2024
+ "*",
2025
+ );
2026
+ }
2027
+ }, [gradientEditTarget]);
2028
+
2029
+ // Interaction-state forced preview (phase 2) — sync statePreviewTarget to
2030
+ // the iframe whenever the prop changes, exactly mirroring gradientEditTarget's
2031
+ // sync effect above: cleared (null/undefined) posts a clearing
2032
+ // state-preview message, set posts the node id + state so the bridge sets
2033
+ // `data-an-state-preview` on the matching element (see the
2034
+ // `statePreviewTarget` prop doc comment and shared/interaction-states.ts's
2035
+ // "Forced-preview mechanism" doc comment for the full contract).
2036
+ useEffect(() => {
2037
+ const win = iframeRef.current?.contentWindow;
2038
+ if (!win) return;
2039
+ win.postMessage(
2040
+ {
2041
+ type: "state-preview",
2042
+ nodeId: statePreviewTarget?.nodeId ?? null,
2043
+ state: statePreviewTarget?.state ?? null,
2044
+ },
2045
+ "*",
2046
+ );
2047
+ }, [statePreviewTarget]);
2048
+
1658
2049
  // Push the constant-size chrome scale into the iframe LIVE (CSS vars only) when
1659
2050
  // overview zoom settles. This is intentionally separate from the srcdoc build so
1660
2051
  // a scale change never rebuilds srcdoc / reloads the iframe (which flashes the
@@ -1665,10 +2056,14 @@ export function DesignCanvas({
1665
2056
  useEffect(() => {
1666
2057
  postOneShotBridgeMessage({
1667
2058
  type: "set-editor-chrome-scale",
1668
- scaleX: editorChromeScaleX,
1669
- scaleY: editorChromeScaleY,
2059
+ scaleX: effectiveEditorChromeScaleX,
2060
+ scaleY: effectiveEditorChromeScaleY,
1670
2061
  });
1671
- }, [editorChromeScaleX, editorChromeScaleY, postOneShotBridgeMessage]);
2062
+ }, [
2063
+ effectiveEditorChromeScaleX,
2064
+ effectiveEditorChromeScaleY,
2065
+ postOneShotBridgeMessage,
2066
+ ]);
1672
2067
 
1673
2068
  // Sync readOnly to the bridge IN-PLACE via postMessage so switching the active
1674
2069
  // surface (board ↔ screen) does not rebuild srcdoc / reload the iframe.
@@ -2013,6 +2408,144 @@ export function DesignCanvas({
2013
2408
  surface.focus({ preventScroll: true });
2014
2409
  }, []);
2015
2410
 
2411
+ // Single-screen pan (Figma parity §3): middle-mouse-button drag always
2412
+ // pans (mirrors MultiScreenCanvas's unconditional `e.button === 1` branch
2413
+ // in its own beginPan); a plain left-button drag pans too when the hand
2414
+ // tool is armed (`handToolActive`) or the spacebar is held
2415
+ // (`spacePanActive` — see the prop doc comment for why DesignCanvas
2416
+ // doesn't listen for the spacebar itself). Movement is 1:1 and
2417
+ // un-animated (direct scrollLeft/scrollTop deltas each mousemove, no
2418
+ // inertia/momentum), matching MultiScreenCanvas's own beginPan feel —
2419
+ // plain `mousemove`/`mouseup` window listeners for the drag's lifetime,
2420
+ // same pattern as its `installDragListeners(handleMouseMove, handlePanEnd)`.
2421
+ //
2422
+ // These handlers live on the scroll container itself, so they fire for
2423
+ // drags that start over the empty canvas background or the creation
2424
+ // overlay (both real DOM nodes in this document). A drag that starts
2425
+ // *inside* the iframe's own document does not reach these handlers —
2426
+ // mousedown does not cross a same-origin/cross-document boundary any more
2427
+ // than wheel events do (see the pinch-zoom-wheel bridge message above for
2428
+ // the equivalent problem with wheel) — so panning from inside interactive
2429
+ // iframe content is a follow-up that would need its own bridge message,
2430
+ // not a gap introduced by this change.
2431
+ const [isPanningState, setIsPanningState] = useState(false);
2432
+
2433
+ const handleScrollSurfaceMouseDown = useCallback(
2434
+ (e: React.MouseEvent<HTMLDivElement>) => {
2435
+ const isMiddleButton = e.button === 1;
2436
+ const isLeftPanGesture =
2437
+ e.button === 0 && (handToolActive || spacePanActive);
2438
+ if (!isMiddleButton && !isLeftPanGesture) return;
2439
+ e.preventDefault();
2440
+ let lastClientX = e.clientX;
2441
+ let lastClientY = e.clientY;
2442
+ setIsPanningState(true);
2443
+
2444
+ const handleMouseMove = (ev: MouseEvent) => {
2445
+ const scroll = scrollContainerRef.current;
2446
+ if (!scroll) return;
2447
+ const dx = ev.clientX - lastClientX;
2448
+ const dy = ev.clientY - lastClientY;
2449
+ lastClientX = ev.clientX;
2450
+ lastClientY = ev.clientY;
2451
+ // 1:1 drag-scroll, no inertia: dragging right/down moves the
2452
+ // viewport left/up over the content, i.e. subtract the pointer delta
2453
+ // from scroll position.
2454
+ scroll.scrollLeft -= dx;
2455
+ scroll.scrollTop -= dy;
2456
+ };
2457
+ const handleMouseUp = () => {
2458
+ setIsPanningState(false);
2459
+ window.removeEventListener("mousemove", handleMouseMove);
2460
+ window.removeEventListener("mouseup", handleMouseUp);
2461
+ };
2462
+ window.addEventListener("mousemove", handleMouseMove);
2463
+ window.addEventListener("mouseup", handleMouseUp);
2464
+ },
2465
+ [handToolActive, spacePanActive],
2466
+ );
2467
+
2468
+ const panCursor = isPanningState
2469
+ ? "grabbing"
2470
+ : handToolActive || spacePanActive
2471
+ ? "grab"
2472
+ : null;
2473
+
2474
+ // OS file drag-and-drop (Figma parity §1): the sandboxed iframe sits on top
2475
+ // of the wrapper and is a normal DOM element to the parent document's drag
2476
+ // events (dragenter/dragover/drop still bubble through it to the wrapper —
2477
+ // unlike mouse clicks, an iframe does not need its own listeners to forward
2478
+ // these), but relying on that alone is fragile across browsers/sandbox
2479
+ // combinations. Track a native-file-drag-in-progress flag via a
2480
+ // dragenter/dragleave depth counter (nested elements each fire enter/leave)
2481
+ // so a transparent capture overlay — mirroring the SingleScreenCreationOverlay
2482
+ // mount pattern above — mounts ONLY while an OS file drag is actually over
2483
+ // this wrapper, guaranteeing the drop lands on our own handler instead of
2484
+ // being swallowed by the iframe.
2485
+ const [nativeFileDragActive, setNativeFileDragActive] = useState(false);
2486
+ const nativeFileDragDepthRef = useRef(0);
2487
+
2488
+ const resetNativeFileDragState = useCallback(() => {
2489
+ nativeFileDragDepthRef.current = 0;
2490
+ setNativeFileDragActive(false);
2491
+ }, []);
2492
+
2493
+ const handleWrapperDragEnter = useCallback(
2494
+ (e: React.DragEvent<HTMLDivElement>) => {
2495
+ if (!isOsFileDragEvent(e)) return;
2496
+ e.preventDefault();
2497
+ nativeFileDragDepthRef.current += 1;
2498
+ setNativeFileDragActive(true);
2499
+ },
2500
+ [],
2501
+ );
2502
+
2503
+ const handleWrapperDragOver = useCallback(
2504
+ (e: React.DragEvent<HTMLDivElement>) => {
2505
+ if (!isOsFileDragEvent(e)) return;
2506
+ e.preventDefault();
2507
+ e.dataTransfer.dropEffect = "copy";
2508
+ },
2509
+ [],
2510
+ );
2511
+
2512
+ const handleWrapperDragLeave = useCallback(
2513
+ (e: React.DragEvent<HTMLDivElement>) => {
2514
+ if (!isOsFileDragEvent(e)) return;
2515
+ nativeFileDragDepthRef.current = Math.max(
2516
+ 0,
2517
+ nativeFileDragDepthRef.current - 1,
2518
+ );
2519
+ if (nativeFileDragDepthRef.current === 0) {
2520
+ setNativeFileDragActive(false);
2521
+ }
2522
+ },
2523
+ [],
2524
+ );
2525
+
2526
+ const handleWrapperDrop = useCallback(
2527
+ (e: React.DragEvent<HTMLDivElement>) => {
2528
+ if (!isOsFileDragEvent(e)) return;
2529
+ e.preventDefault();
2530
+ const files = Array.from(e.dataTransfer.files ?? []);
2531
+ resetNativeFileDragState();
2532
+ if (files.length === 0 || !onDropFiles) return;
2533
+ const iframe = iframeRef.current;
2534
+ const rect = iframe?.getBoundingClientRect();
2535
+ const screenContentPoint =
2536
+ iframe && rect
2537
+ ? getScreenContentPointFromClient(e.clientX, e.clientY, rect, {
2538
+ width: iframe.clientWidth,
2539
+ height: iframe.clientHeight,
2540
+ })
2541
+ : { x: e.clientX, y: e.clientY };
2542
+ onDropFiles(files, { screenContentPoint, screenId });
2543
+ },
2544
+ [onDropFiles, resetNativeFileDragState, screenId],
2545
+ );
2546
+
2547
+ useEffect(() => resetNativeFileDragState, [resetNativeFileDragState]);
2548
+
2016
2549
  // Wrap the iframe in a positioned container so DrawOverlay /
2017
2550
  // CanvasCommentPins can absolutely-position themselves on top of the
2018
2551
  // iframe. The pin component anchors to `.design-canvas-iframe-wrapper`
@@ -2023,6 +2556,10 @@ export function DesignCanvas({
2023
2556
  const iframeElement = (
2024
2557
  <div
2025
2558
  className="design-canvas-iframe-wrapper relative inline-block ring-1 ring-border/60 shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.45)]"
2559
+ onDragEnter={handleWrapperDragEnter}
2560
+ onDragOver={handleWrapperDragOver}
2561
+ onDragLeave={handleWrapperDragLeave}
2562
+ onDrop={handleWrapperDrop}
2026
2563
  style={{
2027
2564
  width: embeddedFrame
2028
2565
  ? embeddedFrameFluid
@@ -2036,6 +2573,26 @@ export function DesignCanvas({
2036
2573
  : deviceFrame === "none"
2037
2574
  ? "100%"
2038
2575
  : (iframeHeight ?? undefined),
2576
+ // BP-DEEP item 2: when a breakpoint chip constrains the viewport
2577
+ // (previewWidthPx) the wrapper is NARROWER than the canvas, so there
2578
+ // is no horizontal overflow for the scroll-centering effect above to
2579
+ // act on — without a layout-level center the frame pins to the
2580
+ // canvas's left edge, which reads as broken next to the
2581
+ // device-preview control's flex-centered framed modes. Block + auto
2582
+ // margins center it in the zoom layer's LAYOUT space, which is safe
2583
+ // for the T-zoom-anchor invariant: the margin inset is constant in
2584
+ // layer-local coordinates (transform scale never changes layout), so
2585
+ // the cursor-anchored zoom math — which only assumes the zoom
2586
+ // layer's own top-left corner stays a fixed point in scroll-content
2587
+ // space — is untouched. Base editing (previewWidthPx unset) keeps
2588
+ // the original inline-block full-width layout unchanged.
2589
+ ...(previewWidthPx != null && !embeddedFrame
2590
+ ? {
2591
+ display: "block",
2592
+ marginLeft: "auto",
2593
+ marginRight: "auto",
2594
+ }
2595
+ : null),
2039
2596
  }}
2040
2597
  >
2041
2598
  <iframe
@@ -2098,17 +2655,72 @@ export function DesignCanvas({
2098
2655
  onCreatePrimitive={onCreatePrimitive}
2099
2656
  />
2100
2657
  ) : null}
2101
- {waitingForEditableExternalSnapshot ? (
2102
- <div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center bg-background/85 px-4 text-center text-sm text-muted-foreground">
2103
- <div className="max-w-[28rem] rounded-md border bg-card px-4 py-3 shadow-sm">
2658
+ {/* OS file drag-over capture overlay — sits over the iframe, NOT
2659
+ inside it, mirroring the SingleScreenCreationOverlay mount pattern
2660
+ just above. Only mounts while a native OS file drag is actually in
2661
+ progress over this wrapper (see handleWrapperDragEnter/Leave), so it
2662
+ never changes existing pointer/click behavior otherwise. Skipped
2663
+ while a creation tool is active — the two capture surfaces would
2664
+ otherwise compete for the same pointer gestures, and an in-app
2665
+ creation-tool drag is never simultaneously an OS file drag. */}
2666
+ {nativeFileDragActive && !activeCreationTool ? (
2667
+ <div
2668
+ data-design-canvas-file-drop-overlay
2669
+ className="pointer-events-none absolute inset-0 z-30 flex items-center justify-center rounded-[inherit] border-2 border-dashed border-[var(--design-editor-accent-color)] bg-[var(--design-editor-accent-color)]/5"
2670
+ >
2671
+ <div className="rounded-md border bg-background/95 px-3 py-2 text-xs font-medium text-foreground shadow-sm">
2104
2672
  {
2105
- externalSnapshotState?.status === "error"
2106
- ? "Waiting for localhost bridge snapshot..." /* i18n-ignore local dev bridge status */
2107
- : "Preparing editable preview..." /* i18n-ignore local dev snapshot status */
2673
+ "Drop to add to this screen" /* i18n-ignore transient OS-drag overlay, mirrors other short drop-hint literals in this file */
2108
2674
  }
2109
2675
  </div>
2110
2676
  </div>
2111
2677
  ) : null}
2678
+ {waitingForEditableExternalSnapshot ? (
2679
+ <div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center bg-background/85 px-4 text-center text-sm text-muted-foreground">
2680
+ {externalSnapshotState?.status === "error" ? (
2681
+ // A real offline dev server previously looked identical to the
2682
+ // ordinary "still loading" state and retried forever on a fixed
2683
+ // 1.5s timer — see getSnapshotRetryDelayMs for the backoff that
2684
+ // now paces the auto-retry loop. Surface the actual failure here
2685
+ // (network error / non-2xx / bridge error message) plus a manual
2686
+ // retry action so the user isn't staring at an unexplained
2687
+ // eternal spinner.
2688
+ <div className="pointer-events-auto flex max-w-[28rem] flex-col items-center gap-2 rounded-md border bg-card px-4 py-3 shadow-sm">
2689
+ <div className="flex items-center gap-1.5 font-medium text-foreground">
2690
+ <IconPlugConnectedX className="size-4 shrink-0 text-destructive" />
2691
+ {
2692
+ "Local dev server unreachable" /* i18n-ignore local dev bridge offline title */
2693
+ }
2694
+ </div>
2695
+ <div className="text-xs text-muted-foreground">
2696
+ {
2697
+ "Is it still running?" /* i18n-ignore local dev bridge offline subtitle */
2698
+ }
2699
+ </div>
2700
+ {externalSnapshotState.message ? (
2701
+ <div className="w-full truncate rounded bg-muted px-2 py-1 font-mono text-[11px] text-muted-foreground">
2702
+ {externalSnapshotState.message}
2703
+ </div>
2704
+ ) : null}
2705
+ <Button
2706
+ type="button"
2707
+ variant="outline"
2708
+ size="sm"
2709
+ onClick={handleManualSnapshotRetry}
2710
+ >
2711
+ <IconRefresh className="size-3.5" />
2712
+ {"Retry" /* i18n-ignore local dev bridge retry button */}
2713
+ </Button>
2714
+ </div>
2715
+ ) : (
2716
+ <div className="max-w-[28rem] rounded-md border bg-card px-4 py-3 shadow-sm">
2717
+ {
2718
+ "Preparing editable preview..." /* i18n-ignore local dev snapshot status */
2719
+ }
2720
+ </div>
2721
+ )}
2722
+ </div>
2723
+ ) : null}
2112
2724
  {/* Draw-to-prompt overlay — sits over the iframe, NOT inside it. */}
2113
2725
  <SharedDrawOverlay
2114
2726
  visible={!!drawMode}
@@ -2225,21 +2837,57 @@ export function DesignCanvas({
2225
2837
  tabIndex={-1}
2226
2838
  onPointerEnter={focusScrollSurface}
2227
2839
  onMouseEnter={focusScrollSurface}
2840
+ onMouseDown={handleScrollSurfaceMouseDown}
2228
2841
  className="relative flex-1 h-full overflow-auto"
2842
+ style={{ cursor: panCursor || undefined }}
2229
2843
  >
2230
2844
  {/* Canvas area. "none" mode fills the canvas (responsive preview);
2231
2845
  framed modes are centered inside the canvas with zoom applied. */}
2232
2846
  {deviceFrame === "none" ? (
2233
- <div className="relative flex h-full w-full items-center justify-center">
2234
- <div
2235
- className="h-full w-full"
2236
- style={{
2237
- transform: `scale(${zoom / 100})`,
2238
- transformOrigin: "center center",
2239
- }}
2240
- >
2241
- {wrappedContent}
2242
- </div>
2847
+ <div
2848
+ ref={zoomLayerRef}
2849
+ className="relative h-full w-full"
2850
+ style={{
2851
+ // T-zoom-anchor: transform-origin MUST be top-left here (not
2852
+ // center-center) to match usePinchZoom's documented contract
2853
+ // ("Assumes the scaled content uses transform-origin: top left
2854
+ // ... Disable for layouts with transform-origin: center center")
2855
+ // and the pinch-zoom-wheel bridge-forwarded handler above, both of
2856
+ // which compute the cursor-anchored scroll delta assuming the
2857
+ // scaled box's own top-left corner is a fixed point in
2858
+ // scroll-content space. Centering this box via flexbox instead
2859
+ // (the previous approach) re-centers the *painted* box around the
2860
+ // container's center on every zoom change, which moves that
2861
+ // "fixed" point every time the box's rendered size changes —
2862
+ // exactly the invariant the cursor-anchor math depends on, so
2863
+ // Cmd/Ctrl+wheel or pinch zoom would drift away from the cursor.
2864
+ // Initial centering (and re-centering on a content/screen swap)
2865
+ // is instead handled by imperatively setting scrollLeft/scrollTop
2866
+ // once — see the effect keyed on [deviceFrame, contentKey] above
2867
+ // usePinchZoom — so the layout itself stays simple and
2868
+ // top-left-anchored while still looking centered at rest.
2869
+ //
2870
+ // BP-DEEP v2 item 5 — zoom-out must anchor CENTER: below 100%
2871
+ // the painted layer is SMALLER than the container, so there is
2872
+ // no scrollable overflow and the old transform shrank the canvas
2873
+ // toward the container's top-left corner. The translate() below
2874
+ // (only nonzero when zoom < 100) re-centers the painted layer in
2875
+ // both axes. This cannot break the cursor-anchor math above:
2876
+ // translate percentages resolve against the layer's LAYOUT box
2877
+ // (constant, zoom-independent), and in the sub-100% regime where
2878
+ // the offset is nonzero the container has no overflow — the
2879
+ // anchor math's scroll deltas clamp to 0 regardless — while at
2880
+ // >= 100% the offset is exactly 0 and the original top-left
2881
+ // contract holds verbatim. The offset is also continuous at
2882
+ // 100% (0), so crossing the boundary mid-gesture cannot jump.
2883
+ transform:
2884
+ zoom < 100
2885
+ ? `translate(${(100 - zoom) / 2}%, ${(100 - zoom) / 2}%) scale(${zoom / 100})`
2886
+ : `scale(${zoom / 100})`,
2887
+ transformOrigin: "top left",
2888
+ }}
2889
+ >
2890
+ {wrappedContent}
2243
2891
  </div>
2244
2892
  ) : (
2245
2893
  <div className="relative flex items-center justify-center min-h-full">
@@ -2256,20 +2904,25 @@ export function DesignCanvas({
2256
2904
 
2257
2905
  {/* Canvas comment pins — anchored to the iframe wrapper. The pins
2258
2906
  themselves render via fixed positioning, so we mount them outside
2259
- the zoom-transformed container to keep coordinates stable. */}
2260
- <CanvasCommentPins
2261
- active={!!pinMode}
2262
- submitMode={drawMode ? "queue" : "direct"}
2263
- onPinsChange={setAnnotationPins}
2264
- submitQueuedSignal={pinSubmitSignal}
2265
- clickPlaneUnderToolbar={!!drawMode}
2266
- onClose={() => onExitPinMode?.()}
2267
- canvasSelector=".design-canvas-iframe-wrapper"
2268
- contextId={commentContextId || designId || "design"}
2269
- contextLabel={
2270
- commentContextLabel || designTitle || commentContextId || designId
2271
- }
2272
- />
2907
+ the zoom-transformed container to keep coordinates stable.
2908
+ Suppressed entirely when commentPinsHidden (Figma-style Shift+C
2909
+ "hide comments" toggle) is set — existing pins disappear and pin
2910
+ drop-mode has nothing to render into until it's toggled back on. */}
2911
+ {!commentPinsHidden && (
2912
+ <CanvasCommentPins
2913
+ active={!!pinMode}
2914
+ submitMode={drawMode ? "queue" : "direct"}
2915
+ onPinsChange={setAnnotationPins}
2916
+ submitQueuedSignal={pinSubmitSignal}
2917
+ clickPlaneUnderToolbar={!!drawMode}
2918
+ onClose={() => onExitPinMode?.()}
2919
+ canvasSelector=".design-canvas-iframe-wrapper"
2920
+ contextId={commentContextId || designId || "design"}
2921
+ contextLabel={
2922
+ commentContextLabel || designTitle || commentContextId || designId
2923
+ }
2924
+ />
2925
+ )}
2273
2926
  </div>
2274
2927
  );
2275
2928
  }
@@ -2293,6 +2946,8 @@ const CREATION_DEFAULT_SIZE: Record<
2293
2946
  rectangle: { width: 160, height: 100 },
2294
2947
  ellipse: { width: 120, height: 120 },
2295
2948
  text: { width: 160, height: 32 },
2949
+ // Figma's frame-tool click default is a 100x100 frame.
2950
+ frame: { width: 100, height: 100 },
2296
2951
  };
2297
2952
 
2298
2953
  /** Default line/arrow length (screen-content px) for a click (no drag). */