@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
@@ -38,7 +38,8 @@ import { z } from "zod";
38
38
 
39
39
  import { getDb, schema } from "../server/db/index.js";
40
40
  import {
41
- readLiveSourceFile,
41
+ prepareInlineSourceEdit,
42
+ SourceWorkspaceEditConflictError,
42
43
  writeInlineSourceFile,
43
44
  type SourceWorkspaceFile,
44
45
  } from "../server/source-workspace.js";
@@ -58,7 +59,6 @@ import {
58
59
  type ShaderPresetName,
59
60
  validateDescriptor,
60
61
  } from "../shared/shader-presets.js";
61
- import { sourceContentHash } from "../shared/source-workspace.js";
62
62
 
63
63
  // ─── Schema ──────────────────────────────────────────────────────────────────
64
64
 
@@ -163,14 +163,10 @@ interface ResolvedDesignFile {
163
163
  * editor access. Mirrors `resolveEditableDesignFile` in apply-visual-edit.ts so
164
164
  * the shader-fill write goes through the exact same ownership gate.
165
165
  *
166
- * The editable base is collab-aware: `readLiveSourceFile` returns the live
167
- * Y.Text content when a collab doc exists for this file, else the SQL row —
168
- * never only the raw SQL snapshot. When the caller supplies
169
- * `source.currentContent`, that in-flight editor snapshot is preferred as the
170
- * base instead (same precedence as apply-motion-edit.ts / apply-component-
171
- * prop-edit.ts), and its versionHash is computed directly from that exact
172
- * string via `sourceContentHash` — not from a fresh live re-read, which would
173
- * prove nothing about whether this specific base is still current.
166
+ * `prepareInlineSourceEdit` keeps the caller's working copy (the transform
167
+ * base) separate from the live hash it is allowed to replace (the write CAS).
168
+ * This preserves unsaved local edits based on the matching SQL revision while
169
+ * still rejecting a third, concurrently-edited live value.
174
170
  */
175
171
  async function resolveEditableDesignFile(source: {
176
172
  designId?: string;
@@ -241,36 +237,27 @@ async function resolveEditableDesignFile(source: {
241
237
  "source.revision is required when source.currentContent is provided.",
242
238
  );
243
239
  }
244
- // Legitimate pre-check, distinct from the CAS write-guard below: reject
245
- // early when the caller's own revision stamp doesn't match the file's SQL
246
- // updatedAt, before any transform work happens.
247
- if (
248
- source.currentContent !== undefined &&
249
- source.revision &&
250
- file.updatedAt &&
251
- source.revision !== file.updatedAt
252
- ) {
253
- throw new ShaderFillRevisionConflictError();
254
- }
255
-
256
- let content: string;
257
- let versionHash: string;
258
- if (source.currentContent !== undefined) {
259
- content = source.currentContent;
260
- versionHash = sourceContentHash(content);
261
- } else {
262
- const workspaceFile: SourceWorkspaceFile = {
263
- id: file.id,
264
- designId: file.designId,
265
- filename: file.filename,
266
- fileType: file.fileType,
267
- content: file.content,
268
- createdAt: null,
269
- updatedAt: file.updatedAt,
270
- };
271
- const live = await readLiveSourceFile(workspaceFile);
272
- content = live.content;
273
- versionHash = live.versionHash;
240
+ const workspaceFile: SourceWorkspaceFile = {
241
+ id: file.id,
242
+ designId: file.designId,
243
+ filename: file.filename,
244
+ fileType: file.fileType,
245
+ content: file.content,
246
+ createdAt: null,
247
+ updatedAt: file.updatedAt,
248
+ };
249
+ let prepared: Awaited<ReturnType<typeof prepareInlineSourceEdit>>;
250
+ try {
251
+ prepared = await prepareInlineSourceEdit({
252
+ file: workspaceFile,
253
+ currentContent: source.currentContent,
254
+ revision: source.revision,
255
+ });
256
+ } catch (error) {
257
+ if (error instanceof SourceWorkspaceEditConflictError) {
258
+ throw new ShaderFillRevisionConflictError();
259
+ }
260
+ throw error;
274
261
  }
275
262
 
276
263
  return {
@@ -278,8 +265,8 @@ async function resolveEditableDesignFile(source: {
278
265
  designId: file.designId,
279
266
  filename: file.filename,
280
267
  fileType: file.fileType,
281
- content,
282
- versionHash,
268
+ content: prepared.content,
269
+ versionHash: prepared.expectedVersionHash,
283
270
  codeLayerSource: {
284
271
  kind: "design-file",
285
272
  designId: file.designId,
@@ -0,0 +1,337 @@
1
+ /**
2
+ * detach-component-instance — Figma's "Detach instance" (⌥⌘B).
3
+ *
4
+ * There is no separate component "definition"/template markup anywhere in
5
+ * this codebase (see `shared/component-model.ts`): a component instance is
6
+ * just an ordinary element carrying a `data-agent-native-component="Name"`
7
+ * annotation (plus optional `data-agent-native-prop-*` overrides), and every
8
+ * instance of the same name is an independently-duplicated copy of HTML —
9
+ * `component_index` only stores metadata (props/variants/runtime selectors),
10
+ * never markup. Detaching an instance therefore doesn't need to "inline a
11
+ * template" the way a real component-instantiation system would: the node's
12
+ * current rendered markup already IS the fully expanded content. Detach
13
+ * severs the *component-instance linkage* by stripping the annotation
14
+ * attributes, so `is-component-instance` checks (`isComponentInstance`,
15
+ * `index-components`, `get-component-details`) stop recognizing this node —
16
+ * exactly Figma's "instance becomes a plain layer" semantics — while leaving
17
+ * position, size, layout, classes, text, and Alpine (`x-data`) behavior
18
+ * completely untouched.
19
+ *
20
+ * Persists through the same deterministic HTML-patch + collab seam as
21
+ * `apply-component-prop-edit` / `apply-visual-edit` (`writeInlineSourceFile`
22
+ * with an `expectedVersionHash` CAS guard), so it participates in the
23
+ * editor's undo/collab machinery exactly like every other HTML-mutating
24
+ * action — there is no separate undo/history log in this codebase to hook
25
+ * into (see those actions' docs).
26
+ *
27
+ * Inline/Alpine designs only; real-app sources fail closed (same posture as
28
+ * `apply-component-prop-edit`).
29
+ */
30
+
31
+ import { defineAction } from "@agent-native/core/action";
32
+ import {
33
+ agentEnterDocument,
34
+ agentLeaveDocument,
35
+ agentUpdateSelection,
36
+ } from "@agent-native/core/collab";
37
+ import {
38
+ accessFilter,
39
+ assertAccess,
40
+ resolveAccess,
41
+ } from "@agent-native/core/sharing";
42
+ import { and, eq } from "drizzle-orm";
43
+ import { z } from "zod";
44
+
45
+ import { getDb, schema } from "../server/db/index.js";
46
+ import "../server/db/index.js"; // ensure registerShareableResource runs
47
+ import {
48
+ writeInlineSourceFile,
49
+ type SourceWorkspaceFile,
50
+ } from "../server/source-workspace.js";
51
+ import { buildCodeLayerProjection } from "../shared/code-layer.js";
52
+ import type { CodeLayerSource } from "../shared/code-layer.js";
53
+ import { agentSelectionDescriptor } from "../shared/collab-selection.js";
54
+ import {
55
+ componentNameFor,
56
+ componentNodeIdMatches,
57
+ COMPONENT_NAME_ATTR,
58
+ COMPONENT_PROP_PREFIX,
59
+ } from "../shared/component-model.js";
60
+ import { designSourceTypeFromData } from "../shared/source-mode.js";
61
+ import { sourceContentHash } from "../shared/source-workspace.js";
62
+
63
+ // ─── Pure transform ────────────────────────────────────────────────────────
64
+
65
+ /**
66
+ * Strip the `data-agent-native-component` annotation and every
67
+ * `data-agent-native-prop-*` override attribute from a node's opening tag,
68
+ * using its source span. Pure — no DB / IO — so it can be unit tested
69
+ * directly.
70
+ *
71
+ * Leaves everything else (classes, style, text, `x-data`, `id`,
72
+ * `data-agent-native-node-id`, …) untouched: the node keeps its current
73
+ * rendered appearance and behavior, it simply stops being recognized as a
74
+ * component instance.
75
+ */
76
+ export function stripComponentAnnotations(
77
+ html: string,
78
+ source: { openStart: number; openEnd: number } | null | undefined,
79
+ ): { content: string; changed: boolean; removedAttributes: string[] } {
80
+ if (!source) {
81
+ return { content: html, changed: false, removedAttributes: [] };
82
+ }
83
+
84
+ const openTag = html.slice(source.openStart, source.openEnd);
85
+ const removed: string[] = [];
86
+ let newOpenTag = openTag;
87
+
88
+ const componentAttrRe = new RegExp(
89
+ `\\s+${COMPONENT_NAME_ATTR}\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>"']+)`,
90
+ "i",
91
+ );
92
+ if (componentAttrRe.test(newOpenTag)) {
93
+ removed.push(COMPONENT_NAME_ATTR);
94
+ newOpenTag = newOpenTag.replace(componentAttrRe, "");
95
+ }
96
+
97
+ const propAttrRe = new RegExp(
98
+ `\\s+${COMPONENT_PROP_PREFIX}[a-z0-9-]+\\s*=\\s*(?:"[^"]*"|'[^']*'|[^\\s>"']+)`,
99
+ "gi",
100
+ );
101
+ newOpenTag = newOpenTag.replace(propAttrRe, (match) => {
102
+ removed.push(match.trim().split("=")[0] ?? match.trim());
103
+ return "";
104
+ });
105
+
106
+ if (newOpenTag === openTag) {
107
+ return { content: html, changed: false, removedAttributes: [] };
108
+ }
109
+
110
+ return {
111
+ content:
112
+ html.slice(0, source.openStart) + newOpenTag + html.slice(source.openEnd),
113
+ changed: true,
114
+ removedAttributes: removed,
115
+ };
116
+ }
117
+
118
+ // ─── Persistence ────────────────────────────────────────────────────────────
119
+
120
+ async function persistEdit(file: {
121
+ id: string;
122
+ designId: string;
123
+ filename: string;
124
+ content: string;
125
+ expectedVersionHash: string;
126
+ }): Promise<string> {
127
+ await assertAccess("design", file.designId, "editor");
128
+
129
+ agentEnterDocument(file.id);
130
+ try {
131
+ const workspaceFile: SourceWorkspaceFile = {
132
+ id: file.id,
133
+ designId: file.designId,
134
+ filename: file.filename,
135
+ fileType: "html",
136
+ content: file.content,
137
+ createdAt: null,
138
+ updatedAt: null,
139
+ };
140
+
141
+ const result = await writeInlineSourceFile({
142
+ designId: file.designId,
143
+ file: workspaceFile,
144
+ content: file.content,
145
+ expectedVersionHash: file.expectedVersionHash,
146
+ });
147
+
148
+ return result.updatedAt;
149
+ } finally {
150
+ agentLeaveDocument(file.id);
151
+ }
152
+ }
153
+
154
+ // ─── Action ───────────────────────────────────────────────────────────────────
155
+
156
+ export default defineAction({
157
+ description:
158
+ "Detach a component instance (Figma's ⌥⌘B): strip the " +
159
+ "data-agent-native-component annotation and its data-agent-native-prop-* " +
160
+ "overrides from the selected instance root so it becomes a plain, " +
161
+ "unlinked element. Position, size, classes, text, and behavior are " +
162
+ "unchanged — only the component-instance linkage is removed. " +
163
+ "Inline/Alpine designs only.",
164
+ schema: z.object({
165
+ designId: z.string().describe("Design project ID"),
166
+ nodeId: z
167
+ .string()
168
+ .describe("data-agent-native-node-id of the component instance root"),
169
+ fileId: z
170
+ .string()
171
+ .optional()
172
+ .describe("Design file id; defaults to index.html"),
173
+ source: z
174
+ .object({
175
+ currentContent: z
176
+ .string()
177
+ .optional()
178
+ .describe("Latest editor HTML snapshot, when available."),
179
+ revision: z
180
+ .string()
181
+ .optional()
182
+ .describe(
183
+ "design_files.updatedAt value the currentContent is based on.",
184
+ ),
185
+ })
186
+ .optional(),
187
+ }),
188
+ run: async ({ designId, nodeId, fileId, source }) => {
189
+ const access = await resolveAccess("design", designId);
190
+ if (!access) throw new Error("Design not found");
191
+
192
+ const rawData = (access.resource as { data?: unknown }).data;
193
+ const sourceType = designSourceTypeFromData(rawData);
194
+
195
+ if (sourceType !== "inline") {
196
+ return {
197
+ designId,
198
+ nodeId,
199
+ sourceType,
200
+ detached: false,
201
+ ctaRequired: true,
202
+ ctaMessage:
203
+ "Detach instance requires a dedicated consented, version-guarded " +
204
+ "compiled-source transform for real-app sources. Not yet available.",
205
+ };
206
+ }
207
+
208
+ await assertAccess("design", designId, "editor");
209
+ const db = getDb();
210
+
211
+ const conditions = [
212
+ accessFilter(schema.designs, schema.designShares),
213
+ eq(schema.designFiles.designId, designId),
214
+ fileId
215
+ ? eq(schema.designFiles.id, fileId)
216
+ : eq(schema.designFiles.filename, "index.html"),
217
+ ];
218
+
219
+ const [file] = await db
220
+ .select({
221
+ id: schema.designFiles.id,
222
+ designId: schema.designFiles.designId,
223
+ filename: schema.designFiles.filename,
224
+ content: schema.designFiles.content,
225
+ updatedAt: schema.designFiles.updatedAt,
226
+ })
227
+ .from(schema.designFiles)
228
+ .innerJoin(
229
+ schema.designs,
230
+ eq(schema.designFiles.designId, schema.designs.id),
231
+ )
232
+ .where(and(...conditions))
233
+ .limit(1);
234
+
235
+ if (!file) throw new Error("Design HTML file not found.");
236
+
237
+ if (
238
+ source?.currentContent &&
239
+ source.revision &&
240
+ file.updatedAt &&
241
+ source.revision !== file.updatedAt
242
+ ) {
243
+ return {
244
+ designId,
245
+ nodeId,
246
+ detached: false,
247
+ conflict: true,
248
+ fileId: file.id,
249
+ filename: file.filename,
250
+ error:
251
+ "This file changed since this detach was prepared. Refresh the editor and try again.",
252
+ };
253
+ }
254
+
255
+ const html =
256
+ typeof source?.currentContent === "string"
257
+ ? source.currentContent
258
+ : (file.content ?? "");
259
+ const baseVersionHash = sourceContentHash(html);
260
+
261
+ const codeLayerSource: CodeLayerSource = {
262
+ kind: "design-file",
263
+ designId: file.designId,
264
+ fileId: file.id,
265
+ filename: file.filename,
266
+ };
267
+
268
+ const projection = buildCodeLayerProjection(html, {
269
+ source: codeLayerSource,
270
+ });
271
+
272
+ const node = projection.nodes.find((n) =>
273
+ componentNodeIdMatches(n, nodeId),
274
+ );
275
+ if (!node) {
276
+ throw new Error(
277
+ `Node "${nodeId}" not found. Run get-code-layer-projection to list current ids.`,
278
+ );
279
+ }
280
+
281
+ const componentName = componentNameFor(node);
282
+ if (!componentName) {
283
+ throw new Error(
284
+ `Node "${nodeId}" is not a component root (no data-agent-native-component attribute) — nothing to detach.`,
285
+ );
286
+ }
287
+
288
+ const {
289
+ content: patchedContent,
290
+ changed,
291
+ removedAttributes,
292
+ } = stripComponentAnnotations(html, node.source);
293
+
294
+ if (!changed) {
295
+ return {
296
+ designId,
297
+ nodeId,
298
+ componentName,
299
+ detached: false,
300
+ fileId: file.id,
301
+ filename: file.filename,
302
+ note: "No component annotation attributes found on this node's open tag.",
303
+ };
304
+ }
305
+
306
+ const updatedAt = await persistEdit({
307
+ id: file.id,
308
+ designId: file.designId,
309
+ filename: file.filename,
310
+ content: patchedContent,
311
+ expectedVersionHash: baseVersionHash,
312
+ });
313
+
314
+ agentUpdateSelection(file.id, {
315
+ selection: agentSelectionDescriptor(
316
+ { nodeId, selector: node.selector },
317
+ "Detaching instance",
318
+ ),
319
+ nodeId,
320
+ editingFile: file.filename,
321
+ designId: file.designId,
322
+ });
323
+
324
+ return {
325
+ designId,
326
+ nodeId,
327
+ componentName,
328
+ detached: true,
329
+ removedAttributes,
330
+ fileId: file.id,
331
+ filename: file.filename,
332
+ updatedAt,
333
+ content: patchedContent,
334
+ note: `Detached from component "${componentName}". This node is now a plain element.`,
335
+ };
336
+ },
337
+ });
@@ -0,0 +1,219 @@
1
+ /**
2
+ * export-design-as-figma-svg — export a design screen (or a selected
3
+ * subtree) as a genuinely VECTOR SVG document that Figma imports as
4
+ * editable layers (rect/path/gradients/filters stay editable; `<text>` is
5
+ * vectorized to outline paths on import — see the returned report's
6
+ * `vectorizedTextCaveat`). This is a different artifact from `export-svg`,
7
+ * which wraps the standalone HTML in a `foreignObject` for the editor's own
8
+ * "Download SVG" parity — Figma cannot import that as vectors at all, it
9
+ * stays an opaque embedded HTML blob.
10
+ *
11
+ * Resolution mirrors `take-design-screenshot.ts`: a single HTML screen
12
+ * (`fileId`, or `designId` + `filename` defaulting to `index.html`), using
13
+ * live collab content when the screen is actively being edited. `nodeId`
14
+ * additionally scopes the export to one selected element's subtree via its
15
+ * `data-agent-native-node-id` — the same attribute the editor stamps on
16
+ * selectable layers.
17
+ */
18
+
19
+ import { defineAction } from "@agent-native/core";
20
+ import { getText, hasCollabState } from "@agent-native/core/collab";
21
+ import { accessFilter } from "@agent-native/core/sharing";
22
+ import { and, eq } from "drizzle-orm";
23
+ import { z } from "zod";
24
+
25
+ import { getDb, schema } from "../server/db/index.js";
26
+ import { trySaveExportFile } from "../server/lib/design-export.js";
27
+ import {
28
+ renderDesignToFigmaSvg,
29
+ safeFigmaSvgFilename,
30
+ } from "../server/lib/design-to-figma-svg.js";
31
+ import { isMissingBrowserError } from "../server/lib/playwright-runtime.js";
32
+ import "../server/db/index.js"; // ensure registerShareableResource runs
33
+
34
+ async function liveContent(
35
+ fileId: string,
36
+ storedContent: string,
37
+ ): Promise<string> {
38
+ try {
39
+ if (await hasCollabState(fileId)) {
40
+ const live = await getText(fileId, "content");
41
+ if (typeof live === "string") return live;
42
+ }
43
+ } catch {
44
+ // SQL content is the deterministic fallback.
45
+ }
46
+ return storedContent;
47
+ }
48
+
49
+ /** Model-actionable message when no headless Chromium binary is available. */
50
+ export function chromiumUnavailableReason(err: unknown): string {
51
+ const detail = err instanceof Error ? err.message : String(err);
52
+ return (
53
+ "A headless Chromium browser is not available in this environment, so the " +
54
+ "vector SVG export cannot run here (this is expected in hosted/serverless " +
55
+ "deploys, which do not bundle a Chromium binary). Fall back to `export-svg` " +
56
+ "(a foreignObject-wrapped HTML snapshot — not Figma-importable as vectors, " +
57
+ "but still a usable static preview) or `export-html`. " +
58
+ `(${detail})`
59
+ );
60
+ }
61
+
62
+ export default defineAction({
63
+ description:
64
+ "Export a design screen (or a selected element's subtree via nodeId) as a " +
65
+ "genuinely vector SVG document — real <rect>/<path>/<text>/<image> markup " +
66
+ "with <linearGradient>/<radialGradient>/<filter> defs, which Figma imports " +
67
+ "as normal EDITABLE layers (unlike `export-svg`'s foreignObject wrapper, " +
68
+ "which Figma cannot import as vectors). Returns the SVG string plus an " +
69
+ "export report classifying each element as vectorized, approximated, " +
70
+ "rasterized, or omitted. Note: Figma converts all imported SVG <text> to " +
71
+ "outlined vector paths on paste/drag-import — geometry stays pixel-exact " +
72
+ "but the text is no longer live/editable type in Figma; see the report's " +
73
+ "`vectorizedTextCaveat`.",
74
+ schema: z.object({
75
+ designId: z
76
+ .string()
77
+ .optional()
78
+ .describe("Design project id. Required unless fileId is provided."),
79
+ fileId: z
80
+ .string()
81
+ .optional()
82
+ .describe(
83
+ "Specific design_files.id to export. Takes priority over designId/filename.",
84
+ ),
85
+ filename: z
86
+ .string()
87
+ .optional()
88
+ .default("index.html")
89
+ .describe(
90
+ "Filename to export when fileId is not provided. Defaults to index.html.",
91
+ ),
92
+ nodeId: z
93
+ .string()
94
+ .optional()
95
+ .describe(
96
+ "Scope the export to one element's subtree via its data-agent-native-node-id, " +
97
+ "instead of the whole screen.",
98
+ ),
99
+ embedImages: z
100
+ .boolean()
101
+ .optional()
102
+ .default(true)
103
+ .describe(
104
+ "Fetch and inline http(s) image sources/background-images as data: URIs, so the " +
105
+ "SVG is self-contained for clipboard paste. Set false to keep absolute URLs.",
106
+ ),
107
+ width: z.coerce
108
+ .number()
109
+ .int()
110
+ .positive()
111
+ .optional()
112
+ .describe(
113
+ "Render viewport width in px. Defaults to the screen's natural width or 1440.",
114
+ ),
115
+ height: z.coerce
116
+ .number()
117
+ .int()
118
+ .positive()
119
+ .optional()
120
+ .describe(
121
+ "Render viewport height in px. Defaults to the screen's natural height or 1200.",
122
+ ),
123
+ }),
124
+ readOnly: true,
125
+ http: { method: "POST" },
126
+ run: async ({
127
+ designId,
128
+ fileId,
129
+ filename,
130
+ nodeId,
131
+ embedImages,
132
+ width,
133
+ height,
134
+ }) => {
135
+ if (!designId && !fileId) {
136
+ throw new Error("designId or fileId is required.");
137
+ }
138
+
139
+ const db = getDb();
140
+ const conditions = [
141
+ accessFilter(schema.designs, schema.designShares),
142
+ fileId
143
+ ? eq(schema.designFiles.id, fileId)
144
+ : eq(schema.designFiles.designId, designId ?? ""),
145
+ ];
146
+ if (!fileId) {
147
+ conditions.push(
148
+ eq(schema.designFiles.filename, filename ?? "index.html"),
149
+ );
150
+ }
151
+
152
+ const [file] = await db
153
+ .select({
154
+ id: schema.designFiles.id,
155
+ designId: schema.designFiles.designId,
156
+ filename: schema.designFiles.filename,
157
+ fileType: schema.designFiles.fileType,
158
+ content: schema.designFiles.content,
159
+ })
160
+ .from(schema.designFiles)
161
+ .innerJoin(
162
+ schema.designs,
163
+ eq(schema.designFiles.designId, schema.designs.id),
164
+ )
165
+ .where(and(...conditions))
166
+ .limit(1);
167
+
168
+ if (!file) {
169
+ const err = new Error("Design file not found") as Error & {
170
+ statusCode: number;
171
+ };
172
+ err.statusCode = 404;
173
+ throw err;
174
+ }
175
+ if (file.fileType !== "html") {
176
+ throw new Error(
177
+ `export-design-as-figma-svg only supports HTML files (got "${file.fileType}").`,
178
+ );
179
+ }
180
+
181
+ const [designRow] = await db
182
+ .select({ title: schema.designs.title })
183
+ .from(schema.designs)
184
+ .where(eq(schema.designs.id, file.designId))
185
+ .limit(1);
186
+
187
+ const html = await liveContent(file.id, file.content ?? "");
188
+
189
+ let result: Awaited<ReturnType<typeof renderDesignToFigmaSvg>>;
190
+ try {
191
+ result = await renderDesignToFigmaSvg({
192
+ html,
193
+ width: width ?? 1440,
194
+ height: height ?? 1200,
195
+ title: designRow?.title ?? file.filename,
196
+ rootSelector: nodeId ? `[data-agent-native-node-id="${nodeId}"]` : null,
197
+ embedImages: embedImages ?? true,
198
+ });
199
+ } catch (err) {
200
+ if (isMissingBrowserError(err)) {
201
+ return { ok: false, reason: chromiumUnavailableReason(err) };
202
+ }
203
+ throw err;
204
+ }
205
+
206
+ const filenameOut = safeFigmaSvgFilename(designRow?.title ?? file.filename);
207
+ const saveResult = await trySaveExportFile(filenameOut, result.svg);
208
+
209
+ return {
210
+ ok: true,
211
+ designId: file.designId,
212
+ fileId: file.id,
213
+ filename: filenameOut,
214
+ svg: result.svg,
215
+ report: result.report,
216
+ ...saveResult,
217
+ };
218
+ },
219
+ });