@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
@@ -0,0 +1,544 @@
1
+ /**
2
+ * swap-component-instance — Figma's "Swap instance".
3
+ *
4
+ * DESIGN NOTE — mapping onto this codebase's data model: there is no
5
+ * separate component "definition"/template anywhere here (see
6
+ * `shared/component-model.ts`) — every instance is an independently
7
+ * duplicated copy of HTML matched purely by its
8
+ * `data-agent-native-component="Name"` annotation. "Swap to component B"
9
+ * therefore means: find an existing instance of B somewhere in the design
10
+ * (any screen), copy ITS current markup in as a replacement for the selected
11
+ * instance of A, and carry over whichever `data-agent-native-prop-*`
12
+ * overrides the selected instance had that B's own instances also declare
13
+ * (Figma's "preserve same-named overrides").
14
+ *
15
+ * Scope decisions, deliberately conservative:
16
+ * - Requires at least one existing instance of the target component
17
+ * somewhere in the design (any file) to copy markup from. There is no
18
+ * component library/definition to fall back to — this is a hard
19
+ * requirement, not a data-model gap to route around.
20
+ * - Only `data-agent-native-prop-*` attribute overrides are carried over.
21
+ * The `x-data` Alpine expression is intentionally NOT merged: Alpine state
22
+ * can hold arbitrary JS (methods, nested objects, expressions) and this
23
+ * codebase already treats blind x-data rewrites as unsafe (see
24
+ * `component-section.tsx`'s `canRebuildAlpineDataLosslessly` bail-out) — the
25
+ * swapped-in instance keeps ITS OWN component's default `x-data` as-is.
26
+ * Prop names present on the old instance but not declared by the new
27
+ * component are dropped (`droppedProps`); prop names the new component
28
+ * declares that the old instance didn't override keep the new component's
29
+ * default (`defaultedProps`). Both are reported so the caller/UI can tell
30
+ * the user what changed.
31
+ * - The selected instance's own `data-agent-native-node-id` is preserved on
32
+ * the swapped-in markup (rather than keeping whatever id the copied
33
+ * instance had) so selection, undo, and any other node-id-keyed state keep
34
+ * addressing the same slot on the canvas.
35
+ *
36
+ * Persists through the same deterministic HTML-patch + collab seam as
37
+ * `apply-component-prop-edit` (`writeInlineSourceFile` + `expectedVersionHash`
38
+ * CAS guard) — same undo/collab posture as every other HTML-mutating action.
39
+ *
40
+ * Inline/Alpine designs only; real-app sources fail closed.
41
+ */
42
+
43
+ import { randomUUID } from "node:crypto";
44
+
45
+ import { defineAction } from "@agent-native/core/action";
46
+ import {
47
+ agentEnterDocument,
48
+ agentLeaveDocument,
49
+ agentUpdateSelection,
50
+ } from "@agent-native/core/collab";
51
+ import {
52
+ accessFilter,
53
+ assertAccess,
54
+ resolveAccess,
55
+ } from "@agent-native/core/sharing";
56
+ import { and, eq } from "drizzle-orm";
57
+ import { z } from "zod";
58
+
59
+ import { getDb, schema } from "../server/db/index.js";
60
+ import "../server/db/index.js"; // ensure registerShareableResource runs
61
+ import {
62
+ writeInlineSourceFile,
63
+ type SourceWorkspaceFile,
64
+ } from "../server/source-workspace.js";
65
+ import {
66
+ buildCodeLayerProjection,
67
+ type CodeLayerNode,
68
+ } from "../shared/code-layer.js";
69
+ import type { CodeLayerSource } from "../shared/code-layer.js";
70
+ import { agentSelectionDescriptor } from "../shared/collab-selection.js";
71
+ import {
72
+ componentNameFor,
73
+ componentNodeIdMatches,
74
+ extractProps,
75
+ propNameToDataAttribute,
76
+ } from "../shared/component-model.js";
77
+ import { designSourceTypeFromData } from "../shared/source-mode.js";
78
+ import { sourceContentHash } from "../shared/source-workspace.js";
79
+ import { applyRootAttributeEdit } from "./apply-component-prop-edit.js";
80
+
81
+ // ─── Pure markup helpers ────────────────────────────────────────────────────
82
+
83
+ /**
84
+ * Find the end offset (exclusive) of the opening tag at the start of
85
+ * `markup`, respecting quoted attribute values that may themselves contain
86
+ * `>`. Returns `markup.length` if no unquoted `>` is found. Pure.
87
+ */
88
+ export function findOpenTagEnd(markup: string): number {
89
+ let quote: '"' | "'" | null = null;
90
+ for (let i = 0; i < markup.length; i++) {
91
+ const ch = markup[i];
92
+ if (quote) {
93
+ if (ch === quote) quote = null;
94
+ continue;
95
+ }
96
+ if (ch === '"' || ch === "'") {
97
+ quote = ch;
98
+ continue;
99
+ }
100
+ if (ch === ">") return i + 1;
101
+ }
102
+ return markup.length;
103
+ }
104
+
105
+ /**
106
+ * Set (or replace) a single attribute on a standalone markup string's opening
107
+ * tag — the same splice `applyRootAttributeEdit` performs against a full
108
+ * document, but for an already-extracted outerHTML fragment (e.g. copied from
109
+ * another instance elsewhere in the design). Pure — exported for tests.
110
+ */
111
+ export function setAttributeOnMarkup(
112
+ markup: string,
113
+ attrName: string,
114
+ attrValue: string,
115
+ ): string {
116
+ const openEnd = findOpenTagEnd(markup);
117
+ const result = applyRootAttributeEdit(
118
+ markup,
119
+ { openStart: 0, openEnd },
120
+ attrName,
121
+ attrValue,
122
+ );
123
+ return result.content;
124
+ }
125
+
126
+ export interface SwapOverrideResult {
127
+ markup: string;
128
+ overriddenProps: string[];
129
+ droppedProps: string[];
130
+ defaultedProps: string[];
131
+ }
132
+
133
+ /**
134
+ * Re-key every descendant in markup copied from another component instance.
135
+ * Keeping the copied `data-agent-native-node-id` values would create duplicate
136
+ * stable layer identities, so selection and the aggregate layer-owner map
137
+ * could jump back to the source instance after a swap. The root keeps the
138
+ * selected instance's id later in `mergeComponentSwapOverrides`; descendants
139
+ * receive fresh ids here.
140
+ *
141
+ * `createNodeId` is injectable so the pure behavior stays deterministic in
142
+ * tests while production uses cryptographically unique ids.
143
+ */
144
+ export function reassignCopiedDescendantNodeIds(
145
+ markup: string,
146
+ createNodeId: () => string = () => `an-${randomUUID()}`,
147
+ ): string {
148
+ const projection = buildCodeLayerProjection(markup);
149
+ const rootIds = new Set(projection.rootNodeIds);
150
+ const descendants = projection.nodes
151
+ .filter((node) => !rootIds.has(node.id) && node.source)
152
+ .sort(
153
+ (a, b) =>
154
+ (b.source?.openStart ?? Number.NEGATIVE_INFINITY) -
155
+ (a.source?.openStart ?? Number.NEGATIVE_INFINITY),
156
+ );
157
+
158
+ let content = markup;
159
+ for (const descendant of descendants) {
160
+ content = applyRootAttributeEdit(
161
+ content,
162
+ descendant.source,
163
+ "data-agent-native-node-id",
164
+ createNodeId(),
165
+ ).content;
166
+ }
167
+ return content;
168
+ }
169
+
170
+ /**
171
+ * Apply the selected instance's `data-agent-native-prop-*` overrides onto a
172
+ * copy of the target component's markup, carrying over only prop names the
173
+ * target component ALSO declares, then stamp the selected instance's stable
174
+ * node id onto the result. Pure — exported for tests.
175
+ */
176
+ export function mergeComponentSwapOverrides(
177
+ targetMarkup: string,
178
+ currentProps: Array<{ name: string; value: string }>,
179
+ targetDefaultProps: Array<{ name: string; value: string }>,
180
+ nodeId: string,
181
+ ): SwapOverrideResult {
182
+ const targetNames = new Set(targetDefaultProps.map((p) => p.name));
183
+ const currentNames = new Set(currentProps.map((p) => p.name));
184
+
185
+ const overriddenProps: string[] = [];
186
+ const droppedProps: string[] = [];
187
+ const defaultedProps: string[] = [];
188
+
189
+ let markup = reassignCopiedDescendantNodeIds(targetMarkup);
190
+
191
+ for (const prop of currentProps) {
192
+ if (targetNames.has(prop.name)) {
193
+ markup = setAttributeOnMarkup(
194
+ markup,
195
+ propNameToDataAttribute(prop.name),
196
+ prop.value,
197
+ );
198
+ overriddenProps.push(prop.name);
199
+ } else {
200
+ droppedProps.push(prop.name);
201
+ }
202
+ }
203
+
204
+ for (const prop of targetDefaultProps) {
205
+ if (!currentNames.has(prop.name)) defaultedProps.push(prop.name);
206
+ }
207
+
208
+ markup = setAttributeOnMarkup(markup, "data-agent-native-node-id", nodeId);
209
+
210
+ return { markup, overriddenProps, droppedProps, defaultedProps };
211
+ }
212
+
213
+ // ─── Persistence ────────────────────────────────────────────────────────────
214
+
215
+ async function persistEdit(file: {
216
+ id: string;
217
+ designId: string;
218
+ filename: string;
219
+ content: string;
220
+ expectedVersionHash: string;
221
+ }): Promise<string> {
222
+ await assertAccess("design", file.designId, "editor");
223
+
224
+ agentEnterDocument(file.id);
225
+ try {
226
+ const workspaceFile: SourceWorkspaceFile = {
227
+ id: file.id,
228
+ designId: file.designId,
229
+ filename: file.filename,
230
+ fileType: "html",
231
+ content: file.content,
232
+ createdAt: null,
233
+ updatedAt: null,
234
+ };
235
+
236
+ const result = await writeInlineSourceFile({
237
+ designId: file.designId,
238
+ file: workspaceFile,
239
+ content: file.content,
240
+ expectedVersionHash: file.expectedVersionHash,
241
+ });
242
+
243
+ return result.updatedAt;
244
+ } finally {
245
+ agentLeaveDocument(file.id);
246
+ }
247
+ }
248
+
249
+ // ─── Action ───────────────────────────────────────────────────────────────────
250
+
251
+ export default defineAction({
252
+ description:
253
+ "Swap a component instance for a different component from elsewhere in " +
254
+ "the design (Figma's Swap instance). Replaces the selected instance's " +
255
+ "markup with a copy of another existing instance of targetComponentName, " +
256
+ "carrying over data-agent-native-prop-* overrides whose prop name exists " +
257
+ "on both components. x-data (Alpine state) is not merged — the swapped-in " +
258
+ "instance keeps the target component's own x-data. Requires at least one " +
259
+ "existing instance of targetComponentName somewhere in the design. " +
260
+ "Inline/Alpine designs only.",
261
+ schema: z.object({
262
+ designId: z.string().describe("Design project ID"),
263
+ nodeId: z
264
+ .string()
265
+ .describe("data-agent-native-node-id of the component instance to swap"),
266
+ fileId: z
267
+ .string()
268
+ .optional()
269
+ .describe(
270
+ "Design file id the instance currently lives in; defaults to index.html",
271
+ ),
272
+ targetComponentName: z
273
+ .string()
274
+ .min(1)
275
+ .describe(
276
+ "Name of the component to swap in, from list-design-components",
277
+ ),
278
+ source: z
279
+ .object({
280
+ currentContent: z
281
+ .string()
282
+ .optional()
283
+ .describe("Latest editor HTML snapshot, when available."),
284
+ revision: z
285
+ .string()
286
+ .optional()
287
+ .describe(
288
+ "design_files.updatedAt value the currentContent is based on.",
289
+ ),
290
+ })
291
+ .optional(),
292
+ }),
293
+ run: async ({ designId, nodeId, fileId, targetComponentName, source }) => {
294
+ const access = await resolveAccess("design", designId);
295
+ if (!access) throw new Error("Design not found");
296
+
297
+ const rawData = (access.resource as { data?: unknown }).data;
298
+ const sourceType = designSourceTypeFromData(rawData);
299
+
300
+ if (sourceType !== "inline") {
301
+ return {
302
+ designId,
303
+ nodeId,
304
+ sourceType,
305
+ swapped: false,
306
+ ctaRequired: true,
307
+ ctaMessage:
308
+ "Swap instance requires a dedicated consented, version-guarded " +
309
+ "compiled-source transform for real-app sources. Not yet available.",
310
+ };
311
+ }
312
+
313
+ await assertAccess("design", designId, "editor");
314
+ const db = getDb();
315
+
316
+ const conditions = [
317
+ accessFilter(schema.designs, schema.designShares),
318
+ eq(schema.designFiles.designId, designId),
319
+ fileId
320
+ ? eq(schema.designFiles.id, fileId)
321
+ : eq(schema.designFiles.filename, "index.html"),
322
+ ];
323
+
324
+ const [file] = await db
325
+ .select({
326
+ id: schema.designFiles.id,
327
+ designId: schema.designFiles.designId,
328
+ filename: schema.designFiles.filename,
329
+ content: schema.designFiles.content,
330
+ updatedAt: schema.designFiles.updatedAt,
331
+ })
332
+ .from(schema.designFiles)
333
+ .innerJoin(
334
+ schema.designs,
335
+ eq(schema.designFiles.designId, schema.designs.id),
336
+ )
337
+ .where(and(...conditions))
338
+ .limit(1);
339
+
340
+ if (!file) throw new Error("Design HTML file not found.");
341
+
342
+ if (
343
+ source?.currentContent &&
344
+ source.revision &&
345
+ file.updatedAt &&
346
+ source.revision !== file.updatedAt
347
+ ) {
348
+ return {
349
+ designId,
350
+ nodeId,
351
+ swapped: false,
352
+ conflict: true,
353
+ fileId: file.id,
354
+ filename: file.filename,
355
+ error:
356
+ "This file changed since this swap was prepared. Refresh the editor and try again.",
357
+ };
358
+ }
359
+
360
+ const html =
361
+ typeof source?.currentContent === "string"
362
+ ? source.currentContent
363
+ : (file.content ?? "");
364
+ const baseVersionHash = sourceContentHash(html);
365
+
366
+ const codeLayerSource: CodeLayerSource = {
367
+ kind: "design-file",
368
+ designId: file.designId,
369
+ fileId: file.id,
370
+ filename: file.filename,
371
+ };
372
+
373
+ const projection = buildCodeLayerProjection(html, {
374
+ source: codeLayerSource,
375
+ });
376
+
377
+ const node = projection.nodes.find((n) =>
378
+ componentNodeIdMatches(n, nodeId),
379
+ );
380
+ if (!node) {
381
+ throw new Error(
382
+ `Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
383
+ );
384
+ }
385
+
386
+ const componentName = componentNameFor(node);
387
+ if (!componentName) {
388
+ throw new Error(
389
+ `Node "${nodeId}" is not a component root (no data-agent-native-component attribute) — nothing to swap.`,
390
+ );
391
+ }
392
+
393
+ if (componentName === targetComponentName) {
394
+ return {
395
+ designId,
396
+ nodeId,
397
+ componentName,
398
+ swapped: false,
399
+ note: `Already an instance of "${targetComponentName}".`,
400
+ };
401
+ }
402
+
403
+ if (!node.source) {
404
+ throw new Error(
405
+ `Node "${nodeId}" has no resolvable source span; cannot swap.`,
406
+ );
407
+ }
408
+
409
+ // ── Find a markup source for the target component ──────────────────────
410
+ const allFiles = await db
411
+ .select({
412
+ id: schema.designFiles.id,
413
+ designId: schema.designFiles.designId,
414
+ filename: schema.designFiles.filename,
415
+ content: schema.designFiles.content,
416
+ })
417
+ .from(schema.designFiles)
418
+ .innerJoin(
419
+ schema.designs,
420
+ eq(schema.designFiles.designId, schema.designs.id),
421
+ )
422
+ .where(
423
+ and(
424
+ accessFilter(schema.designs, schema.designShares),
425
+ eq(schema.designFiles.designId, designId),
426
+ eq(schema.designFiles.fileType, "html"),
427
+ ),
428
+ )
429
+ .orderBy(schema.designFiles.createdAt);
430
+
431
+ let targetNode: CodeLayerNode | undefined;
432
+ let targetHtml = "";
433
+
434
+ for (const row of allFiles) {
435
+ const rowHtml = row.id === file.id ? html : (row.content ?? "");
436
+ if (!rowHtml) continue;
437
+ const rowProjection =
438
+ row.id === file.id
439
+ ? projection
440
+ : buildCodeLayerProjection(rowHtml, {
441
+ source: {
442
+ kind: "design-file",
443
+ designId: row.designId,
444
+ fileId: row.id,
445
+ filename: row.filename,
446
+ },
447
+ });
448
+
449
+ const match = rowProjection.nodes.find((n) => {
450
+ if (row.id === file.id && n.id === node.id) return false;
451
+ return componentNameFor(n) === targetComponentName;
452
+ });
453
+
454
+ if (match) {
455
+ targetNode = match;
456
+ targetHtml = rowHtml;
457
+ break;
458
+ }
459
+ }
460
+
461
+ if (!targetNode) {
462
+ throw new Error(
463
+ `No existing instance of component "${targetComponentName}" found ` +
464
+ "anywhere in this design. Swap needs at least one existing " +
465
+ "instance to copy markup from — run list-design-components to see " +
466
+ "what's available.",
467
+ );
468
+ }
469
+ if (!targetNode.source) {
470
+ throw new Error(
471
+ `The matched "${targetComponentName}" instance has no resolvable source span; cannot swap.`,
472
+ );
473
+ }
474
+
475
+ const targetMarkup = targetHtml.slice(
476
+ targetNode.source.start,
477
+ targetNode.source.end,
478
+ );
479
+
480
+ const currentProps = extractProps(node);
481
+ const targetDefaultProps = extractProps(targetNode);
482
+
483
+ const {
484
+ markup: mergedMarkup,
485
+ overriddenProps,
486
+ droppedProps,
487
+ defaultedProps,
488
+ } = mergeComponentSwapOverrides(
489
+ targetMarkup,
490
+ currentProps,
491
+ targetDefaultProps,
492
+ nodeId,
493
+ );
494
+
495
+ const patchedContent =
496
+ html.slice(0, node.source.start) +
497
+ mergedMarkup +
498
+ html.slice(node.source.end);
499
+
500
+ if (patchedContent === html) {
501
+ return {
502
+ designId,
503
+ nodeId,
504
+ componentName,
505
+ swapped: false,
506
+ note: "Swap produced no change.",
507
+ };
508
+ }
509
+
510
+ const updatedAt = await persistEdit({
511
+ id: file.id,
512
+ designId: file.designId,
513
+ filename: file.filename,
514
+ content: patchedContent,
515
+ expectedVersionHash: baseVersionHash,
516
+ });
517
+
518
+ agentUpdateSelection(file.id, {
519
+ selection: agentSelectionDescriptor(
520
+ { nodeId, selector: node.selector },
521
+ "Swapping instance",
522
+ ),
523
+ nodeId,
524
+ editingFile: file.filename,
525
+ designId: file.designId,
526
+ });
527
+
528
+ return {
529
+ designId,
530
+ nodeId,
531
+ fromComponent: componentName,
532
+ toComponent: targetComponentName,
533
+ swapped: true,
534
+ overriddenProps,
535
+ droppedProps,
536
+ defaultedProps,
537
+ fileId: file.id,
538
+ filename: file.filename,
539
+ updatedAt,
540
+ content: patchedContent,
541
+ note: `Swapped "${componentName}" for "${targetComponentName}".`,
542
+ };
543
+ },
544
+ });
@@ -125,75 +125,50 @@ function labelForWidth(widthPx: number): string {
125
125
  return `desktop-${widthPx}`;
126
126
  }
127
127
 
128
- /** Resolve the viewport list from the optional `widths` input, defaulting to desktop + mobile. */
129
- export function resolveViewports(widths?: number[]): ScreenshotViewport[] {
128
+ /**
129
+ * Resolve the viewport list from the optional `widths` input, defaulting to
130
+ * desktop + mobile. `heights`, when provided, is matched index-for-index
131
+ * against `widths` so a caller that already knows the exact content height
132
+ * (e.g. the annotate-to-agent draw pipeline compositing over a specific
133
+ * on-screen rect) gets a screenshot with the same aspect ratio instead of the
134
+ * device-heuristic default — annotation coordinates are recorded in that
135
+ * exact rect's pixel space, so a mismatched aspect ratio would misalign the
136
+ * composited drawing against the screenshot content. A missing/undefined
137
+ * entry at a given index falls back to `heightForWidth` unchanged, so
138
+ * existing callers that only pass `widths` are unaffected.
139
+ */
140
+ export function resolveViewports(
141
+ widths?: number[],
142
+ heights?: number[],
143
+ ): ScreenshotViewport[] {
130
144
  if (!widths || widths.length === 0) return DEFAULT_VIEWPORTS;
131
- return widths.map((widthPx) => ({
145
+ return widths.map((widthPx, index) => ({
132
146
  label: labelForWidth(widthPx),
133
147
  widthPx,
134
- heightPx: heightForWidth(widthPx),
148
+ heightPx: heights?.[index] || heightForWidth(widthPx),
135
149
  }));
136
150
  }
137
151
 
138
152
  // ---------------------------------------------------------------------------
139
153
  // Playwright loading (mirrors packages/core/src/cli/recap.ts's runShot: a
140
154
  // dynamic import + system-Chrome fallback, so a missing browser binary is a
141
- // clean, catchable failure rather than an unhandled module-resolution crash)
155
+ // clean, catchable failure rather than an unhandled module-resolution crash).
156
+ // The actual bootstrap lives in `playwright-runtime.ts` so other server-side
157
+ // Chromium consumers (e.g. the Figma SVG export's scene extractor in
158
+ // `design-to-figma-svg.ts`) share it instead of duplicating it; re-exported
159
+ // here for backward compatibility with this file's existing imports/spec.
142
160
  // ---------------------------------------------------------------------------
143
161
 
144
- type PlaywrightModule = { chromium: import("@playwright/test").BrowserType };
145
-
146
- async function importPlaywright(): Promise<PlaywrightModule> {
147
- try {
148
- // Bare "playwright" — present when @agent-native/core's optional
149
- // dependency resolved (matches how packages/core/src/cli/recap.ts loads
150
- // it). Loaded via a non-literal specifier so bundlers don't try to
151
- // statically resolve/include it (it's optional and can be entirely
152
- // absent, e.g. in a hosted deploy).
153
- const specifier = "playwright";
154
- return (await import(specifier)) as unknown as PlaywrightModule;
155
- } catch {
156
- // "@playwright/test" is a direct devDependency of this template (used by
157
- // its own e2e suite) and re-exports the same chromium/Browser API.
158
- return (await import("@playwright/test")) as unknown as PlaywrightModule;
159
- }
160
- }
161
-
162
- const SYSTEM_CHROME_EXECUTABLES = [
163
- "/usr/bin/google-chrome-stable",
164
- "/usr/bin/google-chrome",
165
- "/usr/bin/chromium-browser",
166
- "/usr/bin/chromium",
167
- ];
168
-
169
- /** Exported for tests — pure classifier for "no Chromium binary available" errors. */
170
- export function isMissingBrowserError(err: unknown): boolean {
171
- const message = err instanceof Error ? err.message : String(err);
172
- return /Executable doesn't exist|playwright install|browser.*not found|chromium.*not found/i.test(
173
- message,
174
- );
175
- }
176
-
177
- async function launchChromium(
178
- chromium: import("@playwright/test").BrowserType,
179
- ): Promise<import("@playwright/test").Browser> {
180
- const launchOptions = { args: ["--no-sandbox"] };
181
- try {
182
- return await chromium.launch(launchOptions);
183
- } catch (err) {
184
- if (!isMissingBrowserError(err)) throw err;
185
- const { existsSync } = await import("node:fs");
186
- for (const executablePath of SYSTEM_CHROME_EXECUTABLES) {
187
- if (!existsSync(executablePath)) continue;
188
- try {
189
- return await chromium.launch({ ...launchOptions, executablePath });
190
- } catch {
191
- // Try the next candidate; the original error is rethrown below.
192
- }
193
- }
194
- throw err;
195
- }
196
- }
162
+ export {
163
+ importPlaywright,
164
+ isMissingBrowserError,
165
+ launchChromium,
166
+ } from "../server/lib/playwright-runtime.js";
167
+ import {
168
+ importPlaywright,
169
+ launchChromium,
170
+ type PlaywrightModule,
171
+ } from "../server/lib/playwright-runtime.js";
197
172
 
198
173
  /** Human-readable, model-actionable message for the "no Chromium available" case. */
199
174
  export function chromiumUnavailableReason(err: unknown): string {
@@ -510,10 +485,20 @@ export default defineAction({
510
485
  "Viewport widths in px to render. Defaults to [1280, 375] (desktop + mobile). " +
511
486
  "Height is derived per width using standard device aspect ratios.",
512
487
  ),
488
+ heights: z
489
+ .array(z.number().int().min(200).max(4096))
490
+ .optional()
491
+ .describe(
492
+ "Exact viewport heights in px, matched index-for-index against `widths`. " +
493
+ "Use when the caller needs the screenshot's aspect ratio to match a " +
494
+ "known on-screen rect exactly (e.g. compositing an overlay on top) " +
495
+ "instead of the device-heuristic default. Omit an index to fall back " +
496
+ "to the standard derived height for that width.",
497
+ ),
513
498
  }),
514
499
  readOnly: true,
515
500
  http: { method: "POST" },
516
- run: async ({ designId, fileId, filename, widths }, ctx) => {
501
+ run: async ({ designId, fileId, filename, widths, heights }, ctx) => {
517
502
  if (!designId && !fileId) {
518
503
  throw new Error("designId or fileId is required.");
519
504
  }
@@ -569,7 +554,7 @@ export default defineAction({
569
554
  return { ok: false, reason: chromiumUnavailableReason(err) };
570
555
  }
571
556
 
572
- const viewports = resolveViewports(widths);
557
+ const viewports = resolveViewports(widths, heights);
573
558
  let browser: import("@playwright/test").Browser | undefined;
574
559
  try {
575
560
  browser = await launchChromium(playwright.chromium);