@agent-native/core 0.92.9 → 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 (345) 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 +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/deploy/build.ts +133 -5
  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 +48 -4
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  45. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  46. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  47. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  48. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  57. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  58. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  59. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  60. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  61. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  62. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  63. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  64. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  65. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  66. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  67. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  68. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  69. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  70. package/corpus/templates/content/AGENTS.md +7 -6
  71. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  72. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  73. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  74. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  75. package/corpus/templates/content/actions/pull-document.ts +5 -7
  76. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  77. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  78. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  79. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  80. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  81. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  82. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  83. package/corpus/templates/content/app/i18n-data.ts +18 -0
  84. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  85. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  86. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  87. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  88. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  89. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  90. package/corpus/templates/design/AGENTS.md +45 -10
  91. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  92. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  93. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  94. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  95. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  96. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  97. package/corpus/templates/design/actions/generate-design.ts +79 -12
  98. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  99. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  100. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  101. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  102. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  103. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  104. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  105. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  106. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  107. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  108. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  109. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  110. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  111. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  112. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  113. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  114. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  115. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  116. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  117. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  118. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  119. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  120. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  121. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  122. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  123. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  124. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  125. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  126. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  127. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  128. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  129. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  130. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  131. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  132. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  133. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  134. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  135. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  136. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  137. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  138. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  139. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  140. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  141. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  142. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  143. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  144. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  145. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  146. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  147. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  148. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  149. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  150. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  151. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  152. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  153. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  154. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  155. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  156. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  157. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  158. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  159. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  160. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  161. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  162. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  163. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  164. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  165. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  166. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  167. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  168. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  169. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  170. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  171. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  172. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  173. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  174. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  178. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  179. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  180. package/corpus/templates/design/app/components/design/types.ts +33 -0
  181. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  182. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  183. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  184. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  185. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  186. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  187. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  188. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  189. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  190. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  191. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  192. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  193. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  194. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  195. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  196. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  197. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  198. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  199. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  200. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  201. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  202. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  203. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  240. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  241. package/corpus/templates/design/e2e/helpers.ts +27 -6
  242. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  243. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  244. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  245. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  246. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  247. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  248. package/corpus/templates/design/server/plugins/db.ts +41 -0
  249. package/corpus/templates/design/server/source-workspace.ts +64 -0
  250. package/corpus/templates/design/shared/board-file.ts +13 -2
  251. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  252. package/corpus/templates/design/shared/code-layer.ts +122 -8
  253. package/corpus/templates/design/shared/component-library.ts +161 -0
  254. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  255. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  256. package/corpus/templates/design/shared/figma-url.ts +149 -0
  257. package/corpus/templates/design/shared/pen-path.ts +16 -7
  258. package/corpus/templates/design/shared/source-mode.ts +1 -0
  259. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  260. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  261. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  262. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  263. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  264. package/dist/agent/production-agent.d.ts +79 -6
  265. package/dist/agent/production-agent.d.ts.map +1 -1
  266. package/dist/agent/production-agent.js +161 -14
  267. package/dist/agent/production-agent.js.map +1 -1
  268. package/dist/agent/run-manager.d.ts.map +1 -1
  269. package/dist/agent/run-manager.js +29 -5
  270. package/dist/agent/run-manager.js.map +1 -1
  271. package/dist/agent/run-store.d.ts +46 -0
  272. package/dist/agent/run-store.d.ts.map +1 -1
  273. package/dist/agent/run-store.js +61 -0
  274. package/dist/agent/run-store.js.map +1 -1
  275. package/dist/cli/design-connect.d.ts +6 -0
  276. package/dist/cli/design-connect.d.ts.map +1 -1
  277. package/dist/cli/design-connect.js +336 -139
  278. package/dist/cli/design-connect.js.map +1 -1
  279. package/dist/cli/skills.d.ts.map +1 -1
  280. package/dist/cli/skills.js +14 -0
  281. package/dist/cli/skills.js.map +1 -1
  282. package/dist/client/AssistantChat.d.ts +2 -0
  283. package/dist/client/AssistantChat.d.ts.map +1 -1
  284. package/dist/client/AssistantChat.js +19 -3
  285. package/dist/client/AssistantChat.js.map +1 -1
  286. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  287. package/dist/client/MultiTabAssistantChat.js +38 -18
  288. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  289. package/dist/client/agent-chat.d.ts +60 -0
  290. package/dist/client/agent-chat.d.ts.map +1 -1
  291. package/dist/client/agent-chat.js +122 -1
  292. package/dist/client/agent-chat.js.map +1 -1
  293. package/dist/client/chat/index.d.ts +1 -1
  294. package/dist/client/chat/index.d.ts.map +1 -1
  295. package/dist/client/chat/index.js +1 -1
  296. package/dist/client/chat/index.js.map +1 -1
  297. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  298. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  299. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  300. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  301. package/dist/client/index.d.ts +1 -1
  302. package/dist/client/index.d.ts.map +1 -1
  303. package/dist/client/index.js +1 -1
  304. package/dist/client/index.js.map +1 -1
  305. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  306. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  307. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  308. package/dist/collab/awareness-store.d.ts +5 -0
  309. package/dist/collab/awareness-store.d.ts.map +1 -1
  310. package/dist/collab/awareness-store.js +19 -12
  311. package/dist/collab/awareness-store.js.map +1 -1
  312. package/dist/collab/awareness.d.ts +2 -2
  313. package/dist/collab/awareness.d.ts.map +1 -1
  314. package/dist/collab/client.d.ts.map +1 -1
  315. package/dist/collab/client.js +13 -5
  316. package/dist/collab/client.js.map +1 -1
  317. package/dist/collab/index.d.ts +1 -0
  318. package/dist/collab/index.d.ts.map +1 -1
  319. package/dist/collab/index.js +1 -0
  320. package/dist/collab/index.js.map +1 -1
  321. package/dist/collab/recent-edits.d.ts +3 -1
  322. package/dist/collab/recent-edits.d.ts.map +1 -1
  323. package/dist/collab/recent-edits.js +57 -2
  324. package/dist/collab/recent-edits.js.map +1 -1
  325. package/dist/collab/routes.d.ts +1 -1
  326. package/dist/deploy/build.d.ts +24 -0
  327. package/dist/deploy/build.d.ts.map +1 -1
  328. package/dist/deploy/build.js +102 -5
  329. package/dist/deploy/build.js.map +1 -1
  330. package/dist/index.browser.d.ts +1 -1
  331. package/dist/index.browser.d.ts.map +1 -1
  332. package/dist/index.browser.js +1 -1
  333. package/dist/index.browser.js.map +1 -1
  334. package/dist/notifications/routes.d.ts +1 -1
  335. package/dist/observability/routes.d.ts +5 -5
  336. package/dist/progress/routes.d.ts +1 -1
  337. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  338. package/dist/server/agent-chat-plugin.js +87 -10
  339. package/dist/server/agent-chat-plugin.js.map +1 -1
  340. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  341. package/dist/server/builder-design-systems.d.ts +15 -0
  342. package/dist/server/builder-design-systems.d.ts.map +1 -1
  343. package/dist/server/builder-design-systems.js +9 -4
  344. package/dist/server/builder-design-systems.js.map +1 -1
  345. package/package.json +1 -1
@@ -24,6 +24,8 @@
24
24
  * Protocol (parent → iframe):
25
25
  *
26
26
  * { type: 'embedded-canvas-pan-mode', leftButtonEnabled }
27
+ * { type: 'embedded-canvas-gesture-mode', wheelEnabled,
28
+ * spaceKeyForwardingEnabled }
27
29
  * { type: 'embedded-canvas-pan-cancel' }
28
30
  *
29
31
  * Rules:
@@ -244,6 +246,11 @@ declare var __EMBEDDED_SPACE_KEY_FORWARDING_ENABLED__: boolean;
244
246
  }
245
247
  if (e.data.type === "embedded-canvas-pan-mode") {
246
248
  leftButtonEnabled = !!e.data.leftButtonEnabled;
249
+ return;
250
+ }
251
+ if (e.data.type === "embedded-canvas-gesture-mode") {
252
+ wheelEnabled = !!e.data.wheelEnabled;
253
+ spaceKeyForwardingEnabled = !!e.data.spaceKeyForwardingEnabled;
247
254
  }
248
255
  }
249
256
 
@@ -251,7 +251,12 @@
251
251
  el.getAttribute("data-agent-native-primitive") ||
252
252
  ""
253
253
  ).toLowerCase();
254
- if (primitive !== "rectangle" && primitive !== "rect") return false;
254
+ if (
255
+ primitive !== "rectangle" &&
256
+ primitive !== "rect" &&
257
+ primitive !== "frame"
258
+ )
259
+ return false;
255
260
  var cs = window.getComputedStyle(el);
256
261
  return cs.position === "absolute" || cs.position === "fixed";
257
262
  }
@@ -66,6 +66,26 @@
66
66
  var touchedProps: Record<string, string[]> = {};
67
67
  // Map of nodeId -> property -> original inline style value.
68
68
  var originalInlineValues: Record<string, Record<string, string>> = {};
69
+ // Map of nodeId -> resolved element, so applyPreview's per-track lookup
70
+ // during scrub/playback (driven by the parent's own rAF loop — one
71
+ // "motion-preview" message per frame) does one attribute-selector
72
+ // querySelector per node ONCE instead of once per track on every single
73
+ // frame. Invalidated (falls through to a fresh querySelector) whenever the
74
+ // cached element is no longer in the document — e.g. a host-applied edit
75
+ // replaced the node — so a stale reference never silently no-ops a track.
76
+ // Reset alongside the other per-load state in clearPreview so a reload
77
+ // after the previewed screen's DOM changes always re-resolves.
78
+ var elementCache: Record<string, HTMLElement | null> = {};
79
+
80
+ function resolveTrackElement(nodeId: string): HTMLElement | null {
81
+ var cached = elementCache[nodeId];
82
+ if (cached && document.contains(cached)) return cached;
83
+ var el = document.querySelector(
84
+ '[data-agent-native-node-id="' + nodeId + '"]',
85
+ ) as HTMLElement | null;
86
+ elementCache[nodeId] = el;
87
+ return el;
88
+ }
69
89
 
70
90
  function camelizeProp(prop: string): string {
71
91
  return String(prop).replace(/-([a-z])/g, function (_m: string, c: string) {
@@ -673,9 +693,7 @@
673
693
  function applyPreview(t: number): void {
674
694
  for (var i = 0; i < loadedTracks.length; i++) {
675
695
  var track = loadedTracks[i];
676
- var el = document.querySelector(
677
- '[data-agent-native-node-id="' + track.targetNodeId + '"]',
678
- ) as HTMLElement | null;
696
+ var el = resolveTrackElement(track.targetNodeId);
679
697
  if (!el) continue;
680
698
  var value = interpolate(track.keyframes, trackLocalT(track, t));
681
699
  if (value === "") continue;
@@ -700,9 +718,7 @@
700
718
  function clearPreview(): void {
701
719
  var nodeIds = Object.keys(touchedProps);
702
720
  for (var i = 0; i < nodeIds.length; i++) {
703
- var el = document.querySelector(
704
- '[data-agent-native-node-id="' + nodeIds[i] + '"]',
705
- ) as HTMLElement | null;
721
+ var el = resolveTrackElement(nodeIds[i]);
706
722
  if (!el) continue;
707
723
  var props = touchedProps[nodeIds[i]];
708
724
  for (var j = 0; j < props.length; j++) {
@@ -718,6 +734,7 @@
718
734
  loadedTracks = [];
719
735
  loadedDefaultEase = "ease";
720
736
  loadedTimelineDurationMs = null;
737
+ elementCache = {};
721
738
  }
722
739
 
723
740
  // Last previewed playhead position, so reloading the track list (which
@@ -10,6 +10,13 @@
10
10
  */
11
11
  (function () {
12
12
  window.addEventListener("message", function (e: MessageEvent) {
13
+ // Every other *.bridge.ts listener validates the sender before trusting
14
+ // the payload (see editor-chrome.bridge.ts, hit-test.bridge.ts, etc. —
15
+ // all check `e.source !== window.parent`); this one didn't, even though
16
+ // it's just a compile-pipeline smoke-test fixture with no real iframe
17
+ // wiring today. Match the house rule so it stays a safe template to
18
+ // copy from if this file is ever pointed at a live surface.
19
+ if (e.source !== window.parent) return;
13
20
  if (!e.data || e.data.type !== "agent-native:sample-ping") return;
14
21
  var correlationId: string = e.data.correlationId ?? "";
15
22
  try {
@@ -350,9 +350,14 @@ function CodeWorkbenchInner({
350
350
  const onUp = () => {
351
351
  target.removeEventListener("pointermove", onMove);
352
352
  target.removeEventListener("pointerup", onUp);
353
+ target.removeEventListener("pointercancel", onUp);
353
354
  };
354
355
  target.addEventListener("pointermove", onMove);
355
356
  target.addEventListener("pointerup", onUp);
357
+ // A cancelled pointer (e.g. a dialog stealing capture mid-drag) would
358
+ // otherwise never fire pointerup, leaking these listeners onto the
359
+ // resize handle until the next drag piles more on top of them.
360
+ target.addEventListener("pointercancel", onUp);
356
361
  },
357
362
  [api],
358
363
  );
@@ -9,6 +9,7 @@ import {
9
9
  } from "../../code-workbench-theme";
10
10
  import {
11
11
  dispatchKeybinding,
12
+ runCommand,
12
13
  type WorkbenchCommand,
13
14
  type WorkbenchCommandContext,
14
15
  } from "../commands";
@@ -93,11 +94,22 @@ export function MonacoHost({
93
94
  // Safety net: Monaco's own Cmd+S binding (if any) is not guaranteed, and
94
95
  // the root's capture-phase keydown handler already dispatches workbench
95
96
  // commands before Monaco sees them — this addCommand is a belt-and-
96
- // braces fallback for save specifically.
97
+ // braces fallback for save specifically. It goes through the same
98
+ // `workbench.save` command (via runCommand) as every other invocation
99
+ // path, not a bare `api.save()` call, so a failed save (stale version,
100
+ // missing local write consent, network error) still surfaces a toast /
101
+ // consent-retry here instead of failing silently.
97
102
  editor.addCommand(
98
103
  monacoModule.KeyMod.CtrlCmd | monacoModule.KeyCode.KeyS,
99
104
  () => {
100
- void commandContextRef.current.api.save();
105
+ const saveCommand = commandsRef.current.find(
106
+ (command) => command.id === "workbench.save",
107
+ );
108
+ if (saveCommand) {
109
+ void runCommand(saveCommand, commandContextRef.current);
110
+ } else {
111
+ void commandContextRef.current.api.save();
112
+ }
101
113
  },
102
114
  );
103
115
 
@@ -93,6 +93,16 @@ export function FileTree({
93
93
  text: "",
94
94
  at: 0,
95
95
  });
96
+ // When Enter/Escape ends a rename or new-file input via setRenamingPath /
97
+ // setPendingNewFile, React unmounts the focused input as part of that same
98
+ // update. Removing a focused DOM node makes the browser fire a native blur
99
+ // on it, which our onBlur handler would otherwise treat as an independent
100
+ // commit — double-submitting on Enter (rename/create called twice) and
101
+ // silently committing the draft on Escape instead of discarding it. These
102
+ // refs let the key handler mark "I already resolved this input" so the
103
+ // resulting blur is a no-op.
104
+ const renameHandledRef = useRef(false);
105
+ const newFileHandledRef = useRef(false);
96
106
 
97
107
  const rows = flattenVisibleTree(nodes, expandedPaths);
98
108
 
@@ -121,6 +131,7 @@ export function FileTree({
121
131
  );
122
132
 
123
133
  const startRename = useCallback((node: TreeNode) => {
134
+ renameHandledRef.current = false;
124
135
  setRenamingPath(node.path);
125
136
  setRenameDraft(baseName(node.path));
126
137
  }, []);
@@ -293,6 +304,7 @@ export function FileTree({
293
304
  label="New File" /* i18n-ignore */
294
305
  icon={IconPlus}
295
306
  onClick={() => {
307
+ newFileHandledRef.current = false;
296
308
  setPendingNewFile({ parentPath: "" });
297
309
  setNewFileDraft("");
298
310
  }}
@@ -390,14 +402,25 @@ export function FileTree({
390
402
  value={renameDraft}
391
403
  onClick={(event) => event.stopPropagation()}
392
404
  onChange={(event) => setRenameDraft(event.target.value)}
393
- onBlur={() => commitRename(row.node)}
405
+ onBlur={() => {
406
+ // Enter/Escape already resolved this input; the blur
407
+ // firing right after is just the DOM node unmounting,
408
+ // not a real "user clicked away" commit request.
409
+ if (renameHandledRef.current) {
410
+ renameHandledRef.current = false;
411
+ return;
412
+ }
413
+ void commitRename(row.node);
414
+ }}
394
415
  onKeyDown={(event) => {
395
416
  event.stopPropagation();
396
417
  if (event.key === "Enter") {
397
418
  event.preventDefault();
419
+ renameHandledRef.current = true;
398
420
  void commitRename(row.node);
399
421
  } else if (event.key === "Escape") {
400
422
  event.preventDefault();
423
+ renameHandledRef.current = true;
401
424
  setRenamingPath(null);
402
425
  }
403
426
  }}
@@ -418,6 +441,7 @@ export function FileTree({
418
441
  <ContextMenuItem
419
442
  className="text-[12px]"
420
443
  onSelect={() => {
444
+ newFileHandledRef.current = false;
421
445
  toggleFolder(row.path, true);
422
446
  setPendingNewFile({ parentPath: row.path });
423
447
  setNewFileDraft("");
@@ -481,14 +505,25 @@ export function FileTree({
481
505
  value={newFileDraft}
482
506
  placeholder="filename.ext" /* i18n-ignore */
483
507
  onChange={(event) => setNewFileDraft(event.target.value)}
484
- onBlur={() => commitNewFile()}
508
+ onBlur={() => {
509
+ // Enter/Escape already resolved this input; the blur firing
510
+ // right after is just the DOM node unmounting, not a real
511
+ // "user clicked away" commit request.
512
+ if (newFileHandledRef.current) {
513
+ newFileHandledRef.current = false;
514
+ return;
515
+ }
516
+ void commitNewFile();
517
+ }}
485
518
  onKeyDown={(event) => {
486
519
  event.stopPropagation();
487
520
  if (event.key === "Enter") {
488
521
  event.preventDefault();
522
+ newFileHandledRef.current = true;
489
523
  void commitNewFile();
490
524
  } else if (event.key === "Escape") {
491
525
  event.preventDefault();
526
+ newFileHandledRef.current = true;
492
527
  setPendingNewFile(null);
493
528
  setNewFileDraft("");
494
529
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Pure guard extracted from format-on-open.ts so it stays unit-testable
3
+ * without pulling in `../store` / `../model-registry` (and transitively
4
+ * `monaco-editor`, which requires a full browser environment and can't load
5
+ * under vitest's default node/jsdom setup) — same split used by
6
+ * status-bar-lang.ts for StatusBar.tsx.
7
+ */
8
+
9
+ /**
10
+ * Decide whether a completed background format result is still safe to
11
+ * apply. `snapshotContent` is the buffer content that was actually sent to
12
+ * Prettier; formatting runs asynchronously (dynamic plugin imports + async
13
+ * format), so by the time the result comes back the user may have already
14
+ * started editing the live model. Applying the formatted text in that case
15
+ * would silently discard their in-progress edits, so we bail unless the
16
+ * model's current value still matches the snapshot that was formatted.
17
+ */
18
+ export function shouldApplyFormatResult(
19
+ currentModelValue: string,
20
+ snapshotContent: string,
21
+ formatted: string,
22
+ ): boolean {
23
+ if (formatted === snapshotContent) return false;
24
+ return currentModelValue === snapshotContent;
25
+ }
@@ -3,8 +3,11 @@ import { useEffect } from "react";
3
3
  import { modelRegistry } from "../model-registry";
4
4
  import { useWorkbench, type BufferLoadedEvent } from "../store";
5
5
  import { providerKindFromKey } from "../workspace/types";
6
+ import { shouldApplyFormatResult } from "./format-on-open-guard";
6
7
  import { formatWithPrettier, isFormattablePath } from "./prettier-format";
7
8
 
9
+ export { shouldApplyFormatResult } from "./format-on-open-guard";
10
+
8
11
  /**
9
12
  * Auto-format-on-first-open policy: the design's SQL-backed (inline) files
10
13
  * are machine-generated, so the first time a formattable inline buffer is
@@ -42,10 +45,25 @@ export function useFormatOnFirstOpen({ enabled }: { enabled: boolean }): void {
42
45
  void (async () => {
43
46
  const result = await formatWithPrettier(event.read.content, event.path);
44
47
  if ("error" in result) return;
45
- if (result.formatted === event.read.content) return;
46
48
 
47
49
  const entry = modelRegistry.get(event.uri);
48
50
  if (!entry || entry.model.isDisposed()) return;
51
+ // Formatting is async (dynamic plugin imports + Prettier itself), so
52
+ // the user may have started typing in the buffer while we were
53
+ // awaiting the result. Applying `result.formatted` — derived from the
54
+ // pre-format snapshot — over the model unconditionally would silently
55
+ // clobber those in-progress edits. Bail out if the live model no
56
+ // longer matches the snapshot that was formatted; a later save or
57
+ // manual format can pick it up.
58
+ if (
59
+ !shouldApplyFormatResult(
60
+ entry.model.getValue(),
61
+ event.read.content,
62
+ result.formatted,
63
+ )
64
+ ) {
65
+ return;
66
+ }
49
67
  // Push the formatted text without touching savedAltVersionId, so the
50
68
  // buffer becomes dirty (matches the "formatted but unsaved" state a
51
69
  // real edit would produce) — then save silently.
@@ -31,6 +31,16 @@ function monacoUriFor(uri: string): monaco.Uri {
31
31
 
32
32
  class WorkbenchModelRegistry {
33
33
  private entries = new Map<string, WorkbenchModelEntry>();
34
+ /**
35
+ * Uris currently inside an `applyExternalContent`/`reloadContent` call.
36
+ * Monaco fires `onDidChangeContent` synchronously from inside
37
+ * `pushEditOperations`, before this class can update `savedAltVersionId` to
38
+ * the post-edit version — so a dirty-tracking subscriber reading
39
+ * `isDirty()` from that callback would see a stale "dirty" result for an
40
+ * edit nobody typed. Callers (store.tsx's dirty-tracking subscription) use
41
+ * `isApplyingExternalContent` to skip acting on that spurious notification.
42
+ */
43
+ private externalContentUris = new Set<string>();
34
44
 
35
45
  get(uri: string): WorkbenchModelEntry | undefined {
36
46
  return this.entries.get(uri);
@@ -75,6 +85,15 @@ class WorkbenchModelRegistry {
75
85
  return entry.model.getAlternativeVersionId() !== entry.savedAltVersionId;
76
86
  }
77
87
 
88
+ /**
89
+ * True while `uri`'s content is being replaced by `applyExternalContent`.
90
+ * Dirty-tracking subscribers must ignore `onDidChangeContent` notifications
91
+ * that fire while this is true — see the field comment above.
92
+ */
93
+ isApplyingExternalContent(uri: string): boolean {
94
+ return this.externalContentUris.has(uri);
95
+ }
96
+
78
97
  /**
79
98
  * Replace buffer content from an external change (agent edit, canvas edit)
80
99
  * while preserving the undo stack.
@@ -87,14 +106,33 @@ class WorkbenchModelRegistry {
87
106
  entry.savedAltVersionId = model.getAlternativeVersionId();
88
107
  return;
89
108
  }
90
- model.pushEditOperations(
91
- null,
92
- [{ range: model.getFullModelRange(), text: content }],
93
- () => null,
94
- );
109
+ this.externalContentUris.add(uri);
110
+ try {
111
+ model.pushEditOperations(
112
+ null,
113
+ [{ range: model.getFullModelRange(), text: content }],
114
+ () => null,
115
+ );
116
+ } finally {
117
+ this.externalContentUris.delete(uri);
118
+ }
95
119
  entry.savedAltVersionId = model.getAlternativeVersionId();
96
120
  }
97
121
 
122
+ /**
123
+ * Force an already-open buffer to match freshly read content + language,
124
+ * discarding any local edits. Used only for explicit user-initiated reload
125
+ * (e.g. the "File changed elsewhere — reload latest" conflict action) —
126
+ * the caller has already decided to discard unsaved edits, so unlike
127
+ * `applyExternalContent`'s callers this does not gate on dirty state.
128
+ */
129
+ reloadContent(uri: string, content: string, language: string) {
130
+ const entry = this.entries.get(uri);
131
+ if (!entry || entry.model.isDisposed()) return;
132
+ monaco.editor.setModelLanguage(entry.model, language);
133
+ this.applyExternalContent(uri, content);
134
+ }
135
+
98
136
  /** Mark the buffer clean as of the given alternative version id. */
99
137
  markSaved(uri: string, altVersionId: number) {
100
138
  const entry = this.entries.get(uri);
@@ -79,6 +79,21 @@ export function SearchView({ searchSeed }: SearchViewProps) {
79
79
  const [replaceAllFailures, setReplaceAllFailures] = useState(0);
80
80
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
81
81
  const abortRef = useRef<AbortController | null>(null);
82
+ // `providers` is recreated with a new array/object identity on every
83
+ // unrelated design poll tick (it flows through the design's SWR-polled
84
+ // `files` query, which has no structural-sharing guard), even when the
85
+ // actual set of workspace sources hasn't changed. Depending on `providers`
86
+ // directly would re-run the search effect on every poll tick, silently
87
+ // resetting `dismissed` (bringing back results the user just dismissed)
88
+ // and `replaceAllFailures`. Keep the latest providers in a ref for the
89
+ // debounced search to read, and key the effect off a stable signature of
90
+ // provider identities instead so it only re-runs when the source set
91
+ // actually changes.
92
+ const providersRef = useRef(providers);
93
+ providersRef.current = providers;
94
+ const providersSignature = providers
95
+ .map((provider) => provider.key)
96
+ .join(",");
82
97
 
83
98
  useEffect(() => {
84
99
  if (searchSeed.token === 0) return;
@@ -102,7 +117,7 @@ export function SearchView({ searchSeed }: SearchViewProps) {
102
117
  setDismissed({ files: new Set(), matches: new Set() });
103
118
  setReplaceAllFailures(0);
104
119
  searchWorkspace({
105
- providers,
120
+ providers: providersRef.current,
106
121
  query,
107
122
  matchCase,
108
123
  wholeWord,
@@ -121,7 +136,17 @@ export function SearchView({ searchSeed }: SearchViewProps) {
121
136
  if (debounceRef.current) clearTimeout(debounceRef.current);
122
137
  };
123
138
  // eslint-disable-next-line react-hooks/exhaustive-deps
124
- }, [query, matchCase, wholeWord, regex, providers]);
139
+ }, [query, matchCase, wholeWord, regex, providersSignature]);
140
+
141
+ // Safety net: abort any in-flight search when the view unmounts (it
142
+ // normally stays mounted-but-hidden per SideBar, but can genuinely unmount
143
+ // when the workbench itself closes) so a late `setResults` never fires
144
+ // against a torn-down component.
145
+ useEffect(() => {
146
+ return () => {
147
+ abortRef.current?.abort();
148
+ };
149
+ }, []);
125
150
 
126
151
  const visibleFiles = useMemo<FileSearchResult[]>(() => {
127
152
  if (!results) return [];
@@ -268,7 +268,16 @@ export function replaceMatchesInText(
268
268
  count += 1;
269
269
  if (options.regex) {
270
270
  // Support $1, $2, ... backreferences the way String.replace does.
271
- const groups = matchArgs.slice(1, -2);
271
+ // The callback args are (match, ...captureGroups, offset, string[,
272
+ // namedGroups]) — `namedGroups` is only appended when the pattern has
273
+ // named capture groups, so it must be dropped before slicing off the
274
+ // trailing offset/string pair, or a `$N` beyond the real group count
275
+ // would silently resolve to the match offset instead of "".
276
+ let rest = matchArgs.slice(1);
277
+ if (typeof rest[rest.length - 1] === "object") {
278
+ rest = rest.slice(0, -1);
279
+ }
280
+ const groups = rest.slice(0, -2);
272
281
  return replacement.replace(/\$(\d+)/g, (_all, index) => {
273
282
  const groupIndex = Number(index) - 1;
274
283
  return typeof groups[groupIndex] === "string" ? groups[groupIndex] : "";
@@ -341,6 +341,12 @@ export function WorkbenchProvider({
341
341
  if (dirtySubscribedModelsRef.current.has(entry.model)) return;
342
342
  dirtySubscribedModelsRef.current.add(entry.model);
343
343
  entry.model.onDidChangeContent(() => {
344
+ // Programmatic content replacement (agent edit, external reload) fires
345
+ // this synchronously before the model registry updates its saved
346
+ // version marker; treating that as a real edit would incorrectly mark
347
+ // the buffer dirty (and pin a preview tab) for a change the user never
348
+ // made. See modelRegistry.isApplyingExternalContent.
349
+ if (modelRegistry.isApplyingExternalContent(uri)) return;
344
350
  apiRef.current?.markDirty(uri, modelRegistry.isDirty(uri));
345
351
  });
346
352
  }, []);
@@ -369,7 +375,18 @@ export function WorkbenchProvider({
369
375
  try {
370
376
  const read = await provider.readFile(path);
371
377
  const language = read.language ?? languageForPath(path);
372
- modelRegistry.ensureModel(uri, read.content, language);
378
+ // loadBuffer is also used by reloadBuffer to force an already-open
379
+ // buffer back to the latest server content (e.g. the "File changed
380
+ // elsewhere — reload latest" conflict action). `ensureModel` is
381
+ // intentionally a no-op on content when a model already exists (so
382
+ // the initial-open path here never clobbers a buffer someone is
383
+ // mid-edit on) — reloadContent is the explicit, caller-opted-in
384
+ // discard-local-edits path for that case.
385
+ if (modelRegistry.has(uri)) {
386
+ modelRegistry.reloadContent(uri, read.content, language);
387
+ } else {
388
+ modelRegistry.ensureModel(uri, read.content, language);
389
+ }
373
390
  subscribeDirtyTracking(uri);
374
391
  rememberVersionHash(uri, read.versionHash);
375
392
  const firstLoad = !loadedOnceRef.current.has(uri);
@@ -382,7 +399,7 @@ export function WorkbenchProvider({
382
399
  dirty: modelRegistry.isDirty(uri),
383
400
  conflict: false,
384
401
  saving: false,
385
- readonly: read.readonly ?? false,
402
+ readonly: read.readonly ?? existingMeta?.readonly ?? false,
386
403
  language,
387
404
  fileId: read.fileId,
388
405
  savedVersionHash: read.versionHash,