@agent-native/core 0.80.7 → 0.80.9

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 (302) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +6 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +8 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +10 -2
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +10 -2
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +7 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +7 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +7 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +9 -2
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +6 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +6 -0
  13. package/corpus/core/docs/content/template-design.mdx +9 -2
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/cli/mcp.ts +10 -0
  16. package/corpus/core/src/cli/skills.ts +58 -12
  17. package/corpus/core/src/client/AssistantChat.tsx +70 -27
  18. package/corpus/core/src/client/analytics.ts +3 -1
  19. package/corpus/core/src/client/error-format.ts +25 -0
  20. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +13 -11
  21. package/corpus/core/src/client/guided-questions.tsx +32 -4
  22. package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +33 -1
  23. package/corpus/core/src/client/resources/BuiltinCapabilityDetail.tsx +8 -0
  24. package/corpus/core/src/client/resources/use-builtin-capabilities.ts +4 -2
  25. package/corpus/core/src/client/session-replay.ts +34 -0
  26. package/corpus/core/src/client/sharing/ShareButton.tsx +106 -21
  27. package/corpus/core/src/coding-tools/index.ts +1 -1
  28. package/corpus/core/src/mcp/index.ts +2 -0
  29. package/corpus/core/src/mcp/screen-memory-stdio.ts +290 -0
  30. package/corpus/core/src/mcp-client/builtin-capabilities.ts +13 -1
  31. package/corpus/core/src/mcp-client/index.ts +9 -0
  32. package/corpus/core/src/mcp-client/screen-memory-local.ts +461 -0
  33. package/corpus/core/src/onboarding/types.ts +7 -0
  34. package/corpus/core/src/server/agent-chat-plugin.ts +25 -0
  35. package/corpus/core/src/server/agents-bundle.ts +35 -7
  36. package/corpus/core/src/styles/agent-native.css +5 -0
  37. package/corpus/core/src/templates/default/app/global.css +35 -35
  38. package/corpus/templates/analytics/AGENTS.md +6 -0
  39. package/corpus/templates/analytics/actions/github-repo-files.ts +9 -0
  40. package/corpus/templates/analytics/app/components/layout/Layout.tsx +4 -4
  41. package/corpus/templates/analytics/app/global.css +144 -40
  42. package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +1 -1
  43. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +1 -1
  44. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
  45. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +4 -4
  46. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -9
  47. package/corpus/templates/analytics/changelog/2026-06-29-agents-can-search-and-read-connected-github-repositories-whe.md +6 -0
  48. package/corpus/templates/analytics/changelog/2026-06-29-sessions-and-explorer-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  49. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -1
  50. package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +1 -1
  51. package/corpus/templates/assets/app/global.css +99 -35
  52. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
  53. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +4 -4
  54. package/corpus/templates/assets/app/routes/library.tsx +4 -4
  55. package/corpus/templates/assets/changelog/2026-06-29-asset-library-and-detail-layouts-adapt-when-the-agent-sidebar.md +6 -0
  56. package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
  57. package/corpus/templates/brain/app/global.css +63 -35
  58. package/corpus/templates/brain/app/routes/search.tsx +1 -1
  59. package/corpus/templates/brain/app/routes/settings.tsx +1 -1
  60. package/corpus/templates/brain/app/routes/sources.tsx +2 -2
  61. package/corpus/templates/brain/changelog/2026-06-29-search-settings-and-source-layouts-adapt-when-the-agent-sidebar.md +6 -0
  62. package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +4 -4
  63. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +3 -2
  64. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -2
  65. package/corpus/templates/calendar/app/global.css +69 -35
  66. package/corpus/templates/calendar/changelog/2026-06-29-event-and-find-time-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
  67. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  68. package/corpus/templates/chat/app/global.css +35 -35
  69. package/corpus/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  70. package/corpus/templates/clips/AGENTS.md +13 -0
  71. package/corpus/templates/clips/actions/get-screen-memory-status.ts +12 -0
  72. package/corpus/templates/clips/actions/query-screen-memory-context.ts +29 -0
  73. package/corpus/templates/clips/actions/view-screen.ts +2 -2
  74. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  75. package/corpus/templates/clips/app/global.css +47 -35
  76. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +3 -3
  77. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +45 -18
  78. package/corpus/templates/clips/changelog/2026-06-29-meeting-detail-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-06-29-screen-memory-keeps-a-local-rolling-screen-buffer-for-agent-context.md +5 -0
  80. package/corpus/templates/clips/changelog/2026-06-29-video-chat-is-easier-to-find-on-recording-pages-and-s3-setup.md +6 -0
  81. package/corpus/templates/clips/chrome-extension/src/styles.css +39 -39
  82. package/corpus/templates/clips/desktop/src/app.tsx +290 -1
  83. package/corpus/templates/clips/desktop/src/shared/config.ts +83 -0
  84. package/corpus/templates/clips/desktop/src/styles.css +67 -52
  85. package/corpus/templates/clips/desktop/src-tauri/src/config.rs +50 -0
  86. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +17 -0
  87. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +13 -13
  88. package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +990 -0
  89. package/corpus/templates/clips/server/plugins/onboarding.ts +2 -0
  90. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +69 -16
  91. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +3 -3
  92. package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
  93. package/corpus/templates/content/app/global.css +29 -15
  94. package/corpus/templates/content/changelog/2026-06-29-database-gallery-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  95. package/corpus/templates/content/changelog/2026-06-29-read-only-pages-no-longer-try-to-autosave-or-open-editor-onl.md +6 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +25 -20
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +8 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +13 -0
  100. package/corpus/templates/design/AGENTS.md +24 -14
  101. package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1054 -0
  102. package/corpus/templates/design/actions/apply-visual-edit.ts +24 -4
  103. package/corpus/templates/design/actions/edit-design.ts +6 -3
  104. package/corpus/templates/design/actions/export-coding-handoff.ts +12 -4
  105. package/corpus/templates/design/actions/generate-design.ts +6 -21
  106. package/corpus/templates/design/actions/generate-screens.ts +48 -5
  107. package/corpus/templates/design/actions/get-design.ts +1 -0
  108. package/corpus/templates/design/actions/present-design-variants.ts +355 -30
  109. package/corpus/templates/design/actions/update-design.ts +47 -10
  110. package/corpus/templates/design/actions/update-file.ts +33 -8
  111. package/corpus/templates/design/actions/view-screen.ts +89 -31
  112. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +389 -71
  113. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
  114. package/corpus/templates/design/app/components/design/EditPanel.tsx +596 -95
  115. package/corpus/templates/design/app/components/design/LayersPanel.tsx +428 -221
  116. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1197 -228
  117. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +94 -217
  118. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +28 -14
  119. package/corpus/templates/design/app/components/design/index.ts +0 -1
  120. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +228 -170
  121. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +140 -13
  122. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +39 -8
  123. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +4 -2
  124. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +75 -5
  125. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +37 -12
  126. package/corpus/templates/design/app/components/design/inspector/index.ts +1 -0
  127. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +17 -4
  128. package/corpus/templates/design/app/components/design/types.ts +9 -0
  129. package/corpus/templates/design/app/components/layout/Layout.tsx +23 -5
  130. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +448 -259
  131. package/corpus/templates/design/app/global.css +77 -49
  132. package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
  133. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +8 -1
  134. package/corpus/templates/design/app/i18n/zh-TW.ts +4 -0
  135. package/corpus/templates/design/app/i18n-data.ts +55 -3
  136. package/corpus/templates/design/app/pages/DesignEditor.tsx +2783 -658
  137. package/corpus/templates/design/app/root.tsx +1 -1
  138. package/corpus/templates/design/changelog/2026-06-29-agent-edits-to-a-design-are-no-longer-occasionally-dropped-o.md +6 -0
  139. package/corpus/templates/design/changelog/2026-06-29-box-shadow-and-gradient-colors-keep-named-css-colors-like-re.md +6 -0
  140. package/corpus/templates/design/changelog/2026-06-29-containers-no-longer-block-selecting-nested-elements-and-nor.md +6 -0
  141. package/corpus/templates/design/changelog/2026-06-29-cut-cmd-ctrl-x-now-removes-the-selected-element-to-the-clipb.md +6 -0
  142. package/corpus/templates/design/changelog/2026-06-29-deleting-with-multiple-layers-selected-in-the-layers-panel-n.md +6 -0
  143. package/corpus/templates/design/changelog/2026-06-29-design-agents-now-generate-and-refine-variants-from-stronger.md +6 -0
  144. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +6 -0
  145. package/corpus/templates/design/changelog/2026-06-29-design-questions-now-appear-as-a-cleaner-canvas-intake-with-.md +6 -0
  146. package/corpus/templates/design/changelog/2026-06-29-design-variant-directions-now-appear-as-board-screens-with-chat-buttons.md +5 -0
  147. package/corpus/templates/design/changelog/2026-06-29-duplicating-a-screen-now-gives-the-copy-its-own-layer-ids-so.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-29-element-selection-no-longer-snaps-back-to-the-previous-layer.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-29-frame-resize-measurements-now-stay-beside-the-cursor-while-d.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-29-hiding-and-re-showing-a-gradient-fill-now-preserves-each-sto.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-29-inspector-number-fields-have-cleaner-tooltips-and-draggable-.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-29-layer-locking-layer-nesting-generated-question-intake-and-im.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-29-layer-names-in-the-sidebar-can-scroll-horizontally-instead-o.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-29-layer-nesting-in-the-sidebar-now-uses-tighter-spacing-so-nam.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-29-layers-no-longer-repeat-document-wrapper-rows-when-a-screen-.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-29-overview-previews-now-keep-element-hover-and-selection-tied-.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-29-picking-a-design-direction-now-submits-to-the-agent-right-aw.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-29-pressing-escape-now-cancels-a-stuck-screen-drag-in-the-overv.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +6 -0
  160. package/corpus/templates/design/changelog/2026-06-29-screen-overview-only-highlights-screens-when-the-frame-or-ti.md +6 -0
  161. package/corpus/templates/design/changelog/2026-06-29-screen-overview-previews-now-resize-to-match-the-selected-de.md +6 -0
  162. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-now-uses-a-pointer-cursor-instead-.md +6 -0
  163. package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-outlines-now-stay-locked-to-the-se.md +6 -0
  164. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zoom-now-reports-real-screen-scale-separatel.md +6 -0
  165. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-feels-smoother-and-selection-outline.md +6 -0
  166. package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-now-stays-on-the-overview-canvas-ins.md +6 -0
  167. package/corpus/templates/design/changelog/2026-06-29-selecting-a-nested-layer-from-all-screens-now-keeps-you-in-a.md +6 -0
  168. package/corpus/templates/design/changelog/2026-06-29-share-now-groups-link-sharing-exports-and-agent-handoff.md +6 -0
  169. package/corpus/templates/design/changelog/2026-06-29-style-changes-that-set-several-properties-at-once-fixed-size.md +6 -0
  170. package/corpus/templates/design/changelog/2026-06-29-the-design-editor-loading-skeleton-now-uses-a-softer-charcoa.md +6 -0
  171. package/corpus/templates/design/changelog/2026-06-29-the-device-preview-menu-now-sits-next-to-the-preview-button-.md +6 -0
  172. package/corpus/templates/design/changelog/2026-06-29-the-editor-no-longer-crashes-when-a-screen-ends-up-with-dupl.md +6 -0
  173. package/corpus/templates/design/changelog/2026-06-29-the-inspector-stays-read-only-while-an-empty-design-is-still.md +6 -0
  174. package/corpus/templates/design/changelog/2026-06-29-the-missing-design-view-now-matches-the-rest-of-the-editor-c.md +6 -0
  175. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-icon-and-tool-option-menus-better-match-the-edi.md +6 -0
  176. package/corpus/templates/design/changelog/2026-06-29-typography-controls-now-keep-the-font-name-visible-and-move-.md +6 -0
  177. package/corpus/templates/design/e2e/README.md +4 -3
  178. package/corpus/templates/design/e2e/global-setup.ts +24 -23
  179. package/corpus/templates/design/e2e/helpers.ts +122 -11
  180. package/corpus/templates/design/playwright.config.ts +19 -4
  181. package/corpus/templates/design/server/lib/coding-handoff.ts +126 -2
  182. package/corpus/templates/design/server/plugins/core-routes.ts +1 -2
  183. package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
  184. package/corpus/templates/design/shared/code-layer.ts +125 -22
  185. package/corpus/templates/design/shared/color-utils.ts +159 -0
  186. package/corpus/templates/design/shared/generation-session.ts +42 -0
  187. package/corpus/templates/dispatch/app/global.css +79 -35
  188. package/corpus/templates/dispatch/app/routes/integrations.tsx +8 -7
  189. package/corpus/templates/dispatch/changelog/2026-06-29-integration-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  190. package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -2
  191. package/corpus/templates/forms/app/global.css +64 -35
  192. package/corpus/templates/forms/app/pages/FormsListPage.tsx +2 -2
  193. package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +3 -3
  194. package/corpus/templates/forms/changelog/2026-06-29-form-list-and-insight-layouts-adapt-when-the-agent-sidebar.md +6 -0
  195. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +3 -1
  196. package/corpus/templates/macros/app/global.css +25 -9
  197. package/corpus/templates/macros/changelog/2026-06-29-macro-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  198. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -2
  199. package/corpus/templates/mail/app/global.css +42 -36
  200. package/corpus/templates/mail/app/pages/InboxPage.tsx +1 -1
  201. package/corpus/templates/mail/changelog/2026-06-29-the-contact-panel-now-adapts-to-the-available-mail-pane-width.md +6 -0
  202. package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -1
  203. package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +6 -7
  204. package/corpus/templates/plan/app/global.css +74 -42
  205. package/corpus/templates/plan/changelog/2026-06-29-plan-document-blocks-adapt-to-the-available-document-width.md +6 -0
  206. package/corpus/templates/plan/changelog/2026-06-29-plan-titles-and-summaries-can-be-edited-inline-without-focus.md +6 -0
  207. package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -1
  208. package/corpus/templates/slides/app/global.css +49 -33
  209. package/corpus/templates/slides/changelog/2026-06-29-slide-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  210. package/corpus/templates/videos/app/components/layout/Layout.tsx +3 -1
  211. package/corpus/templates/videos/app/global.css +35 -35
  212. package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
  213. package/dist/cli/mcp.d.ts.map +1 -1
  214. package/dist/cli/mcp.js +10 -0
  215. package/dist/cli/mcp.js.map +1 -1
  216. package/dist/cli/skills.d.ts.map +1 -1
  217. package/dist/cli/skills.js +58 -12
  218. package/dist/cli/skills.js.map +1 -1
  219. package/dist/client/AssistantChat.d.ts.map +1 -1
  220. package/dist/client/AssistantChat.js +54 -24
  221. package/dist/client/AssistantChat.js.map +1 -1
  222. package/dist/client/analytics.d.ts.map +1 -1
  223. package/dist/client/analytics.js +2 -1
  224. package/dist/client/analytics.js.map +1 -1
  225. package/dist/client/error-format.d.ts.map +1 -1
  226. package/dist/client/error-format.js +17 -0
  227. package/dist/client/error-format.js.map +1 -1
  228. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  229. package/dist/client/extensions/ExtensionViewer.js +3 -4
  230. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  231. package/dist/client/guided-questions.d.ts +4 -0
  232. package/dist/client/guided-questions.d.ts.map +1 -1
  233. package/dist/client/guided-questions.js +16 -7
  234. package/dist/client/guided-questions.js.map +1 -1
  235. package/dist/client/onboarding/OnboardingPanel.js +26 -1
  236. package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
  237. package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -1
  238. package/dist/client/resources/BuiltinCapabilityDetail.js +1 -1
  239. package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -1
  240. package/dist/client/resources/use-builtin-capabilities.d.ts +1 -1
  241. package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -1
  242. package/dist/client/resources/use-builtin-capabilities.js +2 -1
  243. package/dist/client/resources/use-builtin-capabilities.js.map +1 -1
  244. package/dist/client/session-replay.d.ts.map +1 -1
  245. package/dist/client/session-replay.js +31 -0
  246. package/dist/client/session-replay.js.map +1 -1
  247. package/dist/client/sharing/ShareButton.d.ts +14 -0
  248. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  249. package/dist/client/sharing/ShareButton.js +36 -7
  250. package/dist/client/sharing/ShareButton.js.map +1 -1
  251. package/dist/coding-tools/index.js +1 -1
  252. package/dist/coding-tools/index.js.map +1 -1
  253. package/dist/collab/routes.d.ts +1 -1
  254. package/dist/mcp/index.d.ts +2 -0
  255. package/dist/mcp/index.d.ts.map +1 -1
  256. package/dist/mcp/index.js +1 -0
  257. package/dist/mcp/index.js.map +1 -1
  258. package/dist/mcp/screen-memory-stdio.d.ts +6 -0
  259. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -0
  260. package/dist/mcp/screen-memory-stdio.js +213 -0
  261. package/dist/mcp/screen-memory-stdio.js.map +1 -0
  262. package/dist/mcp-client/builtin-capabilities.d.ts +1 -1
  263. package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -1
  264. package/dist/mcp-client/builtin-capabilities.js +10 -0
  265. package/dist/mcp-client/builtin-capabilities.js.map +1 -1
  266. package/dist/mcp-client/index.d.ts +1 -0
  267. package/dist/mcp-client/index.d.ts.map +1 -1
  268. package/dist/mcp-client/index.js +1 -0
  269. package/dist/mcp-client/index.js.map +1 -1
  270. package/dist/mcp-client/screen-memory-local.d.ts +61 -0
  271. package/dist/mcp-client/screen-memory-local.d.ts.map +1 -0
  272. package/dist/mcp-client/screen-memory-local.js +336 -0
  273. package/dist/mcp-client/screen-memory-local.js.map +1 -0
  274. package/dist/onboarding/types.d.ts +7 -0
  275. package/dist/onboarding/types.d.ts.map +1 -1
  276. package/dist/onboarding/types.js.map +1 -1
  277. package/dist/resources/handlers.d.ts +2 -2
  278. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  279. package/dist/server/agent-chat-plugin.js +26 -0
  280. package/dist/server/agent-chat-plugin.js.map +1 -1
  281. package/dist/server/agents-bundle.d.ts.map +1 -1
  282. package/dist/server/agents-bundle.js +22 -7
  283. package/dist/server/agents-bundle.js.map +1 -1
  284. package/dist/server/transcribe-voice.d.ts +1 -1
  285. package/dist/styles/agent-native.css +5 -0
  286. package/dist/templates/default/app/global.css +35 -35
  287. package/docs/content/locales/ar-SA/template-design.mdx +6 -0
  288. package/docs/content/locales/de-DE/template-design.mdx +8 -0
  289. package/docs/content/locales/es-ES/template-design.mdx +10 -2
  290. package/docs/content/locales/fr-FR/template-design.mdx +10 -2
  291. package/docs/content/locales/hi-IN/template-design.mdx +7 -0
  292. package/docs/content/locales/ja-JP/template-design.mdx +7 -0
  293. package/docs/content/locales/ko-KR/template-design.mdx +7 -0
  294. package/docs/content/locales/pt-BR/template-design.mdx +9 -2
  295. package/docs/content/locales/zh-CN/template-design.mdx +6 -0
  296. package/docs/content/locales/zh-TW/template-design.mdx +6 -0
  297. package/docs/content/template-design.mdx +9 -2
  298. package/package.json +1 -1
  299. package/src/templates/default/app/global.css +35 -35
  300. package/corpus/templates/design/app/components/design/VariantGrid.tsx +0 -189
  301. package/corpus/templates/design/app/components/design/VariantHandoffCard.tsx +0 -115
  302. package/corpus/templates/design/app/hooks/use-variant-flow.ts +0 -258
@@ -15,6 +15,12 @@ import { defineConfig, devices } from "@playwright/test";
15
15
  */
16
16
  const PORT = Number(process.env.E2E_PORT ?? 9333);
17
17
  const BASE_URL = process.env.E2E_BASE_URL ?? `http://127.0.0.1:${PORT}`;
18
+ const E2E_DATABASE_URL = `file:${path.join(
19
+ import.meta.dirname,
20
+ "data",
21
+ "e2e.db",
22
+ )}`;
23
+ const BROWSER_CHANNEL = process.env.E2E_BROWSER_CHANNEL;
18
24
 
19
25
  export default defineConfig({
20
26
  testDir: "./e2e",
@@ -33,14 +39,23 @@ export default defineConfig({
33
39
  actionTimeout: 15_000,
34
40
  navigationTimeout: 30_000,
35
41
  },
36
- projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
42
+ projects: [
43
+ {
44
+ name: BROWSER_CHANNEL ? `chromium-${BROWSER_CHANNEL}` : "chromium",
45
+ use: {
46
+ ...devices["Desktop Chrome"],
47
+ ...(BROWSER_CHANNEL ? { channel: BROWSER_CHANNEL } : {}),
48
+ },
49
+ },
50
+ ],
37
51
  webServer: process.env.E2E_BASE_URL
38
52
  ? undefined
39
53
  : {
40
54
  // APP_NAME + the app-prefixed DESIGN_DATABASE_URL is checked BEFORE the
41
- // generic DATABASE_URL, so a `.env` Postgres URL can never override this
42
- // throwaway local SQLite db.
43
- command: `APP_NAME=design DESIGN_DATABASE_URL="file:./data/e2e.db" PORT=${PORT} pnpm dev`,
55
+ // generic DATABASE_URL, but set both to an absolute SQLite URL so a
56
+ // `.env` Postgres URL or a changed command cwd can never override this
57
+ // throwaway local db.
58
+ command: `APP_NAME=design DESIGN_DATABASE_URL=${JSON.stringify(E2E_DATABASE_URL)} DATABASE_URL=${JSON.stringify(E2E_DATABASE_URL)} PORT=${PORT} corepack pnpm dev`,
44
59
  url: BASE_URL,
45
60
  reuseExistingServer: !process.env.CI,
46
61
  // Cold Vite dep-optimization on first boot can exceed two minutes.
@@ -82,6 +82,23 @@ export function buildRawHandoffUrl({
82
82
  return normalizedOrigin ? `${normalizedOrigin}${path}` : path;
83
83
  }
84
84
 
85
+ export function buildHandoffZipUrl({
86
+ id,
87
+ token,
88
+ origin,
89
+ }: {
90
+ id: string;
91
+ token: string;
92
+ origin?: string | null;
93
+ }): string {
94
+ const params = new URLSearchParams({ token });
95
+ const path = appPath(
96
+ `/api/design-handoff/${encodeURIComponent(id)}.zip?${params.toString()}`,
97
+ );
98
+ const normalizedOrigin = normalizeHandoffOrigin(origin);
99
+ return normalizedOrigin ? `${normalizedOrigin}${path}` : path;
100
+ }
101
+
85
102
  function parseDesignData(data?: string | null): Record<string, unknown> {
86
103
  if (!data) return {};
87
104
  try {
@@ -301,20 +318,127 @@ export function buildDesignHandoffMarkdown(
301
318
  return `${lines.join("\n")}\n`;
302
319
  }
303
320
 
321
+ function safeZipBaseName(title?: string | null): string {
322
+ const safe = (title || "design")
323
+ .replace(/[^a-zA-Z0-9_-]/g, "-")
324
+ .replace(/-+/g, "-")
325
+ .replace(/^-|-$/g, "");
326
+ return safe || "design";
327
+ }
328
+
329
+ export function buildHandoffZipFilename(title?: string | null): string {
330
+ return `${safeZipBaseName(title)}-agent-handoff.zip`;
331
+ }
332
+
333
+ function safeZipPath(filename: string, fallback: string): string {
334
+ const normalized = filename
335
+ .replace(/\\/g, "/")
336
+ .split("/")
337
+ .map((part) => part.trim())
338
+ .filter((part) => part && part !== "." && part !== "..")
339
+ .join("/");
340
+ const withoutControlCharacters = normalized.replace(/[\x00-\x1f\x7f]/g, "");
341
+ return withoutControlCharacters || fallback;
342
+ }
343
+
344
+ function uniqueZipPath(path: string, seen: Set<string>): string {
345
+ if (!seen.has(path)) {
346
+ seen.add(path);
347
+ return path;
348
+ }
349
+ const slash = path.lastIndexOf("/");
350
+ const dir = slash === -1 ? "" : `${path.slice(0, slash + 1)}`;
351
+ const base = slash === -1 ? path : path.slice(slash + 1);
352
+ const dot = base.lastIndexOf(".");
353
+ const name = dot === -1 ? base : base.slice(0, dot);
354
+ const ext = dot === -1 ? "" : base.slice(dot);
355
+ let index = 2;
356
+ while (seen.has(`${dir}${name}-${index}${ext}`)) index += 1;
357
+ const next = `${dir}${name}-${index}${ext}`;
358
+ seen.add(next);
359
+ return next;
360
+ }
361
+
362
+ export async function buildDesignHandoffZip(
363
+ payload: DesignHandoffPayload,
364
+ ): Promise<Uint8Array> {
365
+ const JSZip = (await import("jszip")).default;
366
+ const zip = new JSZip();
367
+ const seenPaths = new Set<string>();
368
+
369
+ const readme = [
370
+ `# ${payload.design.title}`,
371
+ "",
372
+ "This archive contains the source files exported from Agent-Native Design.",
373
+ "Use the files as the source of truth for layout, typography, colors, spacing, copy, and interactions.",
374
+ "",
375
+ `Design ID: ${payload.design.id}`,
376
+ `Exported: ${payload.exportedAt}`,
377
+ payload.design.projectType
378
+ ? `Project type: ${payload.design.projectType}`
379
+ : null,
380
+ "",
381
+ "## Files",
382
+ "",
383
+ ...payload.files.map((file) => `- ${file.filename} (${file.fileType})`),
384
+ ]
385
+ .filter((line): line is string => line !== null)
386
+ .join("\n");
387
+
388
+ zip.file(uniqueZipPath("README.md", seenPaths), readme);
389
+ zip.file(
390
+ uniqueZipPath("design-handoff.json", seenPaths),
391
+ JSON.stringify(
392
+ {
393
+ exportedAt: payload.exportedAt,
394
+ design: payload.design,
395
+ files: payload.files.map((file) => ({
396
+ filename: file.filename,
397
+ fileType: file.fileType,
398
+ })),
399
+ appliedDesignTokens: payload.appliedDesignTokens,
400
+ },
401
+ null,
402
+ 2,
403
+ ),
404
+ );
405
+
406
+ payload.files.forEach((file, index) => {
407
+ const path = uniqueZipPath(
408
+ safeZipPath(file.filename, `file-${index + 1}.${file.fileType || "txt"}`),
409
+ seenPaths,
410
+ );
411
+ zip.file(path, file.content);
412
+ });
413
+
414
+ return zip.generateAsync({ type: "uint8array" });
415
+ }
416
+
304
417
  export function buildCodingHandoffPrompt({
305
418
  rawUrl,
419
+ zipUrl,
306
420
  title,
307
421
  fileCount,
308
422
  }: {
309
423
  rawUrl: string;
424
+ zipUrl?: string;
310
425
  title: string;
311
426
  fileCount: number;
312
427
  }): string {
313
- return [
428
+ const lines = [
314
429
  `Build this design as production code: ${title}`,
315
430
  "",
316
431
  `Fetch the raw design bundle here: ${rawUrl}`,
432
+ ];
433
+ if (zipUrl) {
434
+ lines.push(
435
+ "",
436
+ `If you prefer files, download the ZIP bundle here: ${zipUrl}`,
437
+ );
438
+ }
439
+ lines.push(
317
440
  "",
318
441
  `The bundle contains ${fileCount} file${fileCount === 1 ? "" : "s"} with the exact HTML/CSS/JSX source from the Design app. Use it as the source of truth for layout, typography, colors, spacing, responsive behavior, copy, and interactions. Convert it into the target project stack or Builder.io page/component while preserving the visual intent. If multiple screens are included, implement the primary page first and map the rest to routes, sections, or components as appropriate.`,
319
- ].join("\n");
442
+ );
443
+ return lines.join("\n");
320
444
  }
@@ -6,8 +6,7 @@ import { createCoreRoutesPlugin } from "@agent-native/core/server";
6
6
  // a resolveOpenPath, `/_agent-native/open?app=design&view=editor&designId=…`
7
7
  // falls back to `/<view>` = `/editor`, which has no matching route (the
8
8
  // editor route is `design.$id.tsx` → `/design/:id`) and 404s — so an
9
- // "Open in Design" link for a connected Claude Code / Codex / Cowork never
10
- // opened the design.
9
+ // "Open in Design" link for a connected external agent never opened the design.
11
10
  export default createCoreRoutesPlugin({
12
11
  resolveOpenPath: ({ view, params }) => {
13
12
  if (params.designId) return `/design/${params.designId}`;
@@ -0,0 +1,86 @@
1
+ import { verifyShortLivedToken } from "@agent-native/core/server";
2
+ import { eq } from "drizzle-orm";
3
+ import {
4
+ defineEventHandler,
5
+ getQuery,
6
+ getRouterParam,
7
+ setResponseHeader,
8
+ setResponseStatus,
9
+ type H3Event,
10
+ } from "h3";
11
+
12
+ import { getDb, schema } from "../../../db/index.js";
13
+ import {
14
+ buildDesignHandoffPayload,
15
+ buildDesignHandoffZip,
16
+ buildHandoffZipFilename,
17
+ } from "../../../lib/coding-handoff.js";
18
+ import { buildDesignSnapshot } from "../../../lib/design-snapshot.js";
19
+
20
+ function notFound(event: H3Event) {
21
+ setResponseStatus(event, 404);
22
+ return { error: "Not found" };
23
+ }
24
+
25
+ function normalizeRouteId(value: string | undefined): string | undefined {
26
+ if (!value) return value;
27
+ return value.endsWith(".zip") ? value.slice(0, -4) : value;
28
+ }
29
+
30
+ export default defineEventHandler(async (event: H3Event) => {
31
+ const id = normalizeRouteId(getRouterParam(event, "id"));
32
+ if (!id) {
33
+ setResponseStatus(event, 400);
34
+ return { error: "Design ID is required" };
35
+ }
36
+
37
+ const q = getQuery(event) as {
38
+ token?: string;
39
+ t?: string;
40
+ };
41
+ const token =
42
+ typeof q.token === "string" ? q.token : typeof q.t === "string" ? q.t : "";
43
+
44
+ const verified = verifyShortLivedToken(token, id);
45
+ if (!verified.ok) {
46
+ setResponseStatus(event, 403);
47
+ return { error: "Invalid or expired handoff link" };
48
+ }
49
+
50
+ const db = getDb();
51
+ // guard:allow-unscoped — this unauthenticated endpoint is gated by a signed, expiring handoff token bound to the design id.
52
+ const [design] = await db
53
+ .select()
54
+ .from(schema.designs)
55
+ .where(eq(schema.designs.id, id))
56
+ .limit(1);
57
+ if (!design) return notFound(event);
58
+
59
+ const snapshot = await buildDesignSnapshot(id, design.data);
60
+ if (snapshot.files.length === 0) return notFound(event);
61
+
62
+ const payload = buildDesignHandoffPayload({
63
+ design,
64
+ files: snapshot.files.map((f) => ({
65
+ filename: f.filename,
66
+ fileType: f.fileType,
67
+ content: f.content,
68
+ })),
69
+ resolvedCssVars: snapshot.resolvedCssVars,
70
+ });
71
+ const zip = await buildDesignHandoffZip(payload);
72
+ const filename = buildHandoffZipFilename(design.title);
73
+
74
+ setResponseHeader(event, "Cache-Control", "no-store");
75
+ setResponseHeader(event, "Referrer-Policy", "no-referrer");
76
+ setResponseHeader(event, "X-Content-Type-Options", "nosniff");
77
+ setResponseHeader(event, "Access-Control-Allow-Origin", "*");
78
+ setResponseHeader(event, "Content-Type", "application/zip");
79
+ setResponseHeader(
80
+ event,
81
+ "Content-Disposition",
82
+ `attachment; filename="${filename}"; filename*=UTF-8''${encodeURIComponent(filename)}`,
83
+ );
84
+
85
+ return zip;
86
+ });
@@ -1485,6 +1485,49 @@ function treeTypeForNode(node: CodeLayerNode): CodeLayerTreeNodeType {
1485
1485
  return "element";
1486
1486
  }
1487
1487
 
1488
+ function isCollapsibleDocumentShellNode(
1489
+ node: CodeLayerTreeNode,
1490
+ nodesById: Map<string, CodeLayerNode>,
1491
+ ): boolean {
1492
+ if (node.tag !== "html" && node.tag !== "body") return false;
1493
+ return nodesById.get(node.id)?.layerNameSource === "tag";
1494
+ }
1495
+
1496
+ function compactCodeLayerTreeNodes(
1497
+ nodes: CodeLayerTreeNode[],
1498
+ nodesById: Map<string, CodeLayerNode>,
1499
+ ancestors: Set<string> = new Set(),
1500
+ ): CodeLayerTreeNode[] {
1501
+ const compacted: CodeLayerTreeNode[] = [];
1502
+ const siblingIds = new Set<string>();
1503
+
1504
+ for (const node of nodes) {
1505
+ if (ancestors.has(node.id)) continue;
1506
+
1507
+ const nextAncestors = new Set(ancestors);
1508
+ nextAncestors.add(node.id);
1509
+ const children = compactCodeLayerTreeNodes(
1510
+ node.children,
1511
+ nodesById,
1512
+ nextAncestors,
1513
+ );
1514
+ const compactedNode: CodeLayerTreeNode = { ...node, children };
1515
+ const promotedNodes =
1516
+ isCollapsibleDocumentShellNode(compactedNode, nodesById) &&
1517
+ children.length > 0
1518
+ ? children
1519
+ : [compactedNode];
1520
+
1521
+ for (const promotedNode of promotedNodes) {
1522
+ if (siblingIds.has(promotedNode.id)) continue;
1523
+ siblingIds.add(promotedNode.id);
1524
+ compacted.push(promotedNode);
1525
+ }
1526
+ }
1527
+
1528
+ return compacted;
1529
+ }
1530
+
1488
1531
  function capabilitiesFor(element: ParsedElement): EditCapability[] {
1489
1532
  const capabilities: EditCapability[] = [
1490
1533
  {
@@ -1768,26 +1811,36 @@ export function buildCodeLayerTree(
1768
1811
  });
1769
1812
  }
1770
1813
 
1814
+ const childIdsByParentId = new Map<string, Set<string>>();
1771
1815
  for (const node of projection.nodes) {
1772
1816
  const parent =
1773
1817
  node.parentId && nodesById.has(node.parentId)
1774
1818
  ? treeById.get(node.parentId)
1775
1819
  : undefined;
1776
1820
  const treeNode = treeById.get(node.id);
1777
- if (parent && treeNode) parent.children.push(treeNode);
1778
- }
1821
+ if (!parent || !treeNode || parent.id === treeNode.id) continue;
1822
+ const childIds = childIdsByParentId.get(parent.id) ?? new Set<string>();
1823
+ if (childIds.has(treeNode.id)) continue;
1824
+ childIds.add(treeNode.id);
1825
+ childIdsByParentId.set(parent.id, childIds);
1826
+ parent.children.push(treeNode);
1827
+ }
1828
+
1829
+ const roots: CodeLayerTreeNode[] = [];
1830
+ const rootIds = new Set<string>();
1831
+ const appendRoot = (id: string) => {
1832
+ if (rootIds.has(id)) return;
1833
+ const treeNode = treeById.get(id);
1834
+ if (!treeNode) return;
1835
+ rootIds.add(id);
1836
+ roots.push(treeNode);
1837
+ };
1779
1838
 
1780
- const roots = projection.rootNodeIds
1781
- .map((id) => treeById.get(id))
1782
- .filter((node): node is CodeLayerTreeNode => Boolean(node));
1783
- const rootIds = new Set(roots.map((node) => node.id));
1839
+ projection.rootNodeIds.forEach(appendRoot);
1784
1840
  for (const node of projection.nodes) {
1785
- if (!node.parentId && !rootIds.has(node.id)) {
1786
- const treeNode = treeById.get(node.id);
1787
- if (treeNode) roots.push(treeNode);
1788
- }
1841
+ if (!node.parentId) appendRoot(node.id);
1789
1842
  }
1790
- return roots;
1843
+ return compactCodeLayerTreeNodes(roots, nodesById);
1791
1844
  }
1792
1845
 
1793
1846
  function normalizeSelectorForMatch(selector: string): string {
@@ -1797,6 +1850,26 @@ function normalizeSelectorForMatch(selector: string): string {
1797
1850
  .replace(/\s+/g, " ");
1798
1851
  }
1799
1852
 
1853
+ function selectorPartTag(selectorPart: string): string | null {
1854
+ const match = selectorPart.trim().match(/^([A-Za-z][A-Za-z0-9:-]*)/);
1855
+ return match?.[1]?.toLowerCase() ?? null;
1856
+ }
1857
+
1858
+ function isDocumentRootSelectorPart(selectorPart: string): boolean {
1859
+ const tag = selectorPartTag(selectorPart);
1860
+ return tag === "html" || tag === "body";
1861
+ }
1862
+
1863
+ // Removes positional `:nth-of-type(n)` suffixes from a selector. Runtime
1864
+ // bridge selectors fall back to `:nth-of-type` for elements without a durable
1865
+ // id, and that position is computed against the live (possibly Alpine-mutated)
1866
+ // DOM. When the stored source order differs, the positional index no longer
1867
+ // lines up. Dropping the suffix lets resolution fall back to the element's
1868
+ // stable signal (tag, classes, attributes, ancestor path).
1869
+ function stripPositionalNthOfType(selector: string): string {
1870
+ return selector.replace(/:nth-of-type\(\d+\)/g, "");
1871
+ }
1872
+
1800
1873
  function lastSelectorPart(selector: string): string {
1801
1874
  const parts = normalizeSelectorForMatch(selector).split(" > ");
1802
1875
  return parts[parts.length - 1] ?? selector;
@@ -1926,7 +1999,11 @@ function selectorPathMatchesElement(
1926
1999
  let current: ParsedElement | undefined = element;
1927
2000
  for (let index = parts.length - 1; index >= 0; index -= 1) {
1928
2001
  const selectorPart = parts[index];
1929
- if (!current || !selectorPart) return false;
2002
+ if (!selectorPart) return false;
2003
+ if (!current) {
2004
+ if (isDocumentRootSelectorPart(selectorPart)) continue;
2005
+ return false;
2006
+ }
1930
2007
  if (!simpleSelectorMatchesElement(current, selectorPart)) return false;
1931
2008
  current =
1932
2009
  current.parentIndex === undefined
@@ -2024,26 +2101,52 @@ function resolveTarget(
2024
2101
  };
2025
2102
  }
2026
2103
 
2027
- const matches = projection.nodes.filter((node) =>
2028
- selectorMatches(
2029
- node,
2030
- target.selector ?? "",
2031
- elementByNodeId.get(node.id),
2032
- elementByIndex,
2033
- ),
2034
- );
2104
+ const selectorValue = target.selector ?? "";
2105
+ const matchesForSelector = (value: string): CodeLayerNode[] =>
2106
+ projection.nodes.filter((node) =>
2107
+ selectorMatches(
2108
+ node,
2109
+ value,
2110
+ elementByNodeId.get(node.id),
2111
+ elementByIndex,
2112
+ ),
2113
+ );
2114
+
2115
+ const matches = matchesForSelector(selectorValue);
2035
2116
  if (matches.length === 1 && matches[0]) {
2036
2117
  return { status: "resolved", node: matches[0] };
2037
2118
  }
2038
2119
  if (matches.length > 1) {
2039
2120
  return {
2040
2121
  status: "conflict",
2041
- message: `Selector "${target.selector}" matched ${matches.length} code layer nodes.`,
2122
+ message: `Selector "${selectorValue}" matched ${matches.length} code layer nodes.`,
2042
2123
  };
2043
2124
  }
2125
+
2126
+ // Strict matching found nothing. Runtime selectors anchor unstamped elements
2127
+ // with positional `:nth-of-type(n)` parts, which drift when the live DOM
2128
+ // order differs from the stored source (reordered or runtime-inserted nodes).
2129
+ // Retry once with the positional suffixes dropped so an element that is still
2130
+ // unique by its tag/classes/attributes resolves instead of surfacing a hard
2131
+ // "did not match" error. Genuinely ambiguous results stay a conflict rather
2132
+ // than silently editing the wrong node.
2133
+ const positionTolerantSelector = stripPositionalNthOfType(selectorValue);
2134
+ if (positionTolerantSelector && positionTolerantSelector !== selectorValue) {
2135
+ const tolerantMatches = matchesForSelector(positionTolerantSelector);
2136
+ if (tolerantMatches.length === 1 && tolerantMatches[0]) {
2137
+ return { status: "resolved", node: tolerantMatches[0] };
2138
+ }
2139
+ if (tolerantMatches.length > 1) {
2140
+ return {
2141
+ status: "conflict",
2142
+ message: `Selector "${selectorValue}" matched ${tolerantMatches.length} code layer nodes after ignoring positional :nth-of-type (the element may have been reordered or added at runtime). Re-select the element to refresh its id.`,
2143
+ };
2144
+ }
2145
+ }
2146
+
2044
2147
  return {
2045
2148
  status: "conflict",
2046
- message: `Selector "${target.selector}" did not match a code layer node.`,
2149
+ message: `Selector "${selectorValue}" did not match a code layer node.`,
2047
2150
  };
2048
2151
  }
2049
2152
 
@@ -17,6 +17,160 @@ const RGB_PATTERN =
17
17
  const HSL_PATTERN =
18
18
  /^hsla?\(\s*([0-9.]+)(?:deg)?\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%(?:\s*,\s*([0-9.]+%?))?\s*\)$/i;
19
19
 
20
+ // CSS named colors → hex, so detection/round-tripping works for shadows, gradient
21
+ // stops, and the color picker (not just #hex/rgb()/hsl()). `transparent` is handled
22
+ // separately as fully transparent black.
23
+ const NAMED_COLOR_HEX: Record<string, string> = {
24
+ aliceblue: "#f0f8ff",
25
+ antiquewhite: "#faebd7",
26
+ aqua: "#00ffff",
27
+ aquamarine: "#7fffd4",
28
+ azure: "#f0ffff",
29
+ beige: "#f5f5dc",
30
+ bisque: "#ffe4c4",
31
+ black: "#000000",
32
+ blanchedalmond: "#ffebcd",
33
+ blue: "#0000ff",
34
+ blueviolet: "#8a2be2",
35
+ brown: "#a52a2a",
36
+ burlywood: "#deb887",
37
+ cadetblue: "#5f9ea0",
38
+ chartreuse: "#7fff00",
39
+ chocolate: "#d2691e",
40
+ coral: "#ff7f50",
41
+ cornflowerblue: "#6495ed",
42
+ cornsilk: "#fff8dc",
43
+ crimson: "#dc143c",
44
+ cyan: "#00ffff",
45
+ darkblue: "#00008b",
46
+ darkcyan: "#008b8b",
47
+ darkgoldenrod: "#b8860b",
48
+ darkgray: "#a9a9a9",
49
+ darkgreen: "#006400",
50
+ darkgrey: "#a9a9a9",
51
+ darkkhaki: "#bdb76b",
52
+ darkmagenta: "#8b008b",
53
+ darkolivegreen: "#556b2f",
54
+ darkorange: "#ff8c00",
55
+ darkorchid: "#9932cc",
56
+ darkred: "#8b0000",
57
+ darksalmon: "#e9967a",
58
+ darkseagreen: "#8fbc8f",
59
+ darkslateblue: "#483d8b",
60
+ darkslategray: "#2f4f4f",
61
+ darkslategrey: "#2f4f4f",
62
+ darkturquoise: "#00ced1",
63
+ darkviolet: "#9400d3",
64
+ deeppink: "#ff1493",
65
+ deepskyblue: "#00bfff",
66
+ dimgray: "#696969",
67
+ dimgrey: "#696969",
68
+ dodgerblue: "#1e90ff",
69
+ firebrick: "#b22222",
70
+ floralwhite: "#fffaf0",
71
+ forestgreen: "#228b22",
72
+ fuchsia: "#ff00ff",
73
+ gainsboro: "#dcdcdc",
74
+ ghostwhite: "#f8f8ff",
75
+ gold: "#ffd700",
76
+ goldenrod: "#daa520",
77
+ gray: "#808080",
78
+ green: "#008000",
79
+ greenyellow: "#adff2f",
80
+ grey: "#808080",
81
+ honeydew: "#f0fff0",
82
+ hotpink: "#ff69b4",
83
+ indianred: "#cd5c5c",
84
+ indigo: "#4b0082",
85
+ ivory: "#fffff0",
86
+ khaki: "#f0e68c",
87
+ lavender: "#e6e6fa",
88
+ lavenderblush: "#fff0f5",
89
+ lawngreen: "#7cfc00",
90
+ lemonchiffon: "#fffacd",
91
+ lightblue: "#add8e6",
92
+ lightcoral: "#f08080",
93
+ lightcyan: "#e0ffff",
94
+ lightgoldenrodyellow: "#fafad2",
95
+ lightgray: "#d3d3d3",
96
+ lightgreen: "#90ee90",
97
+ lightgrey: "#d3d3d3",
98
+ lightpink: "#ffb6c1",
99
+ lightsalmon: "#ffa07a",
100
+ lightseagreen: "#20b2aa",
101
+ lightskyblue: "#87cefa",
102
+ lightslategray: "#778899",
103
+ lightslategrey: "#778899",
104
+ lightsteelblue: "#b0c4de",
105
+ lightyellow: "#ffffe0",
106
+ lime: "#00ff00",
107
+ limegreen: "#32cd32",
108
+ linen: "#faf0e6",
109
+ magenta: "#ff00ff",
110
+ maroon: "#800000",
111
+ mediumaquamarine: "#66cdaa",
112
+ mediumblue: "#0000cd",
113
+ mediumorchid: "#ba55d3",
114
+ mediumpurple: "#9370db",
115
+ mediumseagreen: "#3cb371",
116
+ mediumslateblue: "#7b68ee",
117
+ mediumspringgreen: "#00fa9a",
118
+ mediumturquoise: "#48d1cc",
119
+ mediumvioletred: "#c71585",
120
+ midnightblue: "#191970",
121
+ mintcream: "#f5fffa",
122
+ mistyrose: "#ffe4e1",
123
+ moccasin: "#ffe4b5",
124
+ navajowhite: "#ffdead",
125
+ navy: "#000080",
126
+ oldlace: "#fdf5e6",
127
+ olive: "#808000",
128
+ olivedrab: "#6b8e23",
129
+ orange: "#ffa500",
130
+ orangered: "#ff4500",
131
+ orchid: "#da70d6",
132
+ palegoldenrod: "#eee8aa",
133
+ palegreen: "#98fb98",
134
+ paleturquoise: "#afeeee",
135
+ palevioletred: "#db7093",
136
+ papayawhip: "#ffefd5",
137
+ peachpuff: "#ffdab9",
138
+ peru: "#cd853f",
139
+ pink: "#ffc0cb",
140
+ plum: "#dda0dd",
141
+ powderblue: "#b0e0e6",
142
+ purple: "#800080",
143
+ rebeccapurple: "#663399",
144
+ red: "#ff0000",
145
+ rosybrown: "#bc8f8f",
146
+ royalblue: "#4169e1",
147
+ saddlebrown: "#8b4513",
148
+ salmon: "#fa8072",
149
+ sandybrown: "#f4a460",
150
+ seagreen: "#2e8b57",
151
+ seashell: "#fff5ee",
152
+ sienna: "#a0522d",
153
+ silver: "#c0c0c0",
154
+ skyblue: "#87ceeb",
155
+ slateblue: "#6a5acd",
156
+ slategray: "#708090",
157
+ slategrey: "#708090",
158
+ snow: "#fffafa",
159
+ springgreen: "#00ff7f",
160
+ steelblue: "#4682b4",
161
+ tan: "#d2b48c",
162
+ teal: "#008080",
163
+ thistle: "#d8bfd8",
164
+ tomato: "#ff6347",
165
+ turquoise: "#40e0d0",
166
+ violet: "#ee82ee",
167
+ wheat: "#f5deb3",
168
+ white: "#ffffff",
169
+ whitesmoke: "#f5f5f5",
170
+ yellow: "#ffff00",
171
+ yellowgreen: "#9acd32",
172
+ };
173
+
20
174
  export function parseCssColor(value: string): RgbaColor | null {
21
175
  const trimmed = value.trim();
22
176
  if (!trimmed) return null;
@@ -43,6 +197,11 @@ export function parseCssColor(value: string): RgbaColor | null {
43
197
  });
44
198
  }
45
199
 
200
+ const lower = trimmed.toLowerCase();
201
+ if (lower === "transparent") return { r: 0, g: 0, b: 0, a: 0 };
202
+ const namedHex = NAMED_COLOR_HEX[lower];
203
+ if (namedHex) return hexToRgba(namedHex);
204
+
46
205
  return null;
47
206
  }
48
207