@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
@@ -0,0 +1,265 @@
1
+ import { callAction } from "@agent-native/core/client";
2
+
3
+ import type { DrawAnnotation } from "@/components/visual-editor";
4
+
5
+ /**
6
+ * Draw-to-agent visual capture.
7
+ *
8
+ * DrawOverlay's `onSend` still hands the caller a text summary of stroke
9
+ * paths and text labels (see DesignCanvas's `onSend` wiring) — that stays,
10
+ * because raw coordinates remain useful for precision edits. This module
11
+ * additionally rasterizes the annotated screen into ONE PNG the agent can
12
+ * actually see, mirroring the Claude-design-style "draw over a screenshot"
13
+ * flow instead of forcing the agent to reconstruct a picture from
14
+ * coordinates alone.
15
+ *
16
+ * Pipeline: reuse `take-design-screenshot` (already renders the live screen
17
+ * server-side via headless Chromium — see that action's docblock) at the
18
+ * exact on-screen rect the user drew on, composite the serialized
19
+ * strokes/text on top client-side, then hand the finished PNG to the shared
20
+ * `upload-image` core action so only a durable URL — never base64 — reaches
21
+ * the agent chat message or any persisted state.
22
+ *
23
+ * Every step degrades silently to `null` (no thrown errors): a missing
24
+ * Chromium binary in hosted/serverless deploys, a slow render, a network
25
+ * blip fetching the screenshot, or an unconfigured upload provider should
26
+ * never block the user's drawing from reaching the agent as text — see
27
+ * `captureAnnotatedScreenshot`'s caller in DesignCanvas.tsx, which always
28
+ * falls through to the text-only summary.
29
+ */
30
+
31
+ /** Bounds the whole capture pipeline so a slow/hung Chromium render or
32
+ * upload never leaves the user waiting indefinitely to see their message
33
+ * land in chat. Generous enough for a local Playwright cold start. */
34
+ const CAPTURE_TIMEOUT_MS = 9000;
35
+
36
+ const MIN_DIMENSION_PX = 200;
37
+ const MAX_WIDTH_PX = 3840;
38
+ const MAX_HEIGHT_PX = 4096;
39
+
40
+ interface TakeDesignScreenshotResult {
41
+ ok: boolean;
42
+ reason?: string;
43
+ screenshots?: Array<{ url?: string }>;
44
+ }
45
+
46
+ interface UploadImageResult {
47
+ url?: string;
48
+ error?: string;
49
+ }
50
+
51
+ export interface CaptureAnnotatedScreenshotOptions {
52
+ /** Stable design id — used as fallback screen lookup and upload filename. */
53
+ designId?: string;
54
+ /** Specific design_files.id for the annotated screen. Preferred lookup key. */
55
+ fileId?: string;
56
+ /** Filename fallback when `fileId` is not available (paired with `designId`). */
57
+ filename?: string;
58
+ /** Only inline (SQL-backed HTML) screens can be rendered by take-design-screenshot. */
59
+ sourceType?: "inline" | "localhost" | "fusion";
60
+ annotations: DrawAnnotation[];
61
+ /** Layout-space canvas size the annotations were serialized against. */
62
+ canvasSize: { width: number; height: number };
63
+ }
64
+
65
+ function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
66
+ return new Promise((resolve, reject) => {
67
+ const timer = setTimeout(() => {
68
+ reject(
69
+ new Error(`annotation screenshot capture timed out after ${ms}ms`),
70
+ );
71
+ }, ms);
72
+ promise.then(
73
+ (value) => {
74
+ clearTimeout(timer);
75
+ resolve(value);
76
+ },
77
+ (error) => {
78
+ clearTimeout(timer);
79
+ reject(error);
80
+ },
81
+ );
82
+ });
83
+ }
84
+
85
+ function loadImageFromUrl(url: string): Promise<HTMLImageElement> {
86
+ return new Promise((resolve, reject) => {
87
+ const image = new Image();
88
+ image.onload = () => resolve(image);
89
+ image.onerror = () =>
90
+ reject(new Error("Failed to decode screenshot image"));
91
+ image.src = url;
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Draw one annotation (a stroke path or a text label) onto a 2D canvas
97
+ * context, in the same layout-pixel coordinate space DrawOverlay used when
98
+ * it serialized `pathData`/`position` in its own `send()` — see
99
+ * DrawOverlay.tsx's coordinate-model docblock. Parses the "M x,y L x,y ..."
100
+ * syntax with a small regex instead of `new Path2D(...)`: this is the exact
101
+ * (and only) format DrawOverlay's `send()` emits, and avoiding Path2D keeps
102
+ * this rasterizer testable against a plain fake context object — Path2D
103
+ * isn't implemented in jsdom/happy-dom, only real browsers. Exported for unit
104
+ * testing against a fake context (canvas 2D isn't implemented in jsdom).
105
+ */
106
+ export function drawAnnotationsOnContext(
107
+ ctx: Pick<
108
+ CanvasRenderingContext2D,
109
+ | "save"
110
+ | "restore"
111
+ | "beginPath"
112
+ | "moveTo"
113
+ | "lineTo"
114
+ | "stroke"
115
+ | "fillText"
116
+ | "strokeStyle"
117
+ | "fillStyle"
118
+ | "lineWidth"
119
+ | "lineCap"
120
+ | "lineJoin"
121
+ | "font"
122
+ | "textBaseline"
123
+ >,
124
+ annotations: DrawAnnotation[],
125
+ ): void {
126
+ for (const annotation of annotations) {
127
+ if (annotation.type === "path" && annotation.pathData) {
128
+ const points = parsePathDataPoints(annotation.pathData);
129
+ if (points.length < 2) continue;
130
+ ctx.save();
131
+ ctx.strokeStyle = annotation.color;
132
+ ctx.lineWidth = annotation.lineWidth;
133
+ ctx.lineCap = "round";
134
+ ctx.lineJoin = "round";
135
+ ctx.beginPath();
136
+ ctx.moveTo(points[0].x, points[0].y);
137
+ for (let i = 1; i < points.length; i++) {
138
+ ctx.lineTo(points[i].x, points[i].y);
139
+ }
140
+ ctx.stroke();
141
+ ctx.restore();
142
+ } else if (annotation.type === "text" && annotation.text) {
143
+ ctx.save();
144
+ ctx.fillStyle = annotation.color;
145
+ ctx.font = `600 ${14 + annotation.lineWidth}px sans-serif`;
146
+ ctx.textBaseline = "top";
147
+ ctx.fillText(
148
+ annotation.text,
149
+ annotation.position.x,
150
+ annotation.position.y,
151
+ );
152
+ ctx.restore();
153
+ }
154
+ }
155
+ }
156
+
157
+ const PATH_DATA_POINT_RE = /[ML]\s*(-?[\d.]+),(-?[\d.]+)/g;
158
+
159
+ /** Parse DrawOverlay's `"M x,y L x,y L x,y"` path syntax into raw points. */
160
+ function parsePathDataPoints(
161
+ pathData: string,
162
+ ): Array<{ x: number; y: number }> {
163
+ const points: Array<{ x: number; y: number }> = [];
164
+ for (const match of pathData.matchAll(PATH_DATA_POINT_RE)) {
165
+ points.push({ x: Number(match[1]), y: Number(match[2]) });
166
+ }
167
+ return points;
168
+ }
169
+
170
+ /** Fetch the screenshot bytes (avoids canvas cross-origin tainting — a
171
+ * same-process `fetch` + `Blob` object URL is never "foreign" to the canvas,
172
+ * regardless of the storage provider's CORS headers) and composite the
173
+ * annotations on top at the exact requested pixel size. */
174
+ async function compositeScreenshotWithAnnotations(
175
+ screenshotUrl: string,
176
+ annotations: DrawAnnotation[],
177
+ size: { width: number; height: number },
178
+ ): Promise<string | null> {
179
+ const response = await fetch(screenshotUrl);
180
+ if (!response.ok) return null;
181
+ const blob = await response.blob();
182
+ const objectUrl = URL.createObjectURL(blob);
183
+ try {
184
+ const image = await loadImageFromUrl(objectUrl);
185
+ const canvas = document.createElement("canvas");
186
+ canvas.width = size.width;
187
+ canvas.height = size.height;
188
+ const ctx = canvas.getContext("2d");
189
+ if (!ctx) return null;
190
+ ctx.drawImage(image, 0, 0, size.width, size.height);
191
+ drawAnnotationsOnContext(ctx, annotations);
192
+ return canvas.toDataURL("image/png");
193
+ } finally {
194
+ URL.revokeObjectURL(objectUrl);
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Best-effort: render the annotated screen, composite the drawing on top,
200
+ * and upload the result. Returns the durable image URL, or `null` on ANY
201
+ * failure (missing Chromium, no upload provider configured, network error,
202
+ * unsupported source type, timeout) — callers must treat `null` as "fall
203
+ * back to the text-only summary", never as a reason to drop the annotation.
204
+ */
205
+ export async function captureAnnotatedScreenshot(
206
+ options: CaptureAnnotatedScreenshotOptions,
207
+ ): Promise<string | null> {
208
+ const { designId, fileId, filename, sourceType, annotations, canvasSize } =
209
+ options;
210
+
211
+ // take-design-screenshot renders stored SQL/collab HTML content; localhost
212
+ // and fusion screens hold a URL, not renderable HTML, so skip straight to
213
+ // the text-only fallback for those instead of paying for a doomed render.
214
+ if (sourceType && sourceType !== "inline") return null;
215
+ if (!fileId && !designId) return null;
216
+ if (typeof document === "undefined") return null;
217
+ if (
218
+ !Number.isFinite(canvasSize.width) ||
219
+ !Number.isFinite(canvasSize.height) ||
220
+ canvasSize.width < MIN_DIMENSION_PX ||
221
+ canvasSize.height < MIN_DIMENSION_PX
222
+ ) {
223
+ return null;
224
+ }
225
+
226
+ const widthPx = Math.min(MAX_WIDTH_PX, Math.round(canvasSize.width));
227
+ const heightPx = Math.min(MAX_HEIGHT_PX, Math.round(canvasSize.height));
228
+
229
+ try {
230
+ return await withTimeout(
231
+ (async () => {
232
+ const shot = await callAction<TakeDesignScreenshotResult>(
233
+ "take-design-screenshot",
234
+ {
235
+ ...(fileId ? { fileId } : { designId, filename }),
236
+ widths: [widthPx],
237
+ heights: [heightPx],
238
+ },
239
+ );
240
+ const screenshotUrl = shot?.ok ? shot.screenshots?.[0]?.url : undefined;
241
+ if (!screenshotUrl) return null;
242
+
243
+ const compositeDataUrl = await compositeScreenshotWithAnnotations(
244
+ screenshotUrl,
245
+ annotations,
246
+ { width: widthPx, height: heightPx },
247
+ );
248
+ if (!compositeDataUrl) return null;
249
+
250
+ const uploaded = await callAction<UploadImageResult>("upload-image", {
251
+ data: compositeDataUrl,
252
+ filename: `design-annotation-${designId ?? fileId}-${Date.now()}.png`,
253
+ });
254
+ return uploaded?.url ?? null;
255
+ })(),
256
+ CAPTURE_TIMEOUT_MS,
257
+ );
258
+ } catch (error) {
259
+ console.warn(
260
+ "[DesignCanvas] annotated screenshot capture failed; falling back to text-only:",
261
+ error,
262
+ );
263
+ return null;
264
+ }
265
+ }
@@ -1,7 +1,14 @@
1
1
  interface SubmitDesignAnnotationsOptions {
2
2
  message: string;
3
3
  hasQueuedPins: boolean;
4
- send: (message: string) => void;
4
+ /**
5
+ * Deliver the message to the agent chat. Must reject (or throw) when
6
+ * delivery is not confirmed — e.g. `sendToAgentChatAndConfirm` resolving
7
+ * `delivered: false` should be turned into a thrown/rejected error here.
8
+ * A `send` that only fires-and-forgets (never throws on silent drops) will
9
+ * make this function report success even though nothing reached the agent.
10
+ */
11
+ send: (message: string) => void | Promise<void>;
5
12
  markQueuedPinsSubmitted: () => void;
6
13
  exitDrawMode: () => void;
7
14
  onError: (error: unknown) => void;
@@ -11,18 +18,22 @@ interface SubmitDesignAnnotationsOptions {
11
18
  * Submit a drawing/comment batch as one atomic UI transition.
12
19
  *
13
20
  * The overlay and queued pins must remain intact when chat handoff fails so the
14
- * user can retry without recreating their annotation work.
21
+ * user can retry without recreating their annotation work. `send` is awaited
22
+ * so a caller using an ack-confirmed delivery (e.g.
23
+ * `sendToAgentChatAndConfirm`) can reject when the message was silently
24
+ * dropped (no LLM/agent engine configured, panel never mounted, etc.) — the
25
+ * pins/draw-mode teardown below only runs once delivery is confirmed.
15
26
  */
16
- export function submitDesignAnnotations({
27
+ export async function submitDesignAnnotations({
17
28
  message,
18
29
  hasQueuedPins,
19
30
  send,
20
31
  markQueuedPinsSubmitted,
21
32
  exitDrawMode,
22
33
  onError,
23
- }: SubmitDesignAnnotationsOptions): boolean {
34
+ }: SubmitDesignAnnotationsOptions): Promise<boolean> {
24
35
  try {
25
- send(message);
36
+ await send(message);
26
37
  } catch (error) {
27
38
  onError(error);
28
39
  return false;
@@ -1,11 +1,25 @@
1
1
  /**
2
2
  * Converts a viewport point into the iframe's unscaled content coordinates.
3
+ *
4
+ * `scrollOffset` is the embedded screen's own internal document scroll
5
+ * (`iframe.contentWindow.scrollX/scrollY`), already expressed in the iframe's
6
+ * own unscaled content pixels — the SAME units as `iframeContentSize` and the
7
+ * function's return value. It is folded in AFTER the scale division, never
8
+ * multiplied/divided by `scaleX`/`scaleY`: the outer `scaleX`/`scaleY` factor
9
+ * only undoes the *host* canvas's zoom transform (which maps `iframeRect`,
10
+ * a host-viewport rect, onto `iframeContentSize`, the iframe's own layout
11
+ * box) and has nothing to do with how far the iframe's own document has been
12
+ * scrolled internally. Concretely: at 50% host zoom (scaleX = 0.5) with the
13
+ * embedded screen scrolled 600px down, a click at the iframe's visible top
14
+ * edge (rendered content y = 0) must land at content y = 600 — not 1200 (a
15
+ * mistaken `600 / 0.5`) and not 300 (a mistaken `600 * 0.5`).
3
16
  */
4
17
  export function getScreenContentPointFromClient(
5
18
  clientX: number,
6
19
  clientY: number,
7
20
  iframeRect: { left: number; top: number; width: number; height: number },
8
21
  iframeContentSize: { width: number; height: number },
22
+ scrollOffset: { left: number; top: number } = { left: 0, top: 0 },
9
23
  ): { x: number; y: number } {
10
24
  const scaleX =
11
25
  iframeContentSize.width > 0
@@ -16,8 +30,8 @@ export function getScreenContentPointFromClient(
16
30
  ? iframeRect.height / iframeContentSize.height
17
31
  : 1;
18
32
  return {
19
- x: (clientX - iframeRect.left) / (scaleX || 1),
20
- y: (clientY - iframeRect.top) / (scaleY || 1),
33
+ x: (clientX - iframeRect.left) / (scaleX || 1) + scrollOffset.left,
34
+ y: (clientY - iframeRect.top) / (scaleY || 1) + scrollOffset.top,
21
35
  };
22
36
  }
23
37
 
@@ -25,9 +25,14 @@ export function getEmbeddedIframeBackgroundColor(args: {
25
25
  : (args.embeddedFrameBackground ?? "transparent");
26
26
  }
27
27
 
28
- export function embeddedContentOffsetStyle(x: number, y: number): string {
28
+ export function embeddedContentOffsetCss(x: number, y: number): string {
29
29
  if (x === 0 && y === 0) return "";
30
- return `<style data-agent-native-content-offset>body > [data-agent-native-node-id]{translate:${Math.round(x)}px ${Math.round(y)}px;}</style>`;
30
+ return `body > [data-agent-native-node-id]{translate:${Math.round(x)}px ${Math.round(y)}px;}`;
31
+ }
32
+
33
+ export function embeddedContentOffsetStyle(x: number, y: number): string {
34
+ const css = embeddedContentOffsetCss(x, y);
35
+ return css ? `<style data-agent-native-content-offset>${css}</style>` : "";
31
36
  }
32
37
 
33
38
  function injectEmbeddedFrameStyle(content: string, style: string): string {
@@ -1,4 +1,4 @@
1
- import type { ElementInfo } from "../types";
1
+ import type { CanvasLayerHitCandidate, ElementInfo } from "../types";
2
2
 
3
3
  export interface IframeHotkeyPayload {
4
4
  key: string;
@@ -15,9 +15,11 @@ export interface IframeFigmaClipboardPastePayload {
15
15
  }
16
16
 
17
17
  export interface IframeContextMenuPayload {
18
+ screenId?: string;
18
19
  clientX: number;
19
20
  clientY: number;
20
21
  viewportClientX?: number;
21
22
  viewportClientY?: number;
22
23
  info?: ElementInfo | null;
24
+ layerCandidates?: CanvasLayerHitCandidate[];
23
25
  }
@@ -19,6 +19,14 @@ const VOID_HTML_TAGS = new Set([
19
19
  "wbr",
20
20
  ]);
21
21
 
22
+ const INSPECT_CODE_MAX_INLINE_TAG_LENGTH = 48;
23
+
24
+ interface ParsedOpeningTag {
25
+ tagName: string;
26
+ attributes: string[];
27
+ closing: ">" | "/>";
28
+ }
29
+
22
30
  export function normalizedElementTagName(
23
31
  tagName: string | null | undefined,
24
32
  ): string {
@@ -72,6 +80,58 @@ export function truncateOpeningTag(openTag: string, max = 32): string {
72
80
  );
73
81
  }
74
82
 
83
+ function parseInspectCodeOpeningTag(openTag: string): ParsedOpeningTag | null {
84
+ const tagMatch = /^<([a-zA-Z][\w:-]*)([\s\S]*?)(\/?>)$/.exec(openTag.trim());
85
+ if (!tagMatch?.[1] || (tagMatch[3] !== ">" && tagMatch[3] !== "/>")) {
86
+ return null;
87
+ }
88
+
89
+ const attributes: string[] = [];
90
+ const attributePattern =
91
+ /\s+([^\s=/>]+)(?:\s*=\s*("[^"]*"|'[^']*'|[^\s"'=<>`]+))?/g;
92
+ for (const match of (tagMatch[2] ?? "").matchAll(attributePattern)) {
93
+ const name = match[1];
94
+ if (!name) continue;
95
+ const normalizedName = name.toLowerCase();
96
+ if (
97
+ normalizedName === "style" ||
98
+ normalizedName.startsWith("data-agent-native-")
99
+ ) {
100
+ continue;
101
+ }
102
+ attributes.push(`${name}${match[2] ? `=${match[2]}` : ""}`);
103
+ }
104
+
105
+ return {
106
+ tagName: tagMatch[1],
107
+ attributes,
108
+ closing: tagMatch[3],
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Remove Design's runtime-only attributes, then format retained attributes to
114
+ * fit the Inspect Code popover without routine horizontal scrolling.
115
+ */
116
+ export function formatInspectCodeOpeningTag(
117
+ openTag: string,
118
+ maxInlineLength = INSPECT_CODE_MAX_INLINE_TAG_LENGTH,
119
+ ): string {
120
+ const parsed = parseInspectCodeOpeningTag(openTag);
121
+ if (!parsed) return openTag;
122
+
123
+ const inline = `<${parsed.tagName}${
124
+ parsed.attributes.length ? ` ${parsed.attributes.join(" ")}` : ""
125
+ }${parsed.closing}`;
126
+ if (!parsed.attributes.length || inline.length <= maxInlineLength) {
127
+ return inline;
128
+ }
129
+
130
+ return `<${parsed.tagName}\n ${parsed.attributes.join("\n ")}${
131
+ parsed.closing
132
+ }`;
133
+ }
134
+
75
135
  function tagNameFromOpeningTag(openTag: string): string | null {
76
136
  const match = /^<\/?\s*([a-zA-Z][\w:-]*)/.exec(openTag.trim());
77
137
  return match?.[1]?.toLowerCase() ?? null;
@@ -113,7 +173,9 @@ export function elementHtmlPreview(
113
173
  data.classes?.some((item) => item.trim()),
114
174
  );
115
175
  if (!openingTag && !hasFallbackMetadata) return null;
116
- const previewOpeningTag = openingTag ?? fallbackOpeningTag(data);
176
+ const previewOpeningTag = formatInspectCodeOpeningTag(
177
+ openingTag ?? fallbackOpeningTag(data),
178
+ );
117
179
  const tagName =
118
180
  tagNameFromOpeningTag(previewOpeningTag) ??
119
181
  normalizedElementTagName(data.tagName);
@@ -229,20 +291,28 @@ export function parseAlpineDataObject(
229
291
 
230
292
  const out: Record<string, string> = {};
231
293
  // Split on top-level commas only (no nesting / quotes inside values here).
294
+ // The quoted-value alternatives allow backslash-escaped quotes (`\\.`)
295
+ // inside the literal — without that, a value like `'it\'s ok'` truncates
296
+ // at the escaped quote (matching only `'it\'`), silently dropping the rest
297
+ // of the string. That mismatch used to slip past `canRebuildAlpineDataLosslessly`
298
+ // as a false positive: the truncated value round-tripped "stably" (in the
299
+ // sense of parse -> serialize -> parse staying self-consistent) while still
300
+ // being wrong relative to the original source.
232
301
  const pairRe =
233
- /(?:^|,)\s*(?:'([^']+)'|"([^"]+)"|([A-Za-z_$][\w$]*))\s*:\s*('[^']*'|"[^"]*"|true|false|-?\d+(?:\.\d+)?)/g;
302
+ /(?:^|,)\s*(?:'([^']+)'|"([^"]+)"|([A-Za-z_$][\w$]*))\s*:\s*('(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*"|true|false|-?\d+(?:\.\d+)?)/g;
234
303
  let m: RegExpExecArray | null;
235
304
  let matched = false;
236
305
  while ((m = pairRe.exec(inner)) !== null) {
237
306
  matched = true;
238
307
  const key = m[1] ?? m[2] ?? m[3];
239
308
  let raw = m[4]!;
240
- // Unwrap quotes for string literals; keep booleans / numbers verbatim.
241
- if (
242
- (raw.startsWith("'") && raw.endsWith("'")) ||
243
- (raw.startsWith('"') && raw.endsWith('"'))
244
- ) {
245
- raw = raw.slice(1, -1);
309
+ // Unwrap quotes for string literals, un-escaping backslash-escaped quotes
310
+ // back to their literal form (the inverse of the escaping
311
+ // `serializeAlpineDataObject` applies); keep booleans / numbers verbatim.
312
+ if (raw.startsWith("'") && raw.endsWith("'")) {
313
+ raw = raw.slice(1, -1).replace(/\\'/g, "'");
314
+ } else if (raw.startsWith('"') && raw.endsWith('"')) {
315
+ raw = raw.slice(1, -1).replace(/\\"/g, '"');
246
316
  }
247
317
  if (key) out[key] = raw;
248
318
  }