@agent-native/core 0.92.10 → 0.92.12

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 (354) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +214 -17
  5. package/corpus/core/src/agent/run-manager.ts +72 -14
  6. package/corpus/core/src/agent/run-store.ts +152 -27
  7. package/corpus/core/src/cli/design-connect.ts +433 -151
  8. package/corpus/core/src/cli/skills.ts +14 -0
  9. package/corpus/core/src/client/AssistantChat.tsx +25 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
  11. package/corpus/core/src/client/agent-chat-adapter.ts +56 -12
  12. package/corpus/core/src/client/agent-chat.ts +165 -1
  13. package/corpus/core/src/client/chat/index.ts +9 -1
  14. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  15. package/corpus/core/src/client/index.ts +5 -0
  16. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  17. package/corpus/core/src/collab/awareness-store.ts +23 -12
  18. package/corpus/core/src/collab/client.ts +16 -5
  19. package/corpus/core/src/collab/index.ts +4 -0
  20. package/corpus/core/src/collab/recent-edits.ts +65 -2
  21. package/corpus/core/src/index.browser.ts +1 -0
  22. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  23. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  24. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  25. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  26. package/corpus/templates/clips/AGENTS.md +4 -0
  27. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  28. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  29. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  30. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  31. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  32. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +75 -9
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  45. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  46. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  47. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  48. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  57. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  58. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  59. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  60. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  61. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  62. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  63. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  64. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  65. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  66. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  67. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  68. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  69. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  70. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  71. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  72. package/corpus/templates/content/AGENTS.md +7 -6
  73. package/corpus/templates/content/actions/_builder-cms-read-client.ts +82 -7
  74. package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
  75. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  76. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  77. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +4 -7
  78. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  79. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  80. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  81. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  82. package/corpus/templates/content/actions/pull-document.ts +5 -7
  83. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  84. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  85. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  86. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  87. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  88. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  89. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  90. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  91. package/corpus/templates/content/app/i18n-data.ts +18 -0
  92. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  94. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  95. package/corpus/templates/content/shared/api.ts +1 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  100. package/corpus/templates/design/AGENTS.md +45 -10
  101. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  102. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  103. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  104. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  105. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  106. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  107. package/corpus/templates/design/actions/generate-design.ts +79 -12
  108. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  109. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  110. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  111. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  112. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  113. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  114. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  115. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  116. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  117. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  118. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  119. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  120. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  121. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  122. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  123. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  124. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  125. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  126. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  127. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  128. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  129. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  130. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  131. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  132. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  133. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  134. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  135. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  136. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  137. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  138. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  139. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  140. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  141. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  142. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  143. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  144. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  145. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  146. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  147. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  148. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  149. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  150. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  151. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  152. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  153. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  154. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  155. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  156. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  157. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  158. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  159. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  160. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  161. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  162. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  163. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  164. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  165. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  166. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  167. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  168. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  169. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  170. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  171. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  172. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  173. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  174. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  175. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  176. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  177. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  178. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  179. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  180. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  181. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  182. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  183. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  184. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  185. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  186. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  187. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  188. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  189. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  190. package/corpus/templates/design/app/components/design/types.ts +33 -0
  191. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  192. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  193. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  194. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  195. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  196. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  197. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  198. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  199. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  200. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  201. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  202. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  203. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  204. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  205. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  206. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  207. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  208. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  209. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  210. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  211. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  212. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  213. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  249. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  250. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  251. package/corpus/templates/design/e2e/helpers.ts +27 -6
  252. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  253. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  254. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  255. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  256. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  257. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  258. package/corpus/templates/design/server/plugins/db.ts +41 -0
  259. package/corpus/templates/design/server/source-workspace.ts +64 -0
  260. package/corpus/templates/design/shared/board-file.ts +13 -2
  261. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  262. package/corpus/templates/design/shared/code-layer.ts +122 -8
  263. package/corpus/templates/design/shared/component-library.ts +161 -0
  264. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  265. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  266. package/corpus/templates/design/shared/figma-url.ts +149 -0
  267. package/corpus/templates/design/shared/pen-path.ts +16 -7
  268. package/corpus/templates/design/shared/source-mode.ts +1 -0
  269. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  270. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  271. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  272. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  273. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  274. package/dist/agent/production-agent.d.ts +82 -7
  275. package/dist/agent/production-agent.d.ts.map +1 -1
  276. package/dist/agent/production-agent.js +180 -17
  277. package/dist/agent/production-agent.js.map +1 -1
  278. package/dist/agent/run-manager.d.ts.map +1 -1
  279. package/dist/agent/run-manager.js +63 -14
  280. package/dist/agent/run-manager.js.map +1 -1
  281. package/dist/agent/run-store.d.ts +46 -0
  282. package/dist/agent/run-store.d.ts.map +1 -1
  283. package/dist/agent/run-store.js +129 -22
  284. package/dist/agent/run-store.js.map +1 -1
  285. package/dist/cli/design-connect.d.ts +6 -0
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +336 -139
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/skills.d.ts.map +1 -1
  290. package/dist/cli/skills.js +14 -0
  291. package/dist/cli/skills.js.map +1 -1
  292. package/dist/client/AssistantChat.d.ts +2 -0
  293. package/dist/client/AssistantChat.d.ts.map +1 -1
  294. package/dist/client/AssistantChat.js +19 -3
  295. package/dist/client/AssistantChat.js.map +1 -1
  296. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  297. package/dist/client/MultiTabAssistantChat.js +38 -18
  298. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  299. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  300. package/dist/client/agent-chat-adapter.js +48 -12
  301. package/dist/client/agent-chat-adapter.js.map +1 -1
  302. package/dist/client/agent-chat.d.ts +60 -0
  303. package/dist/client/agent-chat.d.ts.map +1 -1
  304. package/dist/client/agent-chat.js +122 -1
  305. package/dist/client/agent-chat.js.map +1 -1
  306. package/dist/client/chat/index.d.ts +1 -1
  307. package/dist/client/chat/index.d.ts.map +1 -1
  308. package/dist/client/chat/index.js +1 -1
  309. package/dist/client/chat/index.js.map +1 -1
  310. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  311. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  312. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  313. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  314. package/dist/client/index.d.ts +1 -1
  315. package/dist/client/index.d.ts.map +1 -1
  316. package/dist/client/index.js +1 -1
  317. package/dist/client/index.js.map +1 -1
  318. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  319. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  320. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  321. package/dist/collab/awareness-store.d.ts +5 -0
  322. package/dist/collab/awareness-store.d.ts.map +1 -1
  323. package/dist/collab/awareness-store.js +19 -12
  324. package/dist/collab/awareness-store.js.map +1 -1
  325. package/dist/collab/awareness.d.ts +2 -2
  326. package/dist/collab/awareness.d.ts.map +1 -1
  327. package/dist/collab/client.d.ts.map +1 -1
  328. package/dist/collab/client.js +13 -5
  329. package/dist/collab/client.js.map +1 -1
  330. package/dist/collab/index.d.ts +1 -0
  331. package/dist/collab/index.d.ts.map +1 -1
  332. package/dist/collab/index.js +1 -0
  333. package/dist/collab/index.js.map +1 -1
  334. package/dist/collab/recent-edits.d.ts +3 -1
  335. package/dist/collab/recent-edits.d.ts.map +1 -1
  336. package/dist/collab/recent-edits.js +57 -2
  337. package/dist/collab/recent-edits.js.map +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/index.browser.d.ts +1 -1
  340. package/dist/index.browser.d.ts.map +1 -1
  341. package/dist/index.browser.js +1 -1
  342. package/dist/index.browser.js.map +1 -1
  343. package/dist/observability/routes.d.ts +5 -5
  344. package/dist/progress/routes.d.ts +1 -1
  345. package/dist/resources/handlers.d.ts +3 -3
  346. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  347. package/dist/server/agent-chat-plugin.js +87 -10
  348. package/dist/server/agent-chat-plugin.js.map +1 -1
  349. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  350. package/dist/server/builder-design-systems.d.ts +15 -0
  351. package/dist/server/builder-design-systems.d.ts.map +1 -1
  352. package/dist/server/builder-design-systems.js +9 -4
  353. package/dist/server/builder-design-systems.js.map +1 -1
  354. package/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import type { FrameGeometry } from "./types";
2
+
1
3
  export function hasBoardSurfaceContent(html: string | undefined) {
2
4
  if (!html) return false;
3
5
  const bodyMatch = html.match(/<body\b[^>]*>([\s\S]*?)<\/body>/i);
@@ -53,6 +55,108 @@ function getCssPixelValue(style: string, name: string) {
53
55
  return Number.isFinite(parsed) ? parsed : null;
54
56
  }
55
57
 
58
+ /**
59
+ * Returns the canvas-space bounds occupied by top-level board nodes.
60
+ *
61
+ * Board coordinates are persisted directly in each root node's absolute
62
+ * `left`/`top`. Keeping this parser string-only makes it usable during SSR and
63
+ * in the jsdom-less unit suite, while restricting the scan to root nodes keeps
64
+ * nested children from being counted twice with parent-relative coordinates.
65
+ */
66
+ export function getBoardSurfaceContentBounds(
67
+ html: string | undefined,
68
+ ): FrameGeometry | null {
69
+ if (!html) return null;
70
+
71
+ const stack: Array<{
72
+ tagName: string;
73
+ nodeId: string;
74
+ offsetX: number;
75
+ offsetY: number;
76
+ }> = [];
77
+ let minX = Number.POSITIVE_INFINITY;
78
+ let minY = Number.POSITIVE_INFINITY;
79
+ let maxX = Number.NEGATIVE_INFINITY;
80
+ let maxY = Number.NEGATIVE_INFINITY;
81
+
82
+ for (const match of html.matchAll(HTML_TAG_RE)) {
83
+ const token = match[0];
84
+ const tagName = match[1]?.toLowerCase();
85
+ if (!tagName) continue;
86
+
87
+ if (token.startsWith("</")) {
88
+ const index = findLastHtmlStackTagIndex(stack, tagName);
89
+ if (index >= 0) stack.splice(index);
90
+ continue;
91
+ }
92
+
93
+ const nodeId = getHtmlAttributeValue(token, "data-agent-native-node-id");
94
+ const style = getHtmlAttributeValue(token, "style");
95
+ const left = getCssPixelValue(style, "left") ?? 0;
96
+ const top = getCssPixelValue(style, "top") ?? 0;
97
+ const parentOffsetX = stack.reduce(
98
+ (total, entry) => total + entry.offsetX,
99
+ 0,
100
+ );
101
+ const parentOffsetY = stack.reduce(
102
+ (total, entry) => total + entry.offsetY,
103
+ 0,
104
+ );
105
+ const isDocumentRootTag =
106
+ tagName === "html" ||
107
+ tagName === "body" ||
108
+ tagName === "style" ||
109
+ tagName === "script";
110
+ if (nodeId && !isDocumentRootTag && !isAccidentalBoardBackdropTag(token)) {
111
+ const primitiveKind = getHtmlAttributeValue(
112
+ token,
113
+ "data-an-primitive",
114
+ ).toLowerCase();
115
+ // Auto-sized text has no persisted width/height. A one-pixel extent is
116
+ // not enough when it sits near a render-window edge. Reserve a modest
117
+ // intrinsic text box; the camera viewport remains the final authority
118
+ // for unusually long content.
119
+ const fallbackWidth = primitiveKind === "text" ? 256 : 1;
120
+ const fallbackHeight = primitiveKind === "text" ? 64 : 1;
121
+ const width = Math.max(
122
+ 1,
123
+ getCssPixelValue(style, "width") ?? fallbackWidth,
124
+ );
125
+ const height = Math.max(
126
+ 1,
127
+ getCssPixelValue(style, "height") ?? fallbackHeight,
128
+ );
129
+ const absoluteLeft = parentOffsetX + left;
130
+ const absoluteTop = parentOffsetY + top;
131
+ minX = Math.min(minX, absoluteLeft);
132
+ minY = Math.min(minY, absoluteTop);
133
+ maxX = Math.max(maxX, absoluteLeft + width);
134
+ maxY = Math.max(maxY, absoluteTop + height);
135
+ }
136
+
137
+ const selfClosing = token.endsWith("/>") || HTML_VOID_TAGS.has(tagName);
138
+ if (!selfClosing) {
139
+ stack.push({ tagName, nodeId, offsetX: left, offsetY: top });
140
+ }
141
+ }
142
+
143
+ if (
144
+ !Number.isFinite(minX) ||
145
+ !Number.isFinite(minY) ||
146
+ !Number.isFinite(maxX) ||
147
+ !Number.isFinite(maxY)
148
+ ) {
149
+ return null;
150
+ }
151
+
152
+ return {
153
+ x: minX,
154
+ y: minY,
155
+ width: Math.max(1, maxX - minX),
156
+ height: Math.max(1, maxY - minY),
157
+ };
158
+ }
159
+
56
160
  function parseCssColor(value: string) {
57
161
  const trimmed = value.trim().toLowerCase();
58
162
  if (!trimmed || trimmed === "transparent") return null;
@@ -167,6 +271,59 @@ export function getBoardSurfaceRenderContent(html: string) {
167
271
  return `${BOARD_SURFACE_RENDER_STYLE}${renderHtml}`;
168
272
  }
169
273
 
274
+ function stripExecutableStaticPreviewContent(html: string) {
275
+ return html
276
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gi, "") // i18n-ignore static-preview sanitizer regex, not visible UI copy
277
+ .replace(/<script\b[^>]*\/?\s*>/gi, "")
278
+ .replace(/<(iframe|object|audio|video)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, "") // i18n-ignore static-preview sanitizer regex, not visible UI copy
279
+ .replace(/<(?:iframe|object|embed|audio|video|source)\b[^>]*\/?\s*>/gi, "")
280
+ .replace(/<(?:link|meta|base)\b[^>]*>/gi, "")
281
+ .replace(/@import\s+(?:url\([^)]*\)|["'][^"']*["'])\s*[^;]*;/gi, "")
282
+ .replace(/url\(\s*(?:"[^"]*"|'[^']*'|[^)]*)\s*\)/gi, "none")
283
+ .replace(/<(?:img|image|use)\b[^>]*>/gi, (tag) =>
284
+ tag.replace(
285
+ /\s+(?:src|srcset|href|xlink:href)\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi,
286
+ "",
287
+ ),
288
+ )
289
+ .replace(/\s+on[a-z][\w:-]*\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
290
+ }
291
+
292
+ /**
293
+ * Produces the inert, compressed document painted behind the live board
294
+ * window at very low zoom. The document is also rendered in an iframe with an
295
+ * empty sandbox, but removing executable markup here is defense in depth and
296
+ * guarantees Alpine/React never starts a duplicate runtime.
297
+ *
298
+ * Root offsets use the same `translate` seam as the interactive finite board
299
+ * iframe. Scaling the body into a small browser-safe viewport makes the whole
300
+ * logical board paintable without allocating or scrolling a 131k iframe.
301
+ */
302
+ export function getBoardSurfaceStaticPreviewContent(args: {
303
+ html: string;
304
+ logicalGeometry: FrameGeometry;
305
+ viewport: { width: number; height: number };
306
+ }) {
307
+ const renderHtml = stripExecutableStaticPreviewContent(
308
+ getBoardSurfaceRenderContent(args.html),
309
+ );
310
+ const width = Math.max(1, args.logicalGeometry.width);
311
+ const height = Math.max(1, args.logicalGeometry.height);
312
+ const viewportWidth = Math.max(1, args.viewport.width);
313
+ const viewportHeight = Math.max(1, args.viewport.height);
314
+ const scale = Math.min(viewportWidth / width, viewportHeight / height);
315
+ const offsetX = -args.logicalGeometry.x;
316
+ const offsetY = -args.logicalGeometry.y;
317
+ const style = `<style data-agent-native-board-static-preview>*,*::before,*::after{animation:none!important;animation-delay:0s!important;transition:none!important;caret-color:transparent!important;}html{width:${viewportWidth}px!important;height:${viewportHeight}px!important;overflow:hidden!important;}body{margin:0!important;width:${width}px!important;height:${height}px!important;overflow:visible!important;transform:scale(${scale})!important;transform-origin:0 0!important;}body>[data-agent-native-node-id]{translate:${offsetX}px ${offsetY}px!important;}</style>`;
318
+ if (/<\/head>/i.test(renderHtml)) {
319
+ return renderHtml.replace(/<\/head>/i, `${style}</head>`);
320
+ }
321
+ if (/<body\b/i.test(renderHtml)) {
322
+ return renderHtml.replace(/<body\b/i, `${style}<body`);
323
+ }
324
+ return `${style}${renderHtml}`;
325
+ }
326
+
170
327
  /** Simple djb2-xor string hash, used to build cheap cache keys elsewhere in
171
328
  * the multi-screen canvas (board content signatures, primitive-parse cache
172
329
  * keys, etc). */
@@ -18,6 +18,59 @@ export interface ScreenViewportSize {
18
18
  height: number;
19
19
  }
20
20
 
21
+ /** Canonical bottom-to-top screen stack. Persisted frame `z` wins; screens
22
+ * without one retain their source order, which is also the canvas DOM paint
23
+ * order. This is shared by the overview canvas and Layers projection so the
24
+ * two surfaces can never disagree about which screen is above another. */
25
+ export function getCanonicalScreenStack(
26
+ screens: ReadonlyArray<{ id: string }>,
27
+ geometryById: Record<string, Partial<FrameGeometry> | undefined>,
28
+ ): string[] {
29
+ return screens
30
+ .map((screen, index) => ({
31
+ id: screen.id,
32
+ index,
33
+ z: Number.isFinite(geometryById[screen.id]?.z)
34
+ ? geometryById[screen.id]!.z!
35
+ : index,
36
+ }))
37
+ .sort((a, b) => a.z - b.z || a.index - b.index)
38
+ .map(({ id }) => id);
39
+ }
40
+
41
+ /** Reorders a canonical bottom-to-top stack using DOM placement semantics:
42
+ * `before` paints below the target and `after` paints above it. `inside` is
43
+ * not a screen-stack operation (it remains the layer-into-screen drop path). */
44
+ export function reorderCanonicalScreenStack(args: {
45
+ orderedIds: readonly string[];
46
+ draggedIds: readonly string[];
47
+ targetId: string;
48
+ placement: "before" | "after" | "inside";
49
+ }): string[] | null {
50
+ if (args.placement === "inside") return null;
51
+ const orderedIds = Array.from(new Set(args.orderedIds));
52
+ const orderedIdSet = new Set(orderedIds);
53
+ if (!orderedIdSet.has(args.targetId)) return null;
54
+ const draggedIdSet = new Set(
55
+ args.draggedIds.filter(
56
+ (id) => orderedIdSet.has(id) && id !== args.targetId,
57
+ ),
58
+ );
59
+ if (draggedIdSet.size === 0) return null;
60
+ const moving = orderedIds.filter((id) => draggedIdSet.has(id));
61
+ const remaining = orderedIds.filter((id) => !draggedIdSet.has(id));
62
+ const targetIndex = remaining.indexOf(args.targetId);
63
+ if (targetIndex < 0) return null;
64
+ const insertionIndex =
65
+ args.placement === "before" ? targetIndex : targetIndex + 1;
66
+ const next = [
67
+ ...remaining.slice(0, insertionIndex),
68
+ ...moving,
69
+ ...remaining.slice(insertionIndex),
70
+ ];
71
+ return next.every((id, index) => id === orderedIds[index]) ? null : next;
72
+ }
73
+
21
74
  export function getBreakpointFrameGeometry(args: {
22
75
  widthPx: number;
23
76
  naturalAspect: number;
@@ -1,10 +1,23 @@
1
1
  import type { CSSProperties } from "react";
2
2
 
3
3
  import type { FrameGeometry } from "./types";
4
+ import type { Point } from "./types";
4
5
 
5
6
  export const OVERVIEW_FRAME_WIDTH = 320;
6
7
  export const SURFACE_PADDING = 240;
7
8
 
9
+ // Chromium does not reliably paint the far interior of the old
10
+ // 131,072×131,072 board iframe. Keep the logical board that large for
11
+ // persistence and hit testing, but render only a stable, chunk-snapped window
12
+ // around the current design. Four-kilopixel chunks plus two chunks of minimum
13
+ // extent leave ample room for nearby drawing/movement without re-keying the
14
+ // iframe on every small edit.
15
+ export const BOARD_SURFACE_RENDER_CHUNK = 4096;
16
+ export const BOARD_SURFACE_RENDER_PADDING = 2048;
17
+ export const BOARD_SURFACE_RENDER_MIN_SIZE = 8192;
18
+ export const BOARD_SURFACE_RENDER_MAX_SIZE = 24_576;
19
+ export const BOARD_SURFACE_STATIC_PREVIEW_SIZE = 4096;
20
+
8
21
  export const LINEUP_RECENTER_SUPPRESS_MAX_AGE_MS = 10_000;
9
22
 
10
23
  export interface LineupRecenterDuplicateArm {
@@ -60,3 +73,174 @@ export function getBoardSurfaceLayerStyle(args: {
60
73
  zIndex: 0,
61
74
  };
62
75
  }
76
+
77
+ /**
78
+ * The interactive board iframe intentionally stays below Chromium's reliable
79
+ * paint limit. At very low zoom the visible world can be wider than that
80
+ * window, so a script-disabled static replica supplies visual coverage behind
81
+ * it. Keep the fallback off at ordinary zoom where the live window already
82
+ * covers the viewport.
83
+ */
84
+ export function shouldRenderBoardSurfaceStaticPreview(args: {
85
+ zoom: number;
86
+ viewportGeometry?: FrameGeometry | null;
87
+ renderGeometry: FrameGeometry;
88
+ }) {
89
+ if (args.viewportGeometry) {
90
+ return (
91
+ args.viewportGeometry.width > args.renderGeometry.width ||
92
+ args.viewportGeometry.height > args.renderGeometry.height
93
+ );
94
+ }
95
+ // ResizeObserver has not reported yet. The 5% fallback matches a 1229px
96
+ // viewport against the 24,576-world-pixel live cap and avoids one blank
97
+ // first paint at the minimum 2% zoom.
98
+ return args.zoom <= 5;
99
+ }
100
+
101
+ export function getBoardSurfaceStaticPreviewViewport(
102
+ logicalGeometry: FrameGeometry,
103
+ ) {
104
+ const longestAxis = Math.max(
105
+ 1,
106
+ logicalGeometry.width,
107
+ logicalGeometry.height,
108
+ );
109
+ const scale = Math.min(1, BOARD_SURFACE_STATIC_PREVIEW_SIZE / longestAxis);
110
+ return {
111
+ width: Math.max(1, logicalGeometry.width * scale),
112
+ height: Math.max(1, logicalGeometry.height * scale),
113
+ };
114
+ }
115
+
116
+ function geometryExtent(geometry: FrameGeometry) {
117
+ return {
118
+ minX: geometry.x,
119
+ minY: geometry.y,
120
+ maxX: geometry.x + Math.max(1, geometry.width),
121
+ maxY: geometry.y + Math.max(1, geometry.height),
122
+ };
123
+ }
124
+
125
+ function fitRenderAxis(args: {
126
+ desiredMin: number;
127
+ desiredMax: number;
128
+ logicalMin: number;
129
+ logicalMax: number;
130
+ focus: number;
131
+ }) {
132
+ const chunk = BOARD_SURFACE_RENDER_CHUNK;
133
+ const logicalSize = Math.max(1, args.logicalMax - args.logicalMin);
134
+ const maxSize = Math.min(BOARD_SURFACE_RENDER_MAX_SIZE, logicalSize);
135
+ const minSize = Math.min(BOARD_SURFACE_RENDER_MIN_SIZE, maxSize);
136
+ let min =
137
+ Math.floor((args.desiredMin - BOARD_SURFACE_RENDER_PADDING) / chunk) *
138
+ chunk;
139
+ let max =
140
+ Math.ceil((args.desiredMax + BOARD_SURFACE_RENDER_PADDING) / chunk) * chunk;
141
+
142
+ if (max - min < minSize) {
143
+ const center = (min + max) / 2;
144
+ min = Math.floor((center - minSize / 2) / chunk) * chunk;
145
+ max = min + minSize;
146
+ }
147
+
148
+ if (max - min > maxSize) {
149
+ // A single browser iframe cannot safely cover arbitrarily distant board
150
+ // islands. Prefer the active design focus while keeping the render window
151
+ // chunk-aligned; persistence and geometry hit testing still use the full
152
+ // logical board and remain lossless.
153
+ min = Math.floor((args.focus - maxSize / 2) / chunk) * chunk;
154
+ max = min + maxSize;
155
+ }
156
+
157
+ if (min < args.logicalMin) {
158
+ max += args.logicalMin - min;
159
+ min = args.logicalMin;
160
+ }
161
+ if (max > args.logicalMax) {
162
+ min -= max - args.logicalMax;
163
+ max = args.logicalMax;
164
+ }
165
+ min = Math.max(args.logicalMin, min);
166
+ max = Math.min(args.logicalMax, Math.max(min + 1, max));
167
+ return { origin: min, size: max - min };
168
+ }
169
+
170
+ /**
171
+ * Builds the finite iframe window used to paint the otherwise-infinite board.
172
+ * The returned geometry is deliberately separate from the logical board
173
+ * geometry so persisted coordinates and broad hit-testing retain their full
174
+ * range while Chromium only has to paint a browser-safe surface.
175
+ */
176
+ export function getBoardSurfaceRenderGeometry(args: {
177
+ logicalGeometry: FrameGeometry;
178
+ contentBounds?: FrameGeometry | null;
179
+ screenGeometries?: readonly FrameGeometry[];
180
+ focus?: { x: number; y: number };
181
+ }): FrameGeometry {
182
+ const candidates = [
183
+ ...(args.contentBounds ? [args.contentBounds] : []),
184
+ ...(args.screenGeometries ?? []),
185
+ ];
186
+ const logical = geometryExtent(args.logicalGeometry);
187
+ const extents = candidates.map(geometryExtent);
188
+ const desired =
189
+ extents.length > 0
190
+ ? {
191
+ minX: Math.min(...extents.map((extent) => extent.minX)),
192
+ minY: Math.min(...extents.map((extent) => extent.minY)),
193
+ maxX: Math.max(...extents.map((extent) => extent.maxX)),
194
+ maxY: Math.max(...extents.map((extent) => extent.maxY)),
195
+ }
196
+ : {
197
+ minX: 0,
198
+ minY: 0,
199
+ maxX: 1,
200
+ maxY: 1,
201
+ };
202
+ const focus = args.focus ?? {
203
+ x: (desired.minX + desired.maxX) / 2,
204
+ y: (desired.minY + desired.maxY) / 2,
205
+ };
206
+ const xAxis = fitRenderAxis({
207
+ desiredMin: desired.minX,
208
+ desiredMax: desired.maxX,
209
+ logicalMin: logical.minX,
210
+ logicalMax: logical.maxX,
211
+ focus: focus.x,
212
+ });
213
+ const yAxis = fitRenderAxis({
214
+ desiredMin: desired.minY,
215
+ desiredMax: desired.maxY,
216
+ logicalMin: logical.minY,
217
+ logicalMax: logical.maxY,
218
+ focus: focus.y,
219
+ });
220
+ return {
221
+ x: xAxis.origin,
222
+ y: yAxis.origin,
223
+ width: xAxis.size,
224
+ height: yAxis.size,
225
+ };
226
+ }
227
+
228
+ export function boardPointToBoardSurfaceLocalPoint(
229
+ point: Point,
230
+ renderGeometry: FrameGeometry,
231
+ ): Point {
232
+ return {
233
+ x: point.x - renderGeometry.x,
234
+ y: point.y - renderGeometry.y,
235
+ };
236
+ }
237
+
238
+ export function boardSurfaceLocalPointToBoardPoint(
239
+ point: Point,
240
+ renderGeometry: FrameGeometry,
241
+ ): Point {
242
+ return {
243
+ x: renderGeometry.x + point.x,
244
+ y: renderGeometry.y + point.y,
245
+ };
246
+ }