@agent-native/core 0.92.10 → 0.92.11

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 (334) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +23 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +181 -15
  5. package/corpus/core/src/agent/run-manager.ts +31 -4
  6. package/corpus/core/src/agent/run-store.ts +80 -0
  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.ts +165 -1
  12. package/corpus/core/src/client/chat/index.ts +9 -1
  13. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  14. package/corpus/core/src/client/index.ts +5 -0
  15. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  16. package/corpus/core/src/collab/awareness-store.ts +23 -12
  17. package/corpus/core/src/collab/client.ts +16 -5
  18. package/corpus/core/src/collab/index.ts +4 -0
  19. package/corpus/core/src/collab/recent-edits.ts +65 -2
  20. package/corpus/core/src/index.browser.ts +1 -0
  21. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  22. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  23. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  24. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  25. package/corpus/templates/clips/AGENTS.md +4 -0
  26. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  27. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  28. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  29. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  30. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  31. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
  32. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  33. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  34. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  44. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  45. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  46. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  47. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  56. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  57. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  58. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  59. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  60. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  61. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  62. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  63. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  64. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  65. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  66. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  67. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  68. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  69. package/corpus/templates/content/AGENTS.md +7 -6
  70. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  71. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  72. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  73. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  74. package/corpus/templates/content/actions/pull-document.ts +5 -7
  75. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  76. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  77. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  78. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  79. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  80. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  81. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  82. package/corpus/templates/content/app/i18n-data.ts +18 -0
  83. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  84. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  85. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  86. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  87. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  88. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  89. package/corpus/templates/design/AGENTS.md +45 -10
  90. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  91. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  92. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  93. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  94. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  95. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  96. package/corpus/templates/design/actions/generate-design.ts +79 -12
  97. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  98. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  99. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  100. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  101. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  102. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  103. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  104. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  105. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  106. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  107. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  108. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  109. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  110. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  111. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  112. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  113. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  114. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  115. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  116. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  117. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  118. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  119. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  120. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  121. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  122. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  123. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  124. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  125. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  126. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  127. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  129. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  130. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  131. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  132. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  133. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  134. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  135. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  136. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  137. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  138. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  139. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  140. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  141. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  142. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  143. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  144. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  145. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  146. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  147. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  148. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  149. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  150. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  151. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  152. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  153. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  154. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  155. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  156. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  157. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  158. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  159. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  160. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  161. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  162. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  163. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  164. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  165. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  166. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  167. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  168. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  169. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  170. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  171. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  172. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  173. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  174. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  178. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  179. package/corpus/templates/design/app/components/design/types.ts +33 -0
  180. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  181. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  182. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  183. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  184. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  185. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  186. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  187. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  188. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  189. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  190. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  191. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  192. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  193. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  194. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  195. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  196. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  197. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  198. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  199. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  200. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  201. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  202. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  239. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  240. package/corpus/templates/design/e2e/helpers.ts +27 -6
  241. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  242. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  243. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  244. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  245. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  246. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  247. package/corpus/templates/design/server/plugins/db.ts +41 -0
  248. package/corpus/templates/design/server/source-workspace.ts +64 -0
  249. package/corpus/templates/design/shared/board-file.ts +13 -2
  250. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  251. package/corpus/templates/design/shared/code-layer.ts +122 -8
  252. package/corpus/templates/design/shared/component-library.ts +161 -0
  253. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  254. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  255. package/corpus/templates/design/shared/figma-url.ts +149 -0
  256. package/corpus/templates/design/shared/pen-path.ts +16 -7
  257. package/corpus/templates/design/shared/source-mode.ts +1 -0
  258. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  259. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  260. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  261. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  262. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  263. package/dist/agent/production-agent.d.ts +79 -6
  264. package/dist/agent/production-agent.d.ts.map +1 -1
  265. package/dist/agent/production-agent.js +161 -14
  266. package/dist/agent/production-agent.js.map +1 -1
  267. package/dist/agent/run-manager.d.ts.map +1 -1
  268. package/dist/agent/run-manager.js +29 -5
  269. package/dist/agent/run-manager.js.map +1 -1
  270. package/dist/agent/run-store.d.ts +46 -0
  271. package/dist/agent/run-store.d.ts.map +1 -1
  272. package/dist/agent/run-store.js +61 -0
  273. package/dist/agent/run-store.js.map +1 -1
  274. package/dist/cli/design-connect.d.ts +6 -0
  275. package/dist/cli/design-connect.d.ts.map +1 -1
  276. package/dist/cli/design-connect.js +336 -139
  277. package/dist/cli/design-connect.js.map +1 -1
  278. package/dist/cli/skills.d.ts.map +1 -1
  279. package/dist/cli/skills.js +14 -0
  280. package/dist/cli/skills.js.map +1 -1
  281. package/dist/client/AssistantChat.d.ts +2 -0
  282. package/dist/client/AssistantChat.d.ts.map +1 -1
  283. package/dist/client/AssistantChat.js +19 -3
  284. package/dist/client/AssistantChat.js.map +1 -1
  285. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  286. package/dist/client/MultiTabAssistantChat.js +38 -18
  287. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  288. package/dist/client/agent-chat.d.ts +60 -0
  289. package/dist/client/agent-chat.d.ts.map +1 -1
  290. package/dist/client/agent-chat.js +122 -1
  291. package/dist/client/agent-chat.js.map +1 -1
  292. package/dist/client/chat/index.d.ts +1 -1
  293. package/dist/client/chat/index.d.ts.map +1 -1
  294. package/dist/client/chat/index.js +1 -1
  295. package/dist/client/chat/index.js.map +1 -1
  296. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  297. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  298. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  299. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  300. package/dist/client/index.d.ts +1 -1
  301. package/dist/client/index.d.ts.map +1 -1
  302. package/dist/client/index.js +1 -1
  303. package/dist/client/index.js.map +1 -1
  304. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  305. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  306. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  307. package/dist/collab/awareness-store.d.ts +5 -0
  308. package/dist/collab/awareness-store.d.ts.map +1 -1
  309. package/dist/collab/awareness-store.js +19 -12
  310. package/dist/collab/awareness-store.js.map +1 -1
  311. package/dist/collab/client.d.ts.map +1 -1
  312. package/dist/collab/client.js +13 -5
  313. package/dist/collab/client.js.map +1 -1
  314. package/dist/collab/index.d.ts +1 -0
  315. package/dist/collab/index.d.ts.map +1 -1
  316. package/dist/collab/index.js +1 -0
  317. package/dist/collab/index.js.map +1 -1
  318. package/dist/collab/recent-edits.d.ts +3 -1
  319. package/dist/collab/recent-edits.d.ts.map +1 -1
  320. package/dist/collab/recent-edits.js +57 -2
  321. package/dist/collab/recent-edits.js.map +1 -1
  322. package/dist/index.browser.d.ts +1 -1
  323. package/dist/index.browser.d.ts.map +1 -1
  324. package/dist/index.browser.js +1 -1
  325. package/dist/index.browser.js.map +1 -1
  326. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  327. package/dist/server/agent-chat-plugin.js +87 -10
  328. package/dist/server/agent-chat-plugin.js.map +1 -1
  329. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  330. package/dist/server/builder-design-systems.d.ts +15 -0
  331. package/dist/server/builder-design-systems.d.ts.map +1 -1
  332. package/dist/server/builder-design-systems.js +9 -4
  333. package/dist/server/builder-design-systems.js.map +1 -1
  334. package/package.json +1 -1
@@ -25,6 +25,7 @@ import {
25
25
  type CanvasFrameGeometry,
26
26
  type CanvasFramePlacement,
27
27
  } from "../shared/canvas-frames.js";
28
+ import { isUniqueConstraintViolation } from "../shared/db-conflict.js";
28
29
  import {
29
30
  makeLocalhostRouteId,
30
31
  titleFromRoutePath,
@@ -488,6 +489,18 @@ export default defineAction({
488
489
  height: number;
489
490
  }> = [];
490
491
  const placementIntents: PlacementIntent[] = [];
492
+ // Duplicate-route guard: `existingFiles`/`existingByFilename`/the
493
+ // route-candidate lookups below are all snapshotted ONCE above the loop
494
+ // and never refreshed as new files are inserted mid-loop, so two entries
495
+ // in the SAME `requestedRoutes` call that resolve to the same route
496
+ // (repeated `paths`, or a `routeId` and a `path` naming the same route)
497
+ // each independently see "no existing match" and each insert a fresh
498
+ // `design_files` row — two overlapping screens for one route. Track
499
+ // routeIds already processed THIS call (keyed with width/height so an
500
+ // intentional multi-viewport request for the same route still creates
501
+ // its distinct variants) and skip later duplicates outright.
502
+ const seenRouteRequestKeys = new Set<string>();
503
+ let placementIndex = 0;
491
504
 
492
505
  for (let index = 0; index < requestedRoutes.length; index += 1) {
493
506
  const input = requestedRoutes[index]!;
@@ -505,6 +518,9 @@ export default defineAction({
505
518
  );
506
519
  const routeId =
507
520
  input.routeId ?? manifestRoute?.id ?? makeLocalhostRouteId(path);
521
+ const routeRequestKey = `${routeId}::${input.width ?? ""}x${input.height ?? ""}`;
522
+ if (seenRouteRequestKeys.has(routeRequestKey)) continue;
523
+ seenRouteRequestKeys.add(routeRequestKey);
508
524
  const title =
509
525
  input.title ?? manifestRoute?.title ?? titleFromRoutePath(path);
510
526
  const sourceFile = input.sourceFile ?? manifestRoute?.sourceFile;
@@ -619,7 +635,9 @@ export default defineAction({
619
635
  const filename =
620
636
  existing?.filename ??
621
637
  uniqueFilename(path, usedFilenames, preferredFilename);
622
- const fileId = existing?.id ?? nanoid();
638
+ // Reassigned below if a concurrent request wins the insert race for
639
+ // this exact (designId, filename) pair — see the `else` branch.
640
+ let fileId = existing?.id ?? nanoid();
623
641
  const existingScreenMetadata = existing
624
642
  ? metadataForFile(
625
643
  existing.id,
@@ -656,16 +674,50 @@ export default defineAction({
656
674
  await seedFromText(existing.id, url);
657
675
  }
658
676
  } else {
659
- await db.insert(schema.designFiles).values({
660
- id: fileId,
661
- designId,
662
- filename,
663
- fileType: "html",
664
- content: url,
665
- createdAt: now,
666
- updatedAt: now,
667
- });
668
- await seedFromText(fileId, url);
677
+ try {
678
+ await db.insert(schema.designFiles).values({
679
+ id: fileId,
680
+ designId,
681
+ filename,
682
+ fileType: "html",
683
+ content: url,
684
+ createdAt: now,
685
+ updatedAt: now,
686
+ });
687
+ await seedFromText(fileId, url);
688
+ } catch (err) {
689
+ if (!isUniqueConstraintViolation(err)) throw err;
690
+ // Cross-request race: this snapshot's `existingFiles` query ran
691
+ // before a concurrent add-localhost-screens call (for the same
692
+ // route) committed its own insert, so both requests independently
693
+ // computed the same deterministic filename and both tried to
694
+ // create it. The `design_files_design_filename_unique_idx` unique
695
+ // index (see server/plugins/db.ts) turns the loser's insert into
696
+ // this error instead of a silent duplicate screen. Recover by
697
+ // adopting whichever row actually won the race and updating its
698
+ // content, the same way the `existing` branch above does.
699
+ const [winner] = await db
700
+ .select()
701
+ .from(schema.designFiles)
702
+ .where(
703
+ and(
704
+ eq(schema.designFiles.designId, designId),
705
+ eq(schema.designFiles.filename, filename),
706
+ ),
707
+ )
708
+ .limit(1);
709
+ if (!winner) throw err;
710
+ fileId = winner.id;
711
+ await db
712
+ .update(schema.designFiles)
713
+ .set({ content: url, fileType: "html", updatedAt: now })
714
+ .where(eq(schema.designFiles.id, winner.id));
715
+ if (await hasCollabState(winner.id)) {
716
+ await applyText(winner.id, url, "content", "agent");
717
+ } else {
718
+ await seedFromText(winner.id, url);
719
+ }
720
+ }
669
721
  }
670
722
 
671
723
  savedScreens.push({
@@ -688,12 +740,13 @@ export default defineAction({
688
740
  x:
689
741
  input.x ??
690
742
  existingFrame?.x ??
691
- layoutStartX + index * (width + layoutGap),
743
+ layoutStartX + placementIndex * (width + layoutGap),
692
744
  y: input.y ?? existingFrame?.y ?? layoutStartY,
693
745
  width,
694
746
  height,
695
- z: input.z ?? existingFrame?.z ?? index,
747
+ z: input.z ?? existingFrame?.z ?? placementIndex,
696
748
  };
749
+ placementIndex += 1;
697
750
  placementIntents.push({
698
751
  fileId,
699
752
  filename,
@@ -12,11 +12,12 @@
12
12
  * HTML attribute on the component root.
13
13
  * - `classReplace` — replaces one Tailwind class with another on the root node.
14
14
  *
15
- * **Tier B (real-app, localhost / fusion):** prop writes require the
16
- * `applyEdit` source capability. Localhost sources have it (design bridge +
17
- * user write consent); fusion sources gain it after bridge hardening. For
18
- * sources without it the action returns a `ctaRequired: true` response and
19
- * does not modify any source.
15
+ * **Real-app sources (localhost / fusion):** deliberately fail closed. This
16
+ * action's patcher operates on SQL-backed HTML design files; it must never be
17
+ * reused for compiled JSX/TSX source. Real-app prop persistence needs a
18
+ * dedicated consented, version-guarded bridge transform. Until that exists,
19
+ * callers may preview but this action returns `ctaRequired: true` without
20
+ * reading or modifying a design file.
20
21
  *
21
22
  * See DESIGN-STUDIO-PLAN.md §6.1, §7 (preview/apply contract), §11 phase 2.
22
23
  */
@@ -38,10 +39,11 @@ import { z } from "zod";
38
39
  import { getDb, schema } from "../server/db/index.js";
39
40
  import "../server/db/index.js"; // ensure registerShareableResource runs
40
41
  import {
42
+ prepareInlineSourceEdit,
43
+ SourceWorkspaceEditConflictError,
41
44
  writeInlineSourceFile,
42
45
  type SourceWorkspaceFile,
43
46
  } from "../server/source-workspace.js";
44
- import { resolveSourceCapabilities } from "../shared/capability-resolver.js";
45
47
  import {
46
48
  applyVisualEdit,
47
49
  buildCodeLayerProjection,
@@ -56,9 +58,7 @@ import {
56
58
  componentNameFor,
57
59
  componentNodeIdMatches,
58
60
  } from "../shared/component-model.js";
59
- import { hasCapability } from "../shared/design-source-capabilities.js";
60
61
  import { designSourceTypeFromData } from "../shared/source-mode.js";
61
- import { sourceContentHash } from "../shared/source-workspace.js";
62
62
 
63
63
  // ─── Helpers ──────────────────────────────────────────────────────────────────
64
64
 
@@ -174,8 +174,8 @@ export default defineAction({
174
174
  "For inline/Alpine designs, edits the data-agent-native-prop-* attributes, " +
175
175
  "x-data expression, or class list of the component root via the deterministic " +
176
176
  "HTML-patch path (same seam as apply-visual-edit). " +
177
- "For real-app sources, the applyEdit capability must be available; if not, " +
178
- "returns ctaRequired=true without modifying any file.",
177
+ "For real-app sources, returns ctaRequired=true without modifying any file; " +
178
+ "compiled source requires a dedicated consented bridge transform.",
179
179
  schema: z.object({
180
180
  designId: z.string().describe("Design project ID"),
181
181
  nodeId: z
@@ -234,19 +234,21 @@ export default defineAction({
234
234
  .optional(),
235
235
  }),
236
236
  run: async ({ designId, nodeId, fileId, edit, source }) => {
237
- const db = getDb();
238
-
239
237
  // ── Access check ────────────────────────────────────────────────────────
240
238
  const access = await resolveAccess("design", designId);
241
239
  if (!access) throw new Error("Design not found");
242
240
 
243
- // ── Source type + capability gate ────────────────────────────────────────
241
+ // ── Source type gate ────────────────────────────────────────────────────
244
242
  const rawData = (access.resource as { data?: unknown }).data;
245
243
  const sourceType = designSourceTypeFromData(rawData);
246
- const caps = resolveSourceCapabilities(sourceType);
247
244
 
248
- // Real-app sources gate on `applyEdit` (bridge write hardening).
249
- if (sourceType !== "inline" && !hasCapability(caps, "applyEdit")) {
245
+ // Fail closed for every real-app tier even if its generic capability map
246
+ // advertises applyEdit. This action only knows how to patch SQL-backed HTML;
247
+ // allowing localhost through here could report success for the mirror while
248
+ // leaving the real JSX/TSX file untouched. A future compiled-source action
249
+ // must perform consent, canonical path resolution, AST anchoring, and an
250
+ // expected-version bridge write as one dedicated transaction.
251
+ if (sourceType !== "inline") {
250
252
  return {
251
253
  designId,
252
254
  nodeId,
@@ -254,13 +256,14 @@ export default defineAction({
254
256
  persisted: false,
255
257
  ctaRequired: true,
256
258
  ctaMessage:
257
- "Prop write-back to real app sources requires the bridge applyEdit " +
258
- "capability, which lands with bridge write hardening. " +
259
+ "Prop write-back to real app sources requires a dedicated consented, " +
260
+ "version-guarded compiled-source transform. " +
259
261
  "Use preview-component-prop-edit to preview without persisting.",
260
262
  };
261
263
  }
262
264
 
263
265
  await assertAccess("design", designId, "editor");
266
+ const db = getDb();
264
267
 
265
268
  // ── Fetch file ───────────────────────────────────────────────────────────
266
269
  const conditions = [
@@ -289,12 +292,24 @@ export default defineAction({
289
292
 
290
293
  if (!file) throw new Error("Design HTML file not found.");
291
294
 
292
- if (
293
- source?.currentContent &&
294
- source.revision &&
295
- file.updatedAt &&
296
- source.revision !== file.updatedAt
297
- ) {
295
+ const workspaceFile: SourceWorkspaceFile = {
296
+ id: file.id,
297
+ designId: file.designId,
298
+ filename: file.filename,
299
+ fileType: "html",
300
+ content: file.content,
301
+ createdAt: null,
302
+ updatedAt: file.updatedAt,
303
+ };
304
+ let prepared: Awaited<ReturnType<typeof prepareInlineSourceEdit>>;
305
+ try {
306
+ prepared = await prepareInlineSourceEdit({
307
+ file: workspaceFile,
308
+ currentContent: source?.currentContent,
309
+ revision: source?.revision,
310
+ });
311
+ } catch (error) {
312
+ if (!(error instanceof SourceWorkspaceEditConflictError)) throw error;
298
313
  return {
299
314
  designId,
300
315
  nodeId,
@@ -307,18 +322,12 @@ export default defineAction({
307
322
  };
308
323
  }
309
324
 
310
- // Prefer explicit editor content after the caller's revision check, and use
311
- // the saved SQL content as the fallback. Collab/Yjs reads can be stale
312
- // across local dev worker processes and make prop controls lag behind.
313
- const html =
314
- typeof source?.currentContent === "string"
315
- ? source.currentContent
316
- : (file.content ?? "");
317
- // Capture the hash of this EXACT base — the same string the transform
318
- // below reads from — so persistEdit can pass it through as
319
- // expectedVersionHash. A re-read at persist time would hash whatever is
320
- // live "then" instead of proving this base is still current.
321
- const baseVersionHash = sourceContentHash(html);
325
+ // The transform runs against the caller's working copy (when supplied),
326
+ // while the persist CAS uses the live hash that working copy is allowed to
327
+ // replace. Keeping those identities separate preserves rapid unsaved prop
328
+ // edits without weakening concurrent-writer rejection.
329
+ const html = prepared.content;
330
+ const baseVersionHash = prepared.expectedVersionHash;
322
331
 
323
332
  // ── Resolve node ─────────────────────────────────────────────────────────
324
333
  const codeLayerSource: CodeLayerSource = {
@@ -32,7 +32,7 @@ import { z } from "zod";
32
32
  import { getDb, schema } from "../server/db/index.js";
33
33
  import "../server/db/index.js"; // ensure registerShareableResource runs
34
34
  import {
35
- readLiveSourceFile,
35
+ prepareInlineSourceEdit,
36
36
  writeInlineSourceFile,
37
37
  type SourceWorkspaceFile,
38
38
  } from "../server/source-workspace.js";
@@ -51,7 +51,6 @@ import {
51
51
  readTimelinePlaybackMode,
52
52
  withTimelinePlaybackMode,
53
53
  } from "../shared/motion-timeline.js";
54
- import { sourceContentHash } from "../shared/source-workspace.js";
55
54
 
56
55
  // ─── DoS-guard caps ──────────────────────────────────────────────────────────
57
56
  //
@@ -343,7 +342,15 @@ export default defineAction({
343
342
  .describe(
344
343
  "Current open editor HTML for the target file. When supplied, the " +
345
344
  "managed motion CSS is patched into this content instead of the " +
346
- "last SQL snapshot so in-flight local edits are preserved.",
345
+ "last SQL snapshot so in-flight local edits are preserved. " +
346
+ "revision must also be supplied.",
347
+ ),
348
+ revision: z
349
+ .string()
350
+ .optional()
351
+ .describe(
352
+ "design_files.updatedAt value the currentContent is based on. " +
353
+ "Required when currentContent is supplied.",
347
354
  ),
348
355
  }),
349
356
  run: async ({
@@ -357,6 +364,7 @@ export default defineAction({
357
364
  defaultEase,
358
365
  includeContent,
359
366
  currentContent: currentContentInput,
367
+ revision,
360
368
  }) => {
361
369
  const access = await assertAccess("design", designId, "editor");
362
370
 
@@ -378,6 +386,7 @@ export default defineAction({
378
386
  filename: schema.designFiles.filename,
379
387
  fileType: schema.designFiles.fileType,
380
388
  content: schema.designFiles.content,
389
+ updatedAt: schema.designFiles.updatedAt,
381
390
  })
382
391
  .from(schema.designFiles)
383
392
  .innerJoin(
@@ -398,40 +407,25 @@ export default defineAction({
398
407
  const fileId = file.id;
399
408
  const resolvedSourceRef = sourceRef ?? fileId;
400
409
 
401
- // The EDITABLE base for compiling/injecting CSS: prefer the caller's
402
- // in-flight editor snapshot when supplied (currentContentInput), else the
403
- // LIVE collab-authoritative content for this file (not the raw SQL row
404
- // a live editor's Y.Text may be ahead of the last SQL snapshot). Either
405
- // way, `baseVersionHash` is computed from the EXACT string chosen as the
406
- // base, at this same point in time, so it faithfully represents "what
407
- // this transform's base was" for the persist's optimistic-concurrency
408
- // check below — never a fresh re-hash of already-transformed content
409
- // (that would trivially match itself and prove nothing raced).
410
- let currentContent: string;
411
- let baseVersionHash: string;
412
- if (currentContentInput !== undefined) {
413
- // Caller supplied their own editor snapshot as the patch base. It may
414
- // already be stale relative to the live doc — that's fine.
415
- // writeInlineSourceFile's own guard (via readLiveSourceFile at write
416
- // time) correctly rejects the write if this base no longer matches
417
- // live state, exactly as designed for every other caller-supplied-base
418
- // action (see apply-component-prop-edit.ts / apply-shader-fill.ts).
419
- currentContent = currentContentInput;
420
- baseVersionHash = sourceContentHash(currentContent);
421
- } else {
422
- const workspaceFileForRead: SourceWorkspaceFile = {
410
+ // Keep the transform working copy separate from the live version it may
411
+ // replace. A caller snapshot can legitimately contain unsaved local edits
412
+ // beyond the matching SQL revision; the write CAS must guard the observed
413
+ // live base, not incorrectly hash those unsaved edits as if already live.
414
+ const prepared = await prepareInlineSourceEdit({
415
+ file: {
423
416
  id: file.id,
424
417
  designId: file.designId,
425
418
  filename: file.filename,
426
419
  fileType: file.fileType,
427
420
  content: file.content,
428
421
  createdAt: null,
429
- updatedAt: null,
430
- };
431
- const live = await readLiveSourceFile(workspaceFileForRead);
432
- currentContent = live.content;
433
- baseVersionHash = live.versionHash;
434
- }
422
+ updatedAt: file.updatedAt,
423
+ },
424
+ currentContent: currentContentInput,
425
+ revision,
426
+ });
427
+ const currentContent = prepared.content;
428
+ const baseVersionHash = prepared.expectedVersionHash;
435
429
 
436
430
  // ── 2. Compile tracks → CSS ─────────────────────────────────────────────
437
431
  const inputTracks = tracks as MotionTrack[];
@@ -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,