@agent-native/core 0.80.2 → 0.80.5

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 (322) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +66 -2
  3. package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -1
  5. package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -1
  7. package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -1
  9. package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -1
  11. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -1
  12. package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
  13. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -1
  14. package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
  15. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -1
  16. package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
  17. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -1
  18. package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
  19. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -1
  20. package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
  21. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -1
  22. package/corpus/core/docs/content/real-time-collaboration.mdx +2 -2
  23. package/corpus/core/docs/content/template-design.mdx +1 -1
  24. package/corpus/core/package.json +3 -7
  25. package/corpus/core/src/action.ts +32 -0
  26. package/corpus/core/src/agent/durable-background.ts +32 -11
  27. package/corpus/core/src/agent/production-agent.ts +14 -1
  28. package/corpus/core/src/agent/run-loop-with-resume.ts +3 -1
  29. package/corpus/core/src/brand-kit/fig/index.ts +17 -37
  30. package/corpus/core/src/cli/index.ts +1 -1
  31. package/corpus/core/src/cli/mcp-config-writers.ts +187 -51
  32. package/corpus/core/src/cli/plan-local.ts +445 -108
  33. package/corpus/core/src/cli/skills.ts +32 -8
  34. package/corpus/core/src/client/AssistantChat.tsx +11 -1
  35. package/corpus/core/src/client/agent-chat.ts +12 -1
  36. package/corpus/core/src/client/composer/PromptComposer.tsx +100 -41
  37. package/corpus/core/src/client/org/OrgSwitcher.tsx +1 -1
  38. package/corpus/core/src/client/sharing/ShareButton.tsx +3 -27
  39. package/corpus/core/src/collab/presence.ts +1 -1
  40. package/corpus/core/src/mcp/connect-route.ts +34 -5
  41. package/corpus/core/src/server/action-discovery.ts +4 -0
  42. package/corpus/core/src/server/action-routes.ts +14 -0
  43. package/corpus/core/src/server/agent-chat-plugin.ts +20 -0
  44. package/corpus/core/src/vite/client.ts +15 -1
  45. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -0
  46. package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
  47. package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
  48. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +117 -33
  49. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  50. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
  51. package/corpus/templates/analytics/changelog/2026-06-27-dashboards-can-now-nest-under-a-parent-in-the-sidebar-via-a-.md +6 -0
  52. package/corpus/templates/analytics/changelog/2026-06-29-analytics-pages-now-sit-closer-under-the-header-with-tighter.md +6 -0
  53. package/corpus/templates/assets/actions/generate-image-batch.ts +3 -0
  54. package/corpus/templates/assets/actions/generate-image.ts +3 -0
  55. package/corpus/templates/assets/changelog/2026-06-29-longer-agent-generation-window.md +6 -0
  56. package/corpus/templates/assets/netlify.toml +3 -3
  57. package/corpus/templates/assets/server/plugins/agent-chat.ts +4 -1
  58. package/corpus/templates/clips/app/components/capture-install-options.tsx +7 -8
  59. package/corpus/templates/clips/app/components/player/timestamped-comment-button.tsx +162 -0
  60. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +1 -1
  61. package/corpus/templates/clips/app/i18n/ar-SA.ts +4 -0
  62. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -0
  63. package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
  64. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -0
  65. package/corpus/templates/clips/app/i18n/fr-FR.ts +4 -0
  66. package/corpus/templates/clips/app/i18n/hi-IN.ts +4 -0
  67. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -0
  68. package/corpus/templates/clips/app/i18n/ko-KR.ts +4 -0
  69. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -0
  70. package/corpus/templates/clips/app/i18n/zh-CN.ts +4 -0
  71. package/corpus/templates/clips/app/i18n/zh-TW.ts +4 -0
  72. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +42 -18
  73. package/corpus/templates/content/AGENTS.md +12 -0
  74. package/corpus/templates/content/app/blocks/BuilderDocsBlocks.tsx +10 -0
  75. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -0
  76. package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +185 -0
  77. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -0
  78. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +19 -0
  79. package/corpus/templates/content/app/components/editor/extensions/ContentReferenceNode.tsx +82 -0
  80. package/corpus/templates/content/app/global.css +9 -0
  81. package/corpus/templates/content/app/i18n/zh-TW.ts +12 -0
  82. package/corpus/templates/content/app/i18n-data.ts +140 -0
  83. package/corpus/templates/content/changelog/2026-06-29-reusable-mdx-references-now-preview-linked-local-documents-i.md +6 -0
  84. package/corpus/templates/content/shared/builder-docs-blocks.ts +8 -0
  85. package/corpus/templates/content/shared/builder-mdx.ts +141 -4
  86. package/corpus/templates/content/shared/nfm.ts +62 -0
  87. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +10 -11
  88. package/corpus/templates/design/AGENTS.md +5 -0
  89. package/corpus/templates/design/_gitignore +7 -0
  90. package/corpus/templates/design/actions/apply-visual-edit.ts +22 -12
  91. package/corpus/templates/design/actions/create-design-system.ts +15 -4
  92. package/corpus/templates/design/actions/create-file.ts +20 -1
  93. package/corpus/templates/design/actions/delete-design-system.ts +2 -2
  94. package/corpus/templates/design/actions/duplicate-design.ts +35 -9
  95. package/corpus/templates/design/actions/generate-design.ts +146 -69
  96. package/corpus/templates/design/actions/generate-screens.ts +51 -19
  97. package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
  98. package/corpus/templates/design/actions/get-design.ts +2 -0
  99. package/corpus/templates/design/actions/import-design-project.ts +28 -9
  100. package/corpus/templates/design/actions/import-document.ts +22 -8
  101. package/corpus/templates/design/actions/index-design-system-with-builder.ts +6 -153
  102. package/corpus/templates/design/actions/navigate.ts +105 -54
  103. package/corpus/templates/design/actions/set-default-design-system.ts +17 -3
  104. package/corpus/templates/design/actions/show-design-questions.ts +2 -0
  105. package/corpus/templates/design/actions/update-design.ts +2 -1
  106. package/corpus/templates/design/actions/view-screen.ts +12 -6
  107. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +4 -4
  108. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +159 -41
  109. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +8 -4
  110. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +37 -42
  111. package/corpus/templates/design/app/components/design/EditPanel.tsx +336 -183
  112. package/corpus/templates/design/app/components/design/LayersPanel.tsx +153 -34
  113. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +135 -33
  114. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +6 -0
  115. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  116. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +4 -10
  117. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +84 -43
  118. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +4 -4
  119. package/corpus/templates/design/app/components/design/inspector/{FigmaColorPicker.tsx → DesignColorPicker.tsx} +89 -62
  120. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +14 -26
  121. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +40 -12
  122. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -4
  123. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +13 -13
  124. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -9
  125. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +13 -4
  126. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
  127. package/corpus/templates/design/app/components/design/inspector/{figma-icons.tsx → design-icons.tsx} +2 -2
  128. package/corpus/templates/design/app/components/design/inspector/index.ts +12 -12
  129. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +5 -2
  130. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -3
  131. package/corpus/templates/design/app/components/layout/Layout.tsx +5 -2
  132. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  133. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +167 -91
  134. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +16 -3
  135. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +15 -0
  136. package/corpus/templates/design/app/global.css +4 -0
  137. package/corpus/templates/design/app/hooks/use-design-system.ts +4 -1
  138. package/corpus/templates/design/app/hooks/use-navigation-state.ts +38 -3
  139. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +9 -2
  140. package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
  141. package/corpus/templates/design/app/i18n-data.ts +527 -0
  142. package/corpus/templates/design/app/lib/design-systems.ts +6 -4
  143. package/corpus/templates/design/app/lib/design-templates.ts +15 -3
  144. package/corpus/templates/design/app/pages/DesignEditor.tsx +641 -158
  145. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +42 -34
  146. package/corpus/templates/design/app/pages/VisualEdit.tsx +100 -0
  147. package/corpus/templates/design/app/root.tsx +42 -9
  148. package/corpus/templates/design/app/routes/visual-edit.tsx +7 -0
  149. package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +1 -1
  150. package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +1 -1
  151. package/corpus/templates/design/changelog/2026-06-28-visual-code-design-mode.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-29-design-generation-prompts-now-show-connect-ai-before-sending.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +1 -1
  154. package/corpus/templates/design/changelog/2026-06-29-fixed-design-editor-exports-and-gradient-edits-so-multi-scre.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-29-png-export-handles-modern-css-color-functions-more-reliably.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-29-png-export-now-ignores-duplicate-clicks-while-a-download-is-.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-29-public-visual-edit-and-shared-design-links-now-invite-visito.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-29-visual-edit-actions-now-reject-missing-design-targets-before.md +6 -0
  160. package/corpus/templates/design/e2e/README.md +71 -0
  161. package/corpus/templates/design/e2e/global-setup.ts +157 -0
  162. package/corpus/templates/design/e2e/helpers.ts +148 -0
  163. package/corpus/templates/design/netlify.toml +3 -4
  164. package/corpus/templates/design/package.json +5 -1
  165. package/corpus/templates/design/playwright.config.ts +51 -0
  166. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +121 -0
  167. package/corpus/templates/design/server/handlers/uploads.ts +89 -38
  168. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +181 -0
  169. package/corpus/templates/design/server/lib/coding-handoff.ts +33 -4
  170. package/corpus/templates/design/server/lib/design-export.ts +35 -8
  171. package/corpus/templates/design/server/plugins/agent-chat.ts +4 -0
  172. package/corpus/templates/design/server/plugins/auth.ts +10 -1
  173. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  174. package/corpus/templates/design/shared/api.ts +2 -0
  175. package/corpus/templates/design/shared/apply-edits.ts +12 -8
  176. package/corpus/templates/design/shared/canvas-math.ts +19 -1
  177. package/corpus/templates/design/shared/code-layer.ts +87 -28
  178. package/corpus/templates/design/shared/generation-session.ts +1 -0
  179. package/corpus/templates/design/shared/resolve-tweaks.ts +4 -3
  180. package/corpus/templates/design/shared/shader-presets.ts +10 -0
  181. package/corpus/templates/design/shared/source-mode.ts +9 -0
  182. package/corpus/templates/design/vitest.config.ts +2 -1
  183. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
  184. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +1 -1
  185. package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +14 -5
  186. package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +14 -5
  187. package/corpus/templates/plan/AGENTS.md +1 -1
  188. package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
  189. package/corpus/templates/plan/actions/validate-local-plan-source.ts +110 -0
  190. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -0
  191. package/corpus/templates/plan/docs/commenting-ux-plan.md +2 -2
  192. package/corpus/templates/plan/server/lib/plan-connector-catalog.ts +1 -0
  193. package/corpus/templates/plan/server/lib/public-action-paths.ts +1 -0
  194. package/corpus/templates/plan/server/plan-content.ts +1 -1
  195. package/corpus/templates/plan/server/ui-plan-html.ts +2 -2
  196. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +6 -153
  197. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +53 -38
  198. package/corpus/templates/slides/app/components/design-system/{fig-import-response.ts → builder-index-response.ts} +9 -6
  199. package/corpus/templates/slides/app/components/visual-editor/MultiSelectChip.tsx +1 -1
  200. package/corpus/templates/slides/changelog/2026-06-29-design-system-setup-now-indexes-uploaded-design-files-throug.md +6 -0
  201. package/corpus/templates/slides/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  202. package/corpus/templates/slides/netlify.toml +3 -6
  203. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +116 -0
  204. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +179 -0
  205. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -1
  206. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  207. package/dist/action.d.ts +23 -0
  208. package/dist/action.d.ts.map +1 -1
  209. package/dist/action.js +9 -0
  210. package/dist/action.js.map +1 -1
  211. package/dist/agent/durable-background.d.ts +9 -7
  212. package/dist/agent/durable-background.d.ts.map +1 -1
  213. package/dist/agent/durable-background.js +27 -11
  214. package/dist/agent/durable-background.js.map +1 -1
  215. package/dist/agent/production-agent.d.ts +10 -0
  216. package/dist/agent/production-agent.d.ts.map +1 -1
  217. package/dist/agent/production-agent.js +4 -1
  218. package/dist/agent/production-agent.js.map +1 -1
  219. package/dist/agent/run-loop-with-resume.d.ts +2 -1
  220. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  221. package/dist/agent/run-loop-with-resume.js +2 -2
  222. package/dist/agent/run-loop-with-resume.js.map +1 -1
  223. package/dist/brand-kit/fig/index.d.ts +4 -4
  224. package/dist/brand-kit/fig/index.d.ts.map +1 -1
  225. package/dist/brand-kit/fig/index.js +14 -27
  226. package/dist/brand-kit/fig/index.js.map +1 -1
  227. package/dist/cli/index.js +1 -1
  228. package/dist/cli/index.js.map +1 -1
  229. package/dist/cli/mcp-config-writers.d.ts +15 -7
  230. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  231. package/dist/cli/mcp-config-writers.js +198 -45
  232. package/dist/cli/mcp-config-writers.js.map +1 -1
  233. package/dist/cli/plan-local.d.ts +30 -0
  234. package/dist/cli/plan-local.d.ts.map +1 -1
  235. package/dist/cli/plan-local.js +281 -56
  236. package/dist/cli/plan-local.js.map +1 -1
  237. package/dist/cli/skills.d.ts +2 -2
  238. package/dist/cli/skills.d.ts.map +1 -1
  239. package/dist/cli/skills.js +32 -8
  240. package/dist/cli/skills.js.map +1 -1
  241. package/dist/client/AssistantChat.d.ts.map +1 -1
  242. package/dist/client/AssistantChat.js +11 -1
  243. package/dist/client/AssistantChat.js.map +1 -1
  244. package/dist/client/agent-chat.d.ts.map +1 -1
  245. package/dist/client/agent-chat.js +15 -2
  246. package/dist/client/agent-chat.js.map +1 -1
  247. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  248. package/dist/client/composer/PromptComposer.js +35 -1
  249. package/dist/client/composer/PromptComposer.js.map +1 -1
  250. package/dist/client/org/OrgSwitcher.js +1 -1
  251. package/dist/client/org/OrgSwitcher.js.map +1 -1
  252. package/dist/client/sharing/ShareButton.d.ts +1 -1
  253. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  254. package/dist/client/sharing/ShareButton.js +2 -15
  255. package/dist/client/sharing/ShareButton.js.map +1 -1
  256. package/dist/collab/presence.d.ts +1 -1
  257. package/dist/collab/presence.js +1 -1
  258. package/dist/collab/presence.js.map +1 -1
  259. package/dist/collab/routes.d.ts +1 -1
  260. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  261. package/dist/mcp/connect-route.d.ts.map +1 -1
  262. package/dist/mcp/connect-route.js +34 -5
  263. package/dist/mcp/connect-route.js.map +1 -1
  264. package/dist/observability/routes.d.ts +2 -2
  265. package/dist/progress/routes.d.ts +1 -1
  266. package/dist/resources/handlers.d.ts +1 -1
  267. package/dist/server/action-discovery.d.ts.map +1 -1
  268. package/dist/server/action-discovery.js +5 -0
  269. package/dist/server/action-discovery.js.map +1 -1
  270. package/dist/server/action-routes.d.ts.map +1 -1
  271. package/dist/server/action-routes.js +14 -0
  272. package/dist/server/action-routes.js.map +1 -1
  273. package/dist/server/agent-chat-plugin.d.ts +6 -0
  274. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  275. package/dist/server/agent-chat-plugin.js +12 -3
  276. package/dist/server/agent-chat-plugin.js.map +1 -1
  277. package/dist/vite/client.d.ts.map +1 -1
  278. package/dist/vite/client.js +12 -1
  279. package/dist/vite/client.js.map +1 -1
  280. package/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
  281. package/docs/content/locales/ar-SA/template-design.mdx +1 -1
  282. package/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
  283. package/docs/content/locales/de-DE/template-design.mdx +1 -1
  284. package/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
  285. package/docs/content/locales/es-ES/template-design.mdx +1 -1
  286. package/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
  287. package/docs/content/locales/fr-FR/template-design.mdx +1 -1
  288. package/docs/content/locales/hi-IN/template-design.mdx +1 -1
  289. package/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
  290. package/docs/content/locales/ja-JP/template-design.mdx +1 -1
  291. package/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
  292. package/docs/content/locales/ko-KR/template-design.mdx +1 -1
  293. package/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
  294. package/docs/content/locales/pt-BR/template-design.mdx +1 -1
  295. package/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
  296. package/docs/content/locales/zh-CN/template-design.mdx +1 -1
  297. package/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
  298. package/docs/content/locales/zh-TW/template-design.mdx +1 -1
  299. package/docs/content/real-time-collaboration.mdx +2 -2
  300. package/docs/content/template-design.mdx +1 -1
  301. package/package.json +3 -7
  302. package/corpus/core/src/brand-kit/fig/decode.ts +0 -410
  303. package/corpus/core/src/brand-kit/fig/extract-design-system.ts +0 -947
  304. package/corpus/core/src/brand-kit/fig/fig-to-html.ts +0 -1906
  305. package/corpus/templates/design/actions/import-figma.ts +0 -55
  306. package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +0 -6
  307. package/corpus/templates/design/server/handlers/import-figma-system.ts +0 -86
  308. package/corpus/templates/design/server/routes/api/import-figma-system.post.ts +0 -1
  309. package/corpus/templates/slides/server/handlers/import-figma-system.ts +0 -101
  310. package/corpus/templates/slides/server/routes/api/import-figma-system.post.ts +0 -1
  311. package/dist/brand-kit/fig/decode.d.ts +0 -33
  312. package/dist/brand-kit/fig/decode.d.ts.map +0 -1
  313. package/dist/brand-kit/fig/decode.js +0 -358
  314. package/dist/brand-kit/fig/decode.js.map +0 -1
  315. package/dist/brand-kit/fig/extract-design-system.d.ts +0 -44
  316. package/dist/brand-kit/fig/extract-design-system.d.ts.map +0 -1
  317. package/dist/brand-kit/fig/extract-design-system.js +0 -752
  318. package/dist/brand-kit/fig/extract-design-system.js.map +0 -1
  319. package/dist/brand-kit/fig/fig-to-html.d.ts +0 -246
  320. package/dist/brand-kit/fig/fig-to-html.d.ts.map +0 -1
  321. package/dist/brand-kit/fig/fig-to-html.js +0 -1506
  322. package/dist/brand-kit/fig/fig-to-html.js.map +0 -1
@@ -1503,6 +1503,18 @@ const enUS = {
1503
1503
  "Restoring will replace the live content for everyone and cannot be undone (though the current state is saved as a version first).",
1504
1504
  versionRestored: "Version restored.",
1505
1505
  versionSavedAutomatically: "Versions are saved automatically as you edit.",
1506
+ reference: {
1507
+ defaultTitle: "Reference",
1508
+ empty: "Referenced document is empty.",
1509
+ loadError: "Could not load referenced document.",
1510
+ loading: "Loading reference...",
1511
+ missingPath: "Reference path missing or unsupported.",
1512
+ nestedSkipped: "Nested reference preview skipped.",
1513
+ notFound: "Referenced document not found.",
1514
+ open: "Open",
1515
+ pathMissing: "Reference path missing",
1516
+ selfReference: "This reference points to the current document.",
1517
+ },
1506
1518
  media: editorMediaMessages,
1507
1519
  properties: editorPropertiesMessages,
1508
1520
  slash: editorSlashMessages,
@@ -6329,6 +6341,129 @@ const editorMessagesByLocale = {
6329
6341
  },
6330
6342
  } satisfies Partial<Record<LocaleCode, PartialMessages["editor"]>>;
6331
6343
 
6344
+ const contentReferenceMessagesByLocale = {
6345
+ "zh-CN": {
6346
+ defaultTitle: "引用",
6347
+ empty: "引用的文档为空。",
6348
+ loadError: "无法加载引用的文档。",
6349
+ loading: "正在加载引用...",
6350
+ missingPath: "引用路径缺失或不受支持。",
6351
+ nestedSkipped: "已跳过嵌套引用预览。",
6352
+ notFound: "找不到引用的文档。",
6353
+ open: "打开",
6354
+ pathMissing: "引用路径缺失",
6355
+ selfReference: "此引用指向当前文档。",
6356
+ },
6357
+ "zh-TW": {
6358
+ defaultTitle: "引用",
6359
+ empty: "引用的文件是空的。",
6360
+ loadError: "無法載入引用的文件。",
6361
+ loading: "正在載入引用...",
6362
+ missingPath: "引用路徑缺失或不受支援。",
6363
+ nestedSkipped: "已略過巢狀引用預覽。",
6364
+ notFound: "找不到引用的文件。",
6365
+ open: "開啟",
6366
+ pathMissing: "引用路徑缺失",
6367
+ selfReference: "此引用指向目前文件。",
6368
+ },
6369
+ "es-ES": {
6370
+ defaultTitle: "Referencia",
6371
+ empty: "El documento referenciado está vacío.",
6372
+ loadError: "No se pudo cargar el documento referenciado.",
6373
+ loading: "Cargando referencia...",
6374
+ missingPath: "La ruta de referencia falta o no es compatible.",
6375
+ nestedSkipped: "Se omitió la vista previa de la referencia anidada.",
6376
+ notFound: "No se encontró el documento referenciado.",
6377
+ open: "Abrir",
6378
+ pathMissing: "Falta la ruta de referencia",
6379
+ selfReference: "Esta referencia apunta al documento actual.",
6380
+ },
6381
+ "fr-FR": {
6382
+ defaultTitle: "Référence",
6383
+ empty: "Le document référencé est vide.",
6384
+ loadError: "Impossible de charger le document référencé.",
6385
+ loading: "Chargement de la référence...",
6386
+ missingPath: "Le chemin de référence est manquant ou non pris en charge.",
6387
+ nestedSkipped: "Aperçu de référence imbriquée ignoré.",
6388
+ notFound: "Document référencé introuvable.",
6389
+ open: "Ouvrir",
6390
+ pathMissing: "Chemin de référence manquant",
6391
+ selfReference: "Cette référence pointe vers le document actuel.",
6392
+ },
6393
+ "de-DE": {
6394
+ defaultTitle: "Referenz",
6395
+ empty: "Das referenzierte Dokument ist leer.",
6396
+ loadError: "Das referenzierte Dokument konnte nicht geladen werden.",
6397
+ loading: "Referenz wird geladen...",
6398
+ missingPath: "Referenzpfad fehlt oder wird nicht unterstützt.",
6399
+ nestedSkipped: "Vorschau der verschachtelten Referenz übersprungen.",
6400
+ notFound: "Referenziertes Dokument nicht gefunden.",
6401
+ open: "Öffnen",
6402
+ pathMissing: "Referenzpfad fehlt",
6403
+ selfReference: "Diese Referenz verweist auf das aktuelle Dokument.",
6404
+ },
6405
+ "ja-JP": {
6406
+ defaultTitle: "参照",
6407
+ empty: "参照先のドキュメントは空です。",
6408
+ loadError: "参照先のドキュメントを読み込めませんでした。",
6409
+ loading: "参照を読み込んでいます...",
6410
+ missingPath: "参照パスがないか、サポートされていません。",
6411
+ nestedSkipped: "ネストされた参照プレビューをスキップしました。",
6412
+ notFound: "参照先のドキュメントが見つかりません。",
6413
+ open: "開く",
6414
+ pathMissing: "参照パスがありません",
6415
+ selfReference: "この参照は現在のドキュメントを指しています。",
6416
+ },
6417
+ "ko-KR": {
6418
+ defaultTitle: "참조",
6419
+ empty: "참조된 문서가 비어 있습니다.",
6420
+ loadError: "참조된 문서를 불러올 수 없습니다.",
6421
+ loading: "참조를 불러오는 중...",
6422
+ missingPath: "참조 경로가 없거나 지원되지 않습니다.",
6423
+ nestedSkipped: "중첩된 참조 미리보기를 건너뛰었습니다.",
6424
+ notFound: "참조된 문서를 찾을 수 없습니다.",
6425
+ open: "열기",
6426
+ pathMissing: "참조 경로 없음",
6427
+ selfReference: "이 참조는 현재 문서를 가리킵니다.",
6428
+ },
6429
+ "pt-BR": {
6430
+ defaultTitle: "Referência",
6431
+ empty: "O documento referenciado está vazio.",
6432
+ loadError: "Não foi possível carregar o documento referenciado.",
6433
+ loading: "Carregando referência...",
6434
+ missingPath: "O caminho da referência está ausente ou não é compatível.",
6435
+ nestedSkipped: "Prévia de referência aninhada ignorada.",
6436
+ notFound: "Documento referenciado não encontrado.",
6437
+ open: "Abrir",
6438
+ pathMissing: "Caminho da referência ausente",
6439
+ selfReference: "Esta referência aponta para o documento atual.",
6440
+ },
6441
+ "hi-IN": {
6442
+ defaultTitle: "संदर्भ",
6443
+ empty: "संदर्भित दस्तावेज़ खाली है।",
6444
+ loadError: "संदर्भित दस्तावेज़ लोड नहीं किया जा सका।",
6445
+ loading: "संदर्भ लोड हो रहा है...",
6446
+ missingPath: "संदर्भ पथ गायब है या समर्थित नहीं है।",
6447
+ nestedSkipped: "नेस्टेड संदर्भ पूर्वावलोकन छोड़ दिया गया।",
6448
+ notFound: "संदर्भित दस्तावेज़ नहीं मिला।",
6449
+ open: "खोलें",
6450
+ pathMissing: "संदर्भ पथ गायब है",
6451
+ selfReference: "यह संदर्भ वर्तमान दस्तावेज़ की ओर इशारा करता है।",
6452
+ },
6453
+ "ar-SA": {
6454
+ defaultTitle: "مرجع",
6455
+ empty: "المستند المشار إليه فارغ.",
6456
+ loadError: "تعذر تحميل المستند المشار إليه.",
6457
+ loading: "جارٍ تحميل المرجع...",
6458
+ missingPath: "مسار المرجع مفقود أو غير مدعوم.",
6459
+ nestedSkipped: "تم تخطي معاينة المرجع المتداخل.",
6460
+ notFound: "لم يتم العثور على المستند المشار إليه.",
6461
+ open: "فتح",
6462
+ pathMissing: "مسار المرجع مفقود",
6463
+ selfReference: "يشير هذا المرجع إلى المستند الحالي.",
6464
+ },
6465
+ } satisfies Partial<Record<LocaleCode, typeof enUS.editor.reference>>;
6466
+
6332
6467
  function mergeMessages(overrides: PartialMessages): Messages {
6333
6468
  return {
6334
6469
  root: { ...enUS.root, ...overrides.root },
@@ -6350,6 +6485,7 @@ function mergeMessages(overrides: PartialMessages): Messages {
6350
6485
  ...overrides.editor?.properties?.filesMediaKinds,
6351
6486
  },
6352
6487
  },
6488
+ reference: { ...enUS.editor.reference, ...overrides.editor?.reference },
6353
6489
  slash: { ...enUS.editor.slash, ...overrides.editor?.slash },
6354
6490
  toolbar: { ...enUS.editor.toolbar, ...overrides.editor?.toolbar },
6355
6491
  },
@@ -6437,6 +6573,10 @@ function mergeMessagesForLocale(
6437
6573
  ...rawLiteralOverrides.editor,
6438
6574
  media: { ...base.editor.media, ...rawLiteralOverrides.editor?.media },
6439
6575
  properties: base.editor.properties,
6576
+ reference: {
6577
+ ...base.editor.reference,
6578
+ ...contentReferenceMessagesByLocale[locale],
6579
+ },
6440
6580
  slash: base.editor.slash,
6441
6581
  toolbar: {
6442
6582
  ...base.editor.toolbar,
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-06-29
4
+ ---
5
+
6
+ Reusable MDX references now preview linked local documents inline.
@@ -49,6 +49,8 @@ export interface BuilderTabbedContentData extends BuilderRawRefData {
49
49
  export interface BuilderSymbolData extends BuilderRawRefData {
50
50
  entry?: string;
51
51
  model?: string;
52
+ source?: string;
53
+ dynamic?: boolean;
52
54
  data?: Record<string, unknown>;
53
55
  }
54
56
 
@@ -104,6 +106,8 @@ export const builderSymbolSchema = z.object({
104
106
  ...rawRefSchema,
105
107
  entry: z.string().trim().max(240).optional(),
106
108
  model: z.string().trim().max(120).optional(),
109
+ source: z.string().trim().max(2_000).optional(),
110
+ dynamic: z.boolean().optional(),
107
111
  data: z.record(z.string(), z.unknown()).optional(),
108
112
  }) as unknown as z.ZodType<BuilderSymbolData>;
109
113
 
@@ -203,6 +207,8 @@ export const builderSymbolMdx: BlockMdxConfig<BuilderSymbolData> = {
203
207
  ...rawRefAttrs(data),
204
208
  entry: data.entry,
205
209
  model: data.model,
210
+ source: data.source,
211
+ dynamic: data.dynamic,
206
212
  data: data.data,
207
213
  }),
208
214
  fromAttrs: (attrs) => ({
@@ -211,6 +217,8 @@ export const builderSymbolMdx: BlockMdxConfig<BuilderSymbolData> = {
211
217
  componentName: attrs.string("componentName"),
212
218
  entry: attrs.string("entry"),
213
219
  model: attrs.string("model"),
220
+ source: attrs.string("source"),
221
+ dynamic: attrs.bool("dynamic"),
214
222
  data: attrs.object<Record<string, unknown>>("data"),
215
223
  }),
216
224
  };
@@ -20,6 +20,7 @@ export const BUILDER_DOCS_MODELS = [
20
20
  "docs-content",
21
21
  "blog-article",
22
22
  "agent-native-blog-article-test",
23
+ "symbol",
23
24
  ] as const;
24
25
 
25
26
  export type BuilderDocsModel = (typeof BUILDER_DOCS_MODELS)[number] | string;
@@ -175,6 +176,7 @@ export function builderRawRootForEntry(model: string, entryId: string) {
175
176
  function modelDirectory(model: string) {
176
177
  if (model === "docs-content") return "docs";
177
178
  if (model === "blog-article") return "blog";
179
+ if (model === "symbol") return "symbols";
178
180
  return safePathPart(model);
179
181
  }
180
182
 
@@ -194,6 +196,23 @@ export function builderMdxPathForEntry(entry: BuilderContentEntry) {
194
196
  )}/${slug}${BUILDER_DOCS_MDX_EXTENSION}`;
195
197
  }
196
198
 
199
+ function builderSymbolMdxPathForEntry(entry: BuilderContentEntry) {
200
+ const data = entry.data ?? {};
201
+ const title = builderEntryTitle(entry);
202
+ const handle = stringFromRecord(data, [
203
+ "handle",
204
+ "slug",
205
+ "urlPath",
206
+ "url",
207
+ "path",
208
+ ]);
209
+ const slug = slugify(handle?.replace(/^\/+|\/+$/g, "") || title, entry.id);
210
+ return `${BUILDER_DOCS_CONTENT_ROOT}/symbols/${safePathPart(
211
+ entry.model,
212
+ "symbol",
213
+ )}/${slug}${BUILDER_DOCS_MDX_EXTENSION}`;
214
+ }
215
+
197
216
  function stringFromRecord(
198
217
  record: Record<string, unknown>,
199
218
  keys: string[],
@@ -357,6 +376,50 @@ function childBlocks(block: unknown): unknown[] {
357
376
  return Array.isArray(block.children) ? block.children : [];
358
377
  }
359
378
 
379
+ function symbolContentEntry(
380
+ symbol: Record<string, unknown>,
381
+ ): BuilderContentEntry | null {
382
+ const content = isRecord(symbol.content) ? symbol.content : null;
383
+ if (!content) return null;
384
+ const rawModel = stringFromRecord(symbol, ["model"]);
385
+ const rawEntry = stringFromRecord(symbol, ["entry"]);
386
+ const id = stringFromRecord(content, ["id", "entry", "uid"]) ?? rawEntry;
387
+ if (!id) return null;
388
+
389
+ const model =
390
+ stringFromRecord(content, ["model", "modelName", "queryModel"]) ??
391
+ rawModel ??
392
+ "symbol";
393
+ const data = isRecord(content.data)
394
+ ? (JSON.parse(JSON.stringify(content.data)) as Record<string, unknown>)
395
+ : {};
396
+ if (!Array.isArray(data.blocks) && Array.isArray(content.blocks)) {
397
+ data.blocks = content.blocks;
398
+ }
399
+ if (!data.blocks) return null;
400
+
401
+ return {
402
+ ...content,
403
+ id,
404
+ model,
405
+ name:
406
+ stringFromRecord(content, ["name"]) ??
407
+ stringFromRecord(data, ["title", "pageTitle", "name"]) ??
408
+ id,
409
+ published:
410
+ typeof content.published === "string" ? content.published : undefined,
411
+ lastUpdated:
412
+ typeof content.lastUpdated === "string" ||
413
+ typeof content.lastUpdated === "number"
414
+ ? content.lastUpdated
415
+ : typeof content.updatedDate === "string" ||
416
+ typeof content.updatedDate === "number"
417
+ ? content.updatedDate
418
+ : undefined,
419
+ data,
420
+ };
421
+ }
422
+
360
423
  function blockSummary(block: unknown) {
361
424
  const name = componentName(block);
362
425
  const options = componentOptions(block);
@@ -382,6 +445,7 @@ interface BlocksToMdxContext {
382
445
  rawRoot: string;
383
446
  files: Record<string, string>;
384
447
  warnings: string[];
448
+ emitReferencedEntry: (entry: BuilderContentEntry) => Promise<string>;
385
449
  }
386
450
 
387
451
  async function builderBlocksToMdxBody(
@@ -493,10 +557,19 @@ async function builderBlockToMdx(
493
557
 
494
558
  if (name === "Symbol") {
495
559
  const symbol = isRecord(options.symbol) ? options.symbol : {};
560
+ const contentEntry = symbolContentEntry(symbol);
561
+ const source = contentEntry
562
+ ? await ctx.emitReferencedEntry(contentEntry)
563
+ : undefined;
496
564
  const data: BuilderSymbolData = {
497
565
  ...raw,
498
566
  entry: typeof symbol.entry === "string" ? symbol.entry : undefined,
499
567
  model: typeof symbol.model === "string" ? symbol.model : undefined,
568
+ source,
569
+ dynamic:
570
+ boolFromRecord(symbol, "dynamic") ??
571
+ boolFromRecord(symbol, "isDynamic") ??
572
+ boolFromRecord(options, "dynamicSymbol"),
500
573
  data: isRecord(symbol.data) ? symbol.data : undefined,
501
574
  };
502
575
  return serializeRegistryBlockToMdx("builder-symbol", {
@@ -525,12 +598,76 @@ async function builderBlockToMdx(
525
598
  export async function builderEntryToMdxBundle(
526
599
  entry: BuilderContentEntry,
527
600
  ): Promise<BuilderMdxBundle> {
601
+ const files: Record<string, string> = {};
602
+ const warnings: string[] = [];
603
+ const emitted = new Set<string>();
604
+ const mdx = await emitBuilderEntryToMdxFile({
605
+ entry,
606
+ files,
607
+ warnings,
608
+ emitted,
609
+ symbolPath: false,
610
+ });
611
+ return { mdx, files, blocks: builderEntryBlocks(entry) };
612
+ }
613
+
614
+ async function emitBuilderEntryToMdxFile({
615
+ entry,
616
+ files,
617
+ warnings,
618
+ emitted,
619
+ symbolPath,
620
+ }: {
621
+ entry: BuilderContentEntry;
622
+ files: Record<string, string>;
623
+ warnings: string[];
624
+ emitted: Set<string>;
625
+ symbolPath: boolean;
626
+ }): Promise<BuilderMdxFile> {
627
+ const path = symbolPath
628
+ ? builderSymbolMdxPathForEntry(entry)
629
+ : builderMdxPathForEntry(entry);
630
+ const key = `${entry.model}:${entry.id}:${path}`;
631
+ if (emitted.has(key)) {
632
+ return {
633
+ path,
634
+ documentId: builderDocumentId(entry.model, entry.id),
635
+ title: builderEntryTitle(entry),
636
+ metadata: {
637
+ model: entry.model,
638
+ entryId: entry.id,
639
+ lastUpdated: normalizeRemoteUpdatedAt(entry),
640
+ published: entry.published,
641
+ sourceHash: builderSourceHash(entry),
642
+ blocksHash: builderBlocksHash(builderEntryBlocks(entry)),
643
+ rawRoot: builderRawRootForEntry(entry.model, entry.id),
644
+ path,
645
+ },
646
+ frontmatter: {},
647
+ body: "",
648
+ source: files[path] ?? "",
649
+ };
650
+ }
651
+ emitted.add(key);
652
+
528
653
  const blocks = builderEntryBlocks(entry);
529
654
  const rawRoot = builderRawRootForEntry(entry.model, entry.id);
530
- const files: Record<string, string> = {};
531
- const ctx: BlocksToMdxContext = { rawRoot, files, warnings: [] };
655
+ const ctx: BlocksToMdxContext = {
656
+ rawRoot,
657
+ files,
658
+ warnings,
659
+ emitReferencedEntry: async (referencedEntry) => {
660
+ const nested = await emitBuilderEntryToMdxFile({
661
+ entry: referencedEntry,
662
+ files,
663
+ warnings,
664
+ emitted,
665
+ symbolPath: true,
666
+ });
667
+ return nested.path;
668
+ },
669
+ };
532
670
  const body = await builderBlocksToMdxBody(blocks, ctx);
533
- const path = builderMdxPathForEntry(entry);
534
671
  const blocksHash = builderBlocksHash(blocks);
535
672
  const sourceHash = builderSourceHash(entry);
536
673
  const frontmatter = frontmatterForEntry({
@@ -552,7 +689,7 @@ export async function builderEntryToMdxBundle(
552
689
  source,
553
690
  };
554
691
  files[path] = source;
555
- return { mdx, files, blocks };
692
+ return mdx;
556
693
  }
557
694
 
558
695
  function parseFrontmatterValue(value: string): unknown {
@@ -673,6 +673,7 @@ function serializeBlock(node: PMNode, indent: number): string[] {
673
673
  return serializeBlockAtom(node, ind);
674
674
  case "registryBlock":
675
675
  return serializeRegistryBlock(node, ind);
676
+ case "contentReference":
676
677
  case "localMdxComponent":
677
678
  return serializeRawSourceBlock(node, ind);
678
679
  default: {
@@ -1199,6 +1200,19 @@ function parseSingleBlock(
1199
1200
  return { nodes: [withIndentAttr(node)], end: childRes.end };
1200
1201
  }
1201
1202
 
1203
+ // Content reference (Notion-style reusable MDX transclusion). The source
1204
+ // owns the reference; the editor resolves and previews it from local files.
1205
+ const contentReferenceTag = matchContentReferenceOpen(dedent);
1206
+ if (contentReferenceTag) {
1207
+ return parseContentReference(
1208
+ lines,
1209
+ start,
1210
+ indent,
1211
+ rel,
1212
+ contentReferenceTag,
1213
+ );
1214
+ }
1215
+
1202
1216
  // Registry block (PascalCase MDX element: <Endpoint .../>, <Checklist .../>,
1203
1217
  // <DataModel …>…</DataModel>, …). These are the dev-doc / OpenAPI library
1204
1218
  // blocks shared with plan, encoded inline. They are recognized by the content
@@ -1288,6 +1302,12 @@ function matchRegistryBlockOpen(dedent: string): string | null {
1288
1302
  return registryBlockSpecByTag(tag) ? tag : null;
1289
1303
  }
1290
1304
 
1305
+ function matchContentReferenceOpen(dedent: string): string | null {
1306
+ return /^<ContentReference(?:[\s/>]|$)/.test(dedent)
1307
+ ? "ContentReference"
1308
+ : null;
1309
+ }
1310
+
1291
1311
  function matchLocalMdxComponentOpen(dedent: string): string | null {
1292
1312
  const m = dedent.match(/^<([A-Z][\w-]*)(?:[\s/>]|$)/);
1293
1313
  if (!m) return null;
@@ -1424,6 +1444,48 @@ function parseRegistryBlock(
1424
1444
  return { nodes: [withIndentAttr(node)], end };
1425
1445
  }
1426
1446
 
1447
+ function parseContentReference(
1448
+ lines: string[],
1449
+ start: number,
1450
+ indent: number,
1451
+ rel: number,
1452
+ _tag: string,
1453
+ ): ParseResult {
1454
+ const withIndentAttr = (node: PMNode): PMNode => {
1455
+ if (rel > 0) node.attrs = { ...(node.attrs || {}), indent: rel };
1456
+ return node;
1457
+ };
1458
+ const dedented = lines.map((l) => stripTabs(l, indent));
1459
+
1460
+ let openEndLine = start;
1461
+ {
1462
+ let joined = "";
1463
+ for (let i = start; i < lines.length; i++) {
1464
+ joined += (i === start ? "" : "\n") + dedented[i];
1465
+ const res = scanOpenTagEnd(joined);
1466
+ if (res) {
1467
+ openEndLine = i;
1468
+ break;
1469
+ }
1470
+ openEndLine = i;
1471
+ }
1472
+ }
1473
+
1474
+ const rawLines = dedented.slice(start, openEndLine + 1);
1475
+ const raw = rawLines.join("\n");
1476
+ const props = parseAttrs(raw);
1477
+ const node: PMNode = {
1478
+ type: "contentReference",
1479
+ attrs: {
1480
+ sourcePath:
1481
+ props.sourcePath ?? props.path ?? props.source ?? props.href ?? "",
1482
+ title: props.title ?? null,
1483
+ __raw: raw,
1484
+ },
1485
+ };
1486
+ return { nodes: [withIndentAttr(node)], end: openEndLine + 1 };
1487
+ }
1488
+
1427
1489
  function parseLocalMdxComponent(
1428
1490
  lines: string[],
1429
1491
  start: number,
@@ -178,19 +178,18 @@ Extracts CSS tokens from a project's generated HTML, or clones an existing desig
178
178
  ### Source: Figma file
179
179
 
180
180
  ```bash
181
- pnpm action import-figma --figmaUrl "https://figma.com/design/<key>/..." \
182
- --description "<extracted tokens / styles summary>"
181
+ pnpm action index-design-system-with-builder \
182
+ --projectName "Acme Figma system" \
183
+ --codeFiles '[{"filename":"figma-summary.md","content":"<extracted tokens / styles summary>"}]'
183
184
  ```
184
185
 
185
- **Prefer the connected Figma MCP** when available (tools like
186
- `get_variable_defs`, `get_design_context`, `get_metadata`, `get_screenshot`).
187
- Call those on the file/selection FIRST to pull real variables (tokens), color
188
- and text styles, and a screenshot — this sidesteps REST/Enterprise limits and
189
- returns a token map directly. Then pass that summary to `import-figma` and build
190
- the system with `create-design-system`. Convert Figma colors (0-1 RGBA) to hex,
191
- effects to CSS box-shadow, and text styles to font tokens; snap spacing to a
192
- 4/8px scale. If no Figma MCP is connected, ask the user to paste the token
193
- values or describe the file.
186
+ Builder is the required extraction/indexing path for Figma-backed design
187
+ systems. If a connected Figma MCP is available (tools like `get_variable_defs`,
188
+ `get_design_context`, `get_metadata`, `get_screenshot`), call those on the
189
+ file/selection first to pull real variables, color/text styles, and screenshot
190
+ notes, then pass that summary to `index-design-system-with-builder` as an
191
+ uploaded text context file. Do not build a local design system from the Figma
192
+ summary unless the user explicitly asks for a manual local fallback.
194
193
 
195
194
  **When the user uploads a raw `.fig` file**, send it to Builder design-system
196
195
  indexing through the setup page upload route. Do not parse `.fig` files locally
@@ -101,6 +101,11 @@ patterns live in `.agents/skills/`.
101
101
  progress. External MCP hosts should use `present-design-variants` so the same
102
102
  picker opens inline instead of writing `application_state` directly.
103
103
  - Use framework sharing actions for design and design-system visibility/grants.
104
+ - `/visual-edit` is a public entry route and public `/design/:id` links may
105
+ render read-only public designs without a session. Do not open anonymous write
106
+ actions: save, share, generation, localhost connection, and screen persistence
107
+ should send signed-out visitors through `/_agent-native/sign-in?return=...`,
108
+ then save or copy the design into the authenticated account before sharing.
104
109
  - When the user asks to download/export, use export actions or point to the
105
110
  editor download menu.
106
111
  - Design source modes are `inline`, `localhost`, and `fusion`. Inline is the
@@ -37,3 +37,10 @@ data/.sessions.json
37
37
  learnings.md
38
38
 
39
39
  .vercel/
40
+
41
+ # Playwright E2E
42
+ /test-results/
43
+ /playwright-report/
44
+ /e2e/.auth/
45
+ data/e2e.db
46
+ data/e2e.db-*
@@ -32,18 +32,28 @@ function parseJsonString(value: unknown): unknown {
32
32
 
33
33
  const sourceSchema = z.preprocess(
34
34
  parseJsonString,
35
- z.object({
36
- kind: z
37
- .enum(["design-file", "inline-html", "local-file", "remote-url"])
38
- .default("design-file"),
39
- designId: z.string().optional(),
40
- fileId: z.string().optional(),
41
- filename: z.string().optional().default("index.html"),
42
- path: z.string().optional(),
43
- url: z.string().optional(),
44
- revision: z.string().optional(),
45
- html: z.string().optional(),
46
- }),
35
+ z
36
+ .object({
37
+ kind: z
38
+ .enum(["design-file", "inline-html", "local-file", "remote-url"])
39
+ .default("design-file"),
40
+ designId: z.string().optional(),
41
+ fileId: z.string().optional(),
42
+ filename: z.string().optional().default("index.html"),
43
+ path: z.string().optional(),
44
+ url: z.string().optional(),
45
+ revision: z.string().optional(),
46
+ html: z.string().optional(),
47
+ })
48
+ .superRefine((source, ctx) => {
49
+ if (source.kind === "design-file" && !source.designId && !source.fileId) {
50
+ ctx.addIssue({
51
+ code: z.ZodIssueCode.custom,
52
+ path: ["designId"],
53
+ message: "designId or fileId is required for design-file sources",
54
+ });
55
+ }
56
+ }),
47
57
  );
48
58
 
49
59
  const targetSchema = z.object({
@@ -3,7 +3,7 @@ import {
3
3
  getRequestUserEmail,
4
4
  getRequestOrgId,
5
5
  } from "@agent-native/core/server/request-context";
6
- import { eq } from "drizzle-orm";
6
+ import { and, eq, isNull } from "drizzle-orm";
7
7
  import { nanoid } from "nanoid";
8
8
  import { z } from "zod";
9
9
 
@@ -66,12 +66,23 @@ export default defineAction({
66
66
  if (!ownerEmail) throw new Error("no authenticated user");
67
67
  const orgId = getRequestOrgId();
68
68
 
69
- // Check only this user's owned systems. Shared systems should not prevent
70
- // the first system a user creates from becoming their default.
69
+ // Check only this user's owned systems within the same org. Shared systems
70
+ // should not prevent the first system a user creates from becoming their
71
+ // default, and systems in other orgs must not suppress the default in this org.
71
72
  const existing = await db
72
73
  .select({ id: schema.designSystems.id })
73
74
  .from(schema.designSystems)
74
- .where(eq(schema.designSystems.ownerEmail, ownerEmail))
75
+ .where(
76
+ orgId
77
+ ? and(
78
+ eq(schema.designSystems.ownerEmail, ownerEmail),
79
+ eq(schema.designSystems.orgId, orgId),
80
+ )
81
+ : and(
82
+ eq(schema.designSystems.ownerEmail, ownerEmail),
83
+ isNull(schema.designSystems.orgId),
84
+ ),
85
+ )
75
86
  .limit(1);
76
87
 
77
88
  const isDefault = existing.length === 0;
@@ -1,7 +1,7 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { seedFromText } from "@agent-native/core/collab";
3
3
  import { assertAccess } from "@agent-native/core/sharing";
4
- import { eq } from "drizzle-orm";
4
+ import { and, eq } from "drizzle-orm";
5
5
  import { nanoid } from "nanoid";
6
6
  import { z } from "zod";
7
7
 
@@ -34,6 +34,25 @@ export default defineAction({
34
34
  await assertAccess("design", designId, "editor");
35
35
 
36
36
  const db = getDb();
37
+
38
+ // Guard against duplicate (designId, filename) — edit-design uses .limit(1)
39
+ // which is non-deterministic when multiple rows match the same key.
40
+ const [existing] = await db
41
+ .select({ id: schema.designFiles.id })
42
+ .from(schema.designFiles)
43
+ .where(
44
+ and(
45
+ eq(schema.designFiles.designId, designId),
46
+ eq(schema.designFiles.filename, filename),
47
+ ),
48
+ )
49
+ .limit(1);
50
+ if (existing) {
51
+ throw new Error(
52
+ `File "${filename}" already exists in design ${designId} — use edit-design to modify it`,
53
+ );
54
+ }
55
+
37
56
  const id = nanoid();
38
57
  const now = new Date().toISOString();
39
58