@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
@@ -19,21 +19,19 @@ import { Slider } from "@/components/ui/slider";
19
19
  import type { StyleChangeMeta } from "../EditPanel";
20
20
  import {
21
21
  DesignColorPicker,
22
- type DesignFillRowPatch,
23
- type DesignGradientStop,
24
- type DesignGradientStopPatch,
22
+ imageFillToBackgroundStyles,
25
23
  type DesignGradientType,
26
24
  type ImageFillValue,
27
25
  } from "../inspector";
28
26
  import type { DesignPaintType } from "../inspector/DesignColorPicker";
29
27
  import type { GlslShaderPanelContext } from "../inspector/GlslShaderPanel";
30
28
  import {
31
- buildFillRows,
32
29
  buildGradientLayer,
33
30
  defaultGradientLayer,
34
31
  defaultGradientStops,
35
32
  fillLayerId,
36
33
  fillLayerIndex,
34
+ imageFillChangePatch,
37
35
  joinCssLayers,
38
36
  parseGradientLayer,
39
37
  SOLID_FILL_ID,
@@ -43,13 +41,22 @@ import {
43
41
  import { colorHasVisibleAlpha, cssColorOrFallback } from "./position-helpers";
44
42
  import { isMixedValue, MIXED_VALUE } from "./selection-helpers";
45
43
 
46
- function normalizeLengthValue(raw: string, defaultUnit: string): string | null {
44
+ export function normalizeLengthValue(
45
+ raw: string,
46
+ defaultUnit: string,
47
+ ): string | null {
47
48
  const trimmed = raw.trim();
48
49
  // Empty / invalid input returns null so the caller reverts the field instead
49
50
  // of committing an empty or garbage CSS value (e.g. fontSize:"" or
50
51
  // flexBasis:"abc") to the element's inline style.
51
52
  if (!trimmed) return null;
52
- if (/^-?\d+(\.\d+)?$/.test(trimmed)) return `${trimmed}${defaultUnit}`;
53
+ // Bare numbers get the field's default unit appended. `\d+` alone rejected a
54
+ // leading-decimal typed value like ".5" (no digit before the dot) even
55
+ // though "0.5" was accepted — the two are numerically identical, so ".5"
56
+ // silently reverted the field instead of becoming ".5px". Accept either a
57
+ // leading-digit form (`5`, `5.5`) or a leading-decimal form (`.5`).
58
+ if (/^-?(\d+(\.\d+)?|\.\d+)$/.test(trimmed))
59
+ return `${trimmed}${defaultUnit}`;
53
60
  // Validate free-form CSS so junk text never reaches the style. Fall back to
54
61
  // accepting the value when CSS.supports is unavailable (SSR/tests) to keep
55
62
  // prior behavior in non-DOM environments.
@@ -63,6 +70,21 @@ function normalizeLengthValue(raw: string, defaultUnit: string): string | null {
63
70
  return trimmed;
64
71
  }
65
72
 
73
+ /**
74
+ * Enter and Escape are the only PropInput keys that manually call `.blur()`
75
+ * after handling themselves (commit-then-blur for Enter, revert-then-blur for
76
+ * Escape). Both must pre-arm `skipNextBlurCommitRef` so the onBlur handler —
77
+ * which fires synchronously inside that manual `.blur()` call, before React
78
+ * re-renders with the just-committed/-reverted draft — doesn't re-run
79
+ * commit() a second time against the same stale closure and double-invoke
80
+ * `onChange` with the identical value. Exported so the contract (which keys
81
+ * require the guard) is unit-testable without needing to simulate real DOM
82
+ * focus/blur timing.
83
+ */
84
+ export function propInputKeyRequiresBlurGuard(key: string): boolean {
85
+ return key === "Enter" || key === "Escape";
86
+ }
87
+
66
88
  /** Compact input row: label + text input.
67
89
  *
68
90
  * For CSS length fields (font-size, padding, width, etc.) pass `defaultUnit`
@@ -92,9 +114,24 @@ export function PropInput({
92
114
  // Escape reverts and blurs; the blur handler must then skip its commit or it
93
115
  // would re-commit the stale draft closure (mirrors ScrubInput's Escape path).
94
116
  const skipNextBlurCommitRef = useRef(false);
117
+ // Tracks focus without becoming a reactive effect dependency: a plain
118
+ // `focused` state variable in the effect's deps below would re-run the
119
+ // resync the instant blur sets it false, re-syncing from a `value` prop
120
+ // that hasn't caught up with the commit that same blur just fired (the
121
+ // classic "Enter/blur flashes back to the old value" bug — see
122
+ // ScrubInput's pendingCommitRef comment for the same class of issue). A ref
123
+ // sidesteps that: the effect only re-runs when `value` itself changes, and
124
+ // simply consults the current focus state at that point.
125
+ const focusedRef = useRef(false);
95
126
 
96
127
  useEffect(() => {
97
- setDraft(value);
128
+ // Skip the resync while the user is actively editing — otherwise an
129
+ // unrelated re-render that changes this exact `value` prop (e.g. a poll
130
+ // tick, or another property's edit recomputing this element's styles)
131
+ // stomps the in-progress keystrokes with the last-committed value. This
132
+ // is the "you're typing and it snaps back to the old value" bug; mirrors
133
+ // ScrubInput's `!focused` guard on its own resync effect.
134
+ if (!focusedRef.current) setDraft(value);
98
135
  }, [value]);
99
136
 
100
137
  const commit = () => {
@@ -120,6 +157,7 @@ export function PropInput({
120
157
  type={type}
121
158
  value={draft}
122
159
  onFocus={(e) => {
160
+ focusedRef.current = true;
123
161
  if (mixed) e.currentTarget.select();
124
162
  }}
125
163
  onChange={(e) => {
@@ -131,6 +169,7 @@ export function PropInput({
131
169
  if (defaultUnit === undefined) onChange(e.target.value);
132
170
  }}
133
171
  onBlur={() => {
172
+ focusedRef.current = false;
134
173
  if (skipNextBlurCommitRef.current) {
135
174
  skipNextBlurCommitRef.current = false;
136
175
  return;
@@ -141,6 +180,13 @@ export function PropInput({
141
180
  if (e.key === "Enter") {
142
181
  e.preventDefault();
143
182
  commit();
183
+ // See propInputKeyRequiresBlurGuard: without this, the blur
184
+ // triggered below re-enters commit() a second time in the same
185
+ // synchronous tick, double-invoking onChange with the identical
186
+ // value.
187
+ skipNextBlurCommitRef.current = propInputKeyRequiresBlurGuard(
188
+ e.key,
189
+ );
144
190
  (e.currentTarget as HTMLInputElement).blur();
145
191
  return;
146
192
  }
@@ -149,7 +195,9 @@ export function PropInput({
149
195
  // Revert the draft to the last committed value and blur, matching
150
196
  // ScrubInput's Escape behavior.
151
197
  setDraft(value);
152
- skipNextBlurCommitRef.current = true;
198
+ skipNextBlurCommitRef.current = propInputKeyRequiresBlurGuard(
199
+ e.key,
200
+ );
153
201
  (e.currentTarget as HTMLInputElement).blur();
154
202
  }
155
203
  }}
@@ -171,6 +219,7 @@ export function ColorInput({
171
219
  backgroundPosition,
172
220
  onBackgroundImageChange,
173
221
  onImageFillChange,
222
+ onImageFillLayerChange,
174
223
  blendMode,
175
224
  onBlendModeChange,
176
225
  supportsLayeredFills = false,
@@ -187,7 +236,34 @@ export function ColorInput({
187
236
  backgroundRepeat?: string;
188
237
  backgroundPosition?: string;
189
238
  onBackgroundImageChange?: (value: string) => void;
239
+ /**
240
+ * Single-layer image-fill commit: receives just the edited `{url, fit}`
241
+ * and leaves building the CSS patch to the caller (via
242
+ * `imageFillToBackgroundStyles`). Kept for callers that only accept that
243
+ * shape; prefer `onImageFillLayerChange` below when the caller can commit
244
+ * an arbitrary multi-property patch, since this single-layer callback has
245
+ * no way to report which layer changed and therefore always causes the
246
+ * caller to overwrite the *whole* background stack — silently discarding
247
+ * any other gradient/image layer stacked alongside it.
248
+ */
190
249
  onImageFillChange?: (value: ImageFillValue) => void;
250
+ /**
251
+ * Layer-index-aware sibling of `onImageFillChange`: fires with the full
252
+ * four-property background patch already merged to preserve every other
253
+ * stacked gradient/image layer (see `imageFillChangePatch`) — the caller
254
+ * should commit it as-is (e.g. via `commitStylePatch`), not narrow it back
255
+ * down to a single-layer write. Takes priority over `onImageFillChange`
256
+ * when both are provided.
257
+ */
258
+ onImageFillLayerChange?: (
259
+ patch: Record<
260
+ | "backgroundImage"
261
+ | "backgroundSize"
262
+ | "backgroundRepeat"
263
+ | "backgroundPosition",
264
+ string
265
+ >,
266
+ ) => void;
191
267
  blendMode?: string;
192
268
  onBlendModeChange?: (value: string) => void;
193
269
  supportsLayeredFills?: boolean;
@@ -212,8 +288,18 @@ export function ColorInput({
212
288
  const [draft, setDraft] = useState(value);
213
289
  const [selectedFillId, setSelectedFillId] = useState(SOLID_FILL_ID);
214
290
  const [selectedStopId, setSelectedStopId] = useState<string | undefined>();
291
+ // Set while a "preview" phase tick has fired but its bracketing "commit"
292
+ // hasn't landed yet (see setNext below) — i.e. mid drag/scrub gesture in
293
+ // the popover. Guards the resync effect so an unrelated external `value`
294
+ // change (e.g. a poll tick, or another property's edit recomputing this
295
+ // element's styles) can't stomp the optimistic draft this gesture is
296
+ // driving. Mirrors ScrubInput's pendingCommitRef guard for the same class
297
+ // of bug, and PropInput's focusedRef guard for the keyboard-typing
298
+ // equivalent.
299
+ const pendingGestureRef = useRef(false);
215
300
 
216
301
  useEffect(() => {
302
+ if (pendingGestureRef.current) return;
217
303
  setDraft(value);
218
304
  }, [value]);
219
305
 
@@ -283,6 +369,7 @@ export function ColorInput({
283
369
  // state. Reject anything that doesn't parse as a plain solid color in
284
370
  // that case instead of forwarding it.
285
371
  if (!supportsLayeredFills && !parseCssColor(next)) return;
372
+ pendingGestureRef.current = phase === "preview";
286
373
  setDraft(next);
287
374
  onChange(next, { phase });
288
375
  };
@@ -323,66 +410,31 @@ export function ColorInput({
323
410
  setSelectedStopId(gradient?.stops[0]?.id);
324
411
  };
325
412
 
326
- const fillRows = supportsLayeredFills
327
- ? buildFillRows(
328
- draft || value || "#000000",
329
- backgroundLayers,
330
- selectedFillId,
331
- )
332
- : undefined;
333
-
334
- const handleFillChange = (id: string, patch: DesignFillRowPatch) => {
335
- if (id === SOLID_FILL_ID) {
336
- if (patch.value !== undefined) setNext(patch.value);
337
- if (patch.opacity !== undefined) {
338
- const parsed = parseCssColor(patch.value ?? draft);
339
- if (parsed) setNext(rgbaToCss(withColorOpacity(parsed, patch.opacity)));
340
- }
341
- return;
342
- }
343
-
344
- const index = fillLayerIndex(id);
345
- if (index === null || !onBackgroundImageChange) return;
346
- const currentLayer = backgroundLayers[index] || "";
347
- if (patch.value !== undefined) {
348
- replaceBackgroundLayer(index, patch.value);
349
- return;
350
- }
351
- if (patch.opacity === undefined) return;
352
- const gradient = parseGradientLayer(currentLayer);
353
- if (!gradient) return;
354
- replaceBackgroundLayer(
355
- index,
356
- buildGradientLayer(
357
- gradient.type,
358
- gradient.stops.map((stop) => ({
359
- ...stop,
360
- opacity: patch.opacity,
361
- })),
362
- gradient.prefix,
363
- ),
364
- );
365
- };
366
-
367
- const handleAddFill = onBackgroundImageChange
368
- ? () => {
369
- const nextLayers = [
370
- defaultGradientLayer("linear", draft || value || "#000000"),
371
- ...backgroundLayers,
372
- ];
373
- onBackgroundImageChange(joinCssLayers(nextLayers));
374
- setSelectedFillId(fillLayerId(0));
375
- setSelectedStopId("stop-0");
376
- }
377
- : undefined;
378
-
379
- const handleRemoveFill = onBackgroundImageChange
380
- ? (id: string) => {
381
- const index = fillLayerIndex(id);
382
- if (index === null) return;
383
- removeBackgroundLayer(index);
413
+ // Prefer the layer-index-aware callback when the caller wired it: it
414
+ // receives the full four-property patch already merged to preserve every
415
+ // sibling gradient/image layer (see `imageFillChangePatch`), instead of
416
+ // just `{url, fit}` — the shape `onImageFillChange` alone can't express a
417
+ // "leave every other layer untouched" write, so a caller stuck with only
418
+ // that prop is structurally forced to replace the whole background stack.
419
+ const handleImageFillChange = onImageFillLayerChange
420
+ ? (nextImage: ImageFillValue) => {
421
+ const styles = imageFillToBackgroundStyles(nextImage);
422
+ const layerIndex = fillLayerIndex(selectedFillId);
423
+ onImageFillLayerChange(
424
+ imageFillChangePatch(
425
+ {
426
+ backgroundImage: backgroundLayers,
427
+ backgroundSize: backgroundSizeLayers,
428
+ backgroundRepeat: backgroundRepeatLayers,
429
+ backgroundPosition: backgroundPositionLayers,
430
+ },
431
+ layerIndex,
432
+ styles,
433
+ ),
434
+ );
435
+ if (layerIndex === null) setSelectedFillId(fillLayerId(0));
384
436
  }
385
- : undefined;
437
+ : onImageFillChange;
386
438
 
387
439
  const handleGradientTypeChange =
388
440
  activeGradient && activeGradientIndex !== null
@@ -394,74 +446,6 @@ export function ColorInput({
394
446
  }
395
447
  : undefined;
396
448
 
397
- const handleGradientStopChange =
398
- activeGradient && activeGradientIndex !== null
399
- ? (id: string, patch: DesignGradientStopPatch) => {
400
- const nextStops = activeGradient.stops.map((stop) =>
401
- stop.id === id ? { ...stop, ...patch } : stop,
402
- );
403
- replaceBackgroundLayer(
404
- activeGradientIndex,
405
- buildGradientLayer(
406
- activeGradient.type,
407
- nextStops,
408
- activeGradient.prefix,
409
- ),
410
- );
411
- }
412
- : undefined;
413
-
414
- const handleAddGradientStop = onBackgroundImageChange
415
- ? () => {
416
- if (activeGradient && activeGradientIndex !== null) {
417
- const nextStop: DesignGradientStop = {
418
- id: `stop-${activeGradient.stops.length}`,
419
- color: draft || "#000000",
420
- position: 50,
421
- opacity: 100,
422
- };
423
- replaceBackgroundLayer(
424
- activeGradientIndex,
425
- buildGradientLayer(
426
- activeGradient.type,
427
- [...activeGradient.stops, nextStop],
428
- activeGradient.prefix,
429
- ),
430
- );
431
- setSelectedStopId(nextStop.id);
432
- return;
433
- }
434
-
435
- onBackgroundImageChange(
436
- joinCssLayers([
437
- defaultGradientLayer("linear", draft || value || "#000000"),
438
- ...backgroundLayers,
439
- ]),
440
- );
441
- setSelectedFillId(fillLayerId(0));
442
- setSelectedStopId("stop-0");
443
- }
444
- : undefined;
445
-
446
- const handleRemoveGradientStop =
447
- activeGradient && activeGradientIndex !== null
448
- ? (id: string) => {
449
- if (activeGradient.stops.length <= 2) return;
450
- const nextStops = activeGradient.stops.filter(
451
- (stop) => stop.id !== id,
452
- );
453
- replaceBackgroundLayer(
454
- activeGradientIndex,
455
- buildGradientLayer(
456
- activeGradient.type,
457
- nextStops,
458
- activeGradient.prefix,
459
- ),
460
- );
461
- setSelectedStopId(nextStops[0]?.id);
462
- }
463
- : undefined;
464
-
465
449
  const selectedPaintType: DesignPaintType =
466
450
  selectedFillId !== SOLID_FILL_ID
467
451
  ? selectedGradient
@@ -574,7 +558,7 @@ export function ColorInput({
574
558
  onPaintValueChange={
575
559
  supportsLayeredFills ? handlePaintValueChange : undefined
576
560
  }
577
- onImageFillChange={onImageFillChange}
561
+ onImageFillChange={handleImageFillChange}
578
562
  backgroundImage={selectedBackgroundLayerValue(backgroundLayers)}
579
563
  backgroundSize={selectedBackgroundLayerValue(backgroundSizeLayers)}
580
564
  backgroundRepeat={selectedBackgroundLayerValue(backgroundRepeatLayers)}
@@ -584,24 +568,10 @@ export function ColorInput({
584
568
  blendMode={blendMode}
585
569
  onBlendModeChange={onBlendModeChange}
586
570
  showBlendMode={Boolean(onBlendModeChange)}
587
- fillRows={fillRows}
588
- selectedFillId={selectedFillId}
589
- onFillSelect={supportsLayeredFills ? setSelectedFillId : undefined}
590
- onFillChange={supportsLayeredFills ? handleFillChange : undefined}
591
- onAddFill={supportsLayeredFills ? handleAddFill : undefined}
592
- onRemoveFill={supportsLayeredFills ? handleRemoveFill : undefined}
593
571
  paintType={selectedPaintType}
594
572
  onPaintTypeChange={handlePaintTypeChange}
595
573
  gradientType={activeGradient?.type}
596
574
  onGradientTypeChange={handleGradientTypeChange}
597
- gradientStops={activeGradient?.stops}
598
- selectedStopId={selectedStopId}
599
- onGradientStopSelect={setSelectedStopId}
600
- onGradientStopChange={handleGradientStopChange}
601
- onAddGradientStop={
602
- supportsLayeredFills ? handleAddGradientStop : undefined
603
- }
604
- onRemoveGradientStop={handleRemoveGradientStop}
605
575
  documentColors={documentColors}
606
576
  supportedPaintTypes={supportedPaintTypes}
607
577
  glslShaderContext={glslShaderContext}
@@ -1,4 +1,8 @@
1
- import { parseCssColor } from "@shared/color-utils";
1
+ import {
2
+ parseCssColor,
3
+ rgbaToCss,
4
+ withColorOpacity,
5
+ } from "@shared/color-utils";
2
6
 
3
7
  export function cssLengthNumber(
4
8
  value: string | undefined,
@@ -179,6 +183,54 @@ export function outlineOffsetForPosition(
179
183
  return `${roundToOneDecimal(-widthPx / 2)}px`;
180
184
  }
181
185
 
186
+ /**
187
+ * Resolve the borderStyle/outlineStyle to persist when (re)creating or
188
+ * restoring a stroke layer: preserve whatever real style was already set
189
+ * (solid/dashed/dotted/etc), and only default to "solid" when there's none
190
+ * yet or the legacy "none" hide value is still in place. Shared by both the
191
+ * border and outline "restore" branches in the Stroke section's add-layer
192
+ * handler so they stay in parity — previously only the outline branch did
193
+ * this (`styles.outlineStyle === "none" ? "solid" : styles.outlineStyle ||
194
+ * "solid"`); the border branch a few lines above it hardcoded `"solid"`
195
+ * unconditionally, silently discarding a dashed/dotted style whenever a
196
+ * hidden-but-existing border was restored via the section's + button
197
+ * instead of its own eye toggle (which already preserves style correctly).
198
+ */
199
+ export function resolveRestoredStrokeStyle(
200
+ styleValue: string | undefined,
201
+ ): string {
202
+ return styleValue === "none" ? "solid" : styleValue || "solid";
203
+ }
204
+
205
+ /**
206
+ * The style patch StrokeLayerControl's eye-toggle "show" click commits when
207
+ * un-hiding a border/outline stroke: restores full alpha on the stashed RGB
208
+ * color (see `strokeHiddenByColor`), guarantees a non-zero width, and only
209
+ * forces the style back to "solid" when it was the legacy "none" hide value
210
+ * (preserving a dashed/dotted style otherwise). Extracted as a pure function
211
+ * — rather than three sequential `onStyleChange` calls — so the caller can
212
+ * commit it as ONE atomic patch via `commitStylePatch`/`onStylesChange`
213
+ * (a single undo/history step, matching every other multi-property commit
214
+ * in this file, e.g. TextStrokeProperties' equivalent show handler).
215
+ */
216
+ export function strokeShowPatch(
217
+ prefix: "border" | "outline",
218
+ color: string,
219
+ width: string,
220
+ styleValue: string,
221
+ ): Record<string, string> {
222
+ const parsed = parseCssColor(color);
223
+ const restoredColor = parsed
224
+ ? rgbaToCss(withColorOpacity(parsed, 100))
225
+ : "#000000";
226
+ const patch: Record<string, string> = {
227
+ [`${prefix}Color`]: restoredColor,
228
+ [`${prefix}Width`]: width === "0px" ? "1px" : width || "1px",
229
+ };
230
+ if (styleValue === "none") patch[`${prefix}Style`] = "solid";
231
+ return patch;
232
+ }
233
+
182
234
  export function swatchStyle(value: string | undefined) {
183
235
  return {
184
236
  background: