@agent-native/core 0.80.4 → 0.80.6

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 (341) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +28 -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 +21 -0
  26. package/corpus/core/src/agent/durable-background.ts +32 -11
  27. package/corpus/core/src/agent/production-agent.ts +11 -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/create.ts +1 -0
  31. package/corpus/core/src/cli/index.ts +1 -1
  32. package/corpus/core/src/cli/mcp-config-writers.ts +187 -51
  33. package/corpus/core/src/cli/skills.ts +18 -3
  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/agent-chat-plugin.ts +20 -0
  43. package/corpus/core/src/sharing/registry.ts +17 -0
  44. package/corpus/core/src/templates/default/pnpm-workspace.yaml +1 -0
  45. package/corpus/core/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
  46. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -0
  47. package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
  48. package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
  49. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +117 -33
  50. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
  51. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
  52. package/corpus/templates/analytics/changelog/2026-06-27-dashboards-can-now-nest-under-a-parent-in-the-sidebar-via-a-.md +6 -0
  53. package/corpus/templates/analytics/changelog/2026-06-29-analytics-pages-now-sit-closer-under-the-header-with-tighter.md +6 -0
  54. package/corpus/templates/assets/actions/generate-image-batch.ts +3 -0
  55. package/corpus/templates/assets/actions/generate-image.ts +3 -0
  56. package/corpus/templates/assets/changelog/2026-06-29-longer-agent-generation-window.md +6 -0
  57. package/corpus/templates/assets/netlify.toml +3 -3
  58. package/corpus/templates/assets/server/plugins/agent-chat.ts +4 -1
  59. package/corpus/templates/clips/app/components/capture-install-options.tsx +7 -8
  60. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +1 -1
  61. package/corpus/templates/content/AGENTS.md +57 -44
  62. package/corpus/templates/content/README.md +16 -0
  63. package/corpus/templates/content/actions/_builder-cms-read-client.ts +125 -0
  64. package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +28 -6
  65. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +238 -69
  66. package/corpus/templates/content/actions/_builder-cms-write-settings.ts +125 -43
  67. package/corpus/templates/content/actions/_database-source-utils.ts +746 -97
  68. package/corpus/templates/content/actions/_database-utils.ts +17 -14
  69. package/corpus/templates/content/actions/_federation-join.ts +14 -2
  70. package/corpus/templates/content/actions/_join-suggestion.ts +89 -14
  71. package/corpus/templates/content/actions/_local-file-documents.ts +2 -1
  72. package/corpus/templates/content/actions/attach-content-database-source.ts +124 -5
  73. package/corpus/templates/content/actions/bind-content-database-source-field.ts +256 -0
  74. package/corpus/templates/content/actions/change-content-database-source-role.ts +420 -0
  75. package/corpus/templates/content/actions/create-document.ts +8 -0
  76. package/corpus/templates/content/actions/execute-builder-source-batch.ts +282 -0
  77. package/corpus/templates/content/actions/execute-builder-source-execution.ts +220 -8
  78. package/corpus/templates/content/actions/get-content-database.ts +10 -2
  79. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +19 -2
  80. package/corpus/templates/content/actions/prepare-builder-source-review.ts +47 -15
  81. package/corpus/templates/content/actions/refresh-content-database-source.ts +11 -4
  82. package/corpus/templates/content/actions/review-content-database-source-change-set.ts +6 -2
  83. package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +32 -12
  84. package/corpus/templates/content/actions/stage-builder-revision.ts +14 -10
  85. package/corpus/templates/content/actions/validate-builder-source-execution.ts +25 -3
  86. package/corpus/templates/content/app/blocks/BuilderDocsBlocks.tsx +10 -0
  87. package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -0
  88. package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +185 -0
  89. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1520 -553
  90. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -0
  91. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +150 -1
  92. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +19 -0
  93. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +647 -66
  94. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +543 -204
  95. package/corpus/templates/content/app/components/editor/extensions/ContentReferenceNode.tsx +82 -0
  96. package/corpus/templates/content/app/global.css +9 -0
  97. package/corpus/templates/content/app/hooks/use-content-database.ts +62 -0
  98. package/corpus/templates/content/app/i18n/zh-TW.ts +76 -0
  99. package/corpus/templates/content/app/i18n-data.ts +924 -0
  100. package/corpus/templates/content/changelog/2026-06-29-builder-review-checks-now-preserve-publish-and-unpublish-cho.md +6 -0
  101. package/corpus/templates/content/changelog/2026-06-29-database-sources-can-now-be-added-as-more-rows-or-matched.md +6 -0
  102. package/corpus/templates/content/changelog/2026-06-29-fixed-loading-the-final-rows-in-larger-databases-without-bre.md +6 -0
  103. package/corpus/templates/content/changelog/2026-06-29-reusable-mdx-references-now-preview-linked-local-documents-i.md +6 -0
  104. package/corpus/templates/content/shared/api.ts +91 -1
  105. package/corpus/templates/content/shared/builder-docs-blocks.ts +8 -0
  106. package/corpus/templates/content/shared/builder-mdx.ts +141 -4
  107. package/corpus/templates/content/shared/nfm.ts +62 -0
  108. package/corpus/templates/content/vite.config.ts +14 -7
  109. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +10 -11
  110. package/corpus/templates/design/AGENTS.md +5 -0
  111. package/corpus/templates/design/_gitignore +7 -0
  112. package/corpus/templates/design/actions/apply-visual-edit.ts +22 -12
  113. package/corpus/templates/design/actions/create-design-system.ts +15 -4
  114. package/corpus/templates/design/actions/create-file.ts +20 -1
  115. package/corpus/templates/design/actions/delete-design-system.ts +2 -2
  116. package/corpus/templates/design/actions/duplicate-design.ts +35 -9
  117. package/corpus/templates/design/actions/generate-design.ts +146 -69
  118. package/corpus/templates/design/actions/generate-screens.ts +51 -19
  119. package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
  120. package/corpus/templates/design/actions/get-design.ts +2 -0
  121. package/corpus/templates/design/actions/import-design-project.ts +28 -9
  122. package/corpus/templates/design/actions/import-document.ts +22 -8
  123. package/corpus/templates/design/actions/index-design-system-with-builder.ts +6 -153
  124. package/corpus/templates/design/actions/navigate.ts +105 -54
  125. package/corpus/templates/design/actions/set-default-design-system.ts +17 -3
  126. package/corpus/templates/design/actions/show-design-questions.ts +2 -0
  127. package/corpus/templates/design/actions/update-design.ts +2 -1
  128. package/corpus/templates/design/actions/view-screen.ts +12 -6
  129. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +4 -4
  130. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +159 -41
  131. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +8 -4
  132. package/corpus/templates/design/app/components/design/DrawOverlay.tsx +37 -42
  133. package/corpus/templates/design/app/components/design/EditPanel.tsx +336 -183
  134. package/corpus/templates/design/app/components/design/LayersPanel.tsx +153 -34
  135. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +135 -33
  136. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +6 -0
  137. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
  138. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +4 -10
  139. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +84 -43
  140. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +4 -4
  141. package/corpus/templates/design/app/components/design/inspector/{FigmaColorPicker.tsx → DesignColorPicker.tsx} +89 -62
  142. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +14 -26
  143. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +40 -12
  144. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -4
  145. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +13 -13
  146. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -9
  147. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +13 -4
  148. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
  149. package/corpus/templates/design/app/components/design/inspector/{figma-icons.tsx → design-icons.tsx} +2 -2
  150. package/corpus/templates/design/app/components/design/inspector/index.ts +12 -12
  151. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +5 -2
  152. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -3
  153. package/corpus/templates/design/app/components/layout/Layout.tsx +5 -2
  154. package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
  155. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +167 -91
  156. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +16 -3
  157. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +15 -0
  158. package/corpus/templates/design/app/global.css +4 -0
  159. package/corpus/templates/design/app/hooks/use-design-system.ts +4 -1
  160. package/corpus/templates/design/app/hooks/use-navigation-state.ts +38 -3
  161. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +9 -2
  162. package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
  163. package/corpus/templates/design/app/i18n-data.ts +527 -0
  164. package/corpus/templates/design/app/lib/design-systems.ts +6 -4
  165. package/corpus/templates/design/app/lib/design-templates.ts +15 -3
  166. package/corpus/templates/design/app/pages/DesignEditor.tsx +641 -158
  167. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +42 -34
  168. package/corpus/templates/design/app/pages/VisualEdit.tsx +100 -0
  169. package/corpus/templates/design/app/root.tsx +42 -9
  170. package/corpus/templates/design/app/routes/visual-edit.tsx +7 -0
  171. package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +1 -1
  172. package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +1 -1
  173. package/corpus/templates/design/changelog/2026-06-28-visual-code-design-mode.md +6 -0
  174. package/corpus/templates/design/changelog/2026-06-29-design-generation-prompts-now-show-connect-ai-before-sending.md +6 -0
  175. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +1 -1
  176. package/corpus/templates/design/changelog/2026-06-29-fixed-design-editor-exports-and-gradient-edits-so-multi-scre.md +6 -0
  177. package/corpus/templates/design/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  178. package/corpus/templates/design/changelog/2026-06-29-png-export-handles-modern-css-color-functions-more-reliably.md +6 -0
  179. package/corpus/templates/design/changelog/2026-06-29-png-export-now-ignores-duplicate-clicks-while-a-download-is-.md +6 -0
  180. package/corpus/templates/design/changelog/2026-06-29-public-visual-edit-and-shared-design-links-now-invite-visito.md +6 -0
  181. package/corpus/templates/design/changelog/2026-06-29-visual-edit-actions-now-reject-missing-design-targets-before.md +6 -0
  182. package/corpus/templates/design/e2e/README.md +71 -0
  183. package/corpus/templates/design/e2e/global-setup.ts +157 -0
  184. package/corpus/templates/design/e2e/helpers.ts +148 -0
  185. package/corpus/templates/design/netlify.toml +3 -4
  186. package/corpus/templates/design/package.json +5 -1
  187. package/corpus/templates/design/playwright.config.ts +51 -0
  188. package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +121 -0
  189. package/corpus/templates/design/server/handlers/uploads.ts +89 -38
  190. package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +181 -0
  191. package/corpus/templates/design/server/lib/coding-handoff.ts +33 -4
  192. package/corpus/templates/design/server/lib/design-export.ts +35 -8
  193. package/corpus/templates/design/server/plugins/agent-chat.ts +4 -0
  194. package/corpus/templates/design/server/plugins/auth.ts +10 -1
  195. package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  196. package/corpus/templates/design/shared/api.ts +2 -0
  197. package/corpus/templates/design/shared/apply-edits.ts +12 -8
  198. package/corpus/templates/design/shared/canvas-math.ts +19 -1
  199. package/corpus/templates/design/shared/code-layer.ts +87 -28
  200. package/corpus/templates/design/shared/generation-session.ts +1 -0
  201. package/corpus/templates/design/shared/resolve-tweaks.ts +4 -3
  202. package/corpus/templates/design/shared/shader-presets.ts +10 -0
  203. package/corpus/templates/design/shared/source-mode.ts +9 -0
  204. package/corpus/templates/design/vitest.config.ts +2 -1
  205. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
  206. package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +1 -1
  207. package/corpus/templates/plan/AGENTS.md +1 -1
  208. package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
  209. package/corpus/templates/plan/actions/validate-local-plan-source.ts +2 -10
  210. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -0
  211. package/corpus/templates/plan/docs/commenting-ux-plan.md +2 -2
  212. package/corpus/templates/plan/server/plan-content.ts +1 -1
  213. package/corpus/templates/plan/server/ui-plan-html.ts +2 -2
  214. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +6 -153
  215. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +53 -38
  216. package/corpus/templates/slides/app/components/design-system/{fig-import-response.ts → builder-index-response.ts} +9 -6
  217. package/corpus/templates/slides/app/components/visual-editor/MultiSelectChip.tsx +1 -1
  218. package/corpus/templates/slides/changelog/2026-06-29-design-system-setup-now-indexes-uploaded-design-files-throug.md +6 -0
  219. package/corpus/templates/slides/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
  220. package/corpus/templates/slides/netlify.toml +3 -6
  221. package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +116 -0
  222. package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +179 -0
  223. package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -1
  224. package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +1 -0
  225. package/dist/action.d.ts +15 -0
  226. package/dist/action.d.ts.map +1 -1
  227. package/dist/action.js +6 -0
  228. package/dist/action.js.map +1 -1
  229. package/dist/agent/durable-background.d.ts +9 -7
  230. package/dist/agent/durable-background.d.ts.map +1 -1
  231. package/dist/agent/durable-background.js +27 -11
  232. package/dist/agent/durable-background.js.map +1 -1
  233. package/dist/agent/production-agent.d.ts +7 -0
  234. package/dist/agent/production-agent.d.ts.map +1 -1
  235. package/dist/agent/production-agent.js +4 -1
  236. package/dist/agent/production-agent.js.map +1 -1
  237. package/dist/agent/run-loop-with-resume.d.ts +2 -1
  238. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  239. package/dist/agent/run-loop-with-resume.js +2 -2
  240. package/dist/agent/run-loop-with-resume.js.map +1 -1
  241. package/dist/brand-kit/fig/index.d.ts +4 -4
  242. package/dist/brand-kit/fig/index.d.ts.map +1 -1
  243. package/dist/brand-kit/fig/index.js +14 -27
  244. package/dist/brand-kit/fig/index.js.map +1 -1
  245. package/dist/cli/create.d.ts.map +1 -1
  246. package/dist/cli/create.js +1 -0
  247. package/dist/cli/create.js.map +1 -1
  248. package/dist/cli/index.js +1 -1
  249. package/dist/cli/index.js.map +1 -1
  250. package/dist/cli/mcp-config-writers.d.ts +15 -7
  251. package/dist/cli/mcp-config-writers.d.ts.map +1 -1
  252. package/dist/cli/mcp-config-writers.js +198 -45
  253. package/dist/cli/mcp-config-writers.js.map +1 -1
  254. package/dist/cli/skills.d.ts +1 -1
  255. package/dist/cli/skills.d.ts.map +1 -1
  256. package/dist/cli/skills.js +18 -3
  257. package/dist/cli/skills.js.map +1 -1
  258. package/dist/client/AssistantChat.d.ts.map +1 -1
  259. package/dist/client/AssistantChat.js +11 -1
  260. package/dist/client/AssistantChat.js.map +1 -1
  261. package/dist/client/agent-chat.d.ts.map +1 -1
  262. package/dist/client/agent-chat.js +15 -2
  263. package/dist/client/agent-chat.js.map +1 -1
  264. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  265. package/dist/client/composer/PromptComposer.js +35 -1
  266. package/dist/client/composer/PromptComposer.js.map +1 -1
  267. package/dist/client/org/OrgSwitcher.js +1 -1
  268. package/dist/client/org/OrgSwitcher.js.map +1 -1
  269. package/dist/client/sharing/ShareButton.d.ts +1 -1
  270. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  271. package/dist/client/sharing/ShareButton.js +2 -15
  272. package/dist/client/sharing/ShareButton.js.map +1 -1
  273. package/dist/collab/presence.d.ts +1 -1
  274. package/dist/collab/presence.js +1 -1
  275. package/dist/collab/presence.js.map +1 -1
  276. package/dist/collab/routes.d.ts +1 -1
  277. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  278. package/dist/mcp/connect-route.d.ts.map +1 -1
  279. package/dist/mcp/connect-route.js +34 -5
  280. package/dist/mcp/connect-route.js.map +1 -1
  281. package/dist/notifications/routes.d.ts +2 -2
  282. package/dist/observability/routes.d.ts +8 -8
  283. package/dist/resources/handlers.d.ts +1 -1
  284. package/dist/server/action-discovery.d.ts.map +1 -1
  285. package/dist/server/action-discovery.js +5 -0
  286. package/dist/server/action-discovery.js.map +1 -1
  287. package/dist/server/agent-chat-plugin.d.ts +6 -0
  288. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  289. package/dist/server/agent-chat-plugin.js +12 -3
  290. package/dist/server/agent-chat-plugin.js.map +1 -1
  291. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  292. package/dist/sharing/registry.d.ts.map +1 -1
  293. package/dist/sharing/registry.js +11 -0
  294. package/dist/sharing/registry.js.map +1 -1
  295. package/dist/templates/default/pnpm-workspace.yaml +1 -0
  296. package/dist/templates/workspace-root/pnpm-workspace.yaml +1 -0
  297. package/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
  298. package/docs/content/locales/ar-SA/template-design.mdx +1 -1
  299. package/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
  300. package/docs/content/locales/de-DE/template-design.mdx +1 -1
  301. package/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
  302. package/docs/content/locales/es-ES/template-design.mdx +1 -1
  303. package/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
  304. package/docs/content/locales/fr-FR/template-design.mdx +1 -1
  305. package/docs/content/locales/hi-IN/template-design.mdx +1 -1
  306. package/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
  307. package/docs/content/locales/ja-JP/template-design.mdx +1 -1
  308. package/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
  309. package/docs/content/locales/ko-KR/template-design.mdx +1 -1
  310. package/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
  311. package/docs/content/locales/pt-BR/template-design.mdx +1 -1
  312. package/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
  313. package/docs/content/locales/zh-CN/template-design.mdx +1 -1
  314. package/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
  315. package/docs/content/locales/zh-TW/template-design.mdx +1 -1
  316. package/docs/content/real-time-collaboration.mdx +2 -2
  317. package/docs/content/template-design.mdx +1 -1
  318. package/package.json +3 -7
  319. package/src/templates/default/pnpm-workspace.yaml +1 -0
  320. package/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
  321. package/corpus/core/src/brand-kit/fig/decode.ts +0 -410
  322. package/corpus/core/src/brand-kit/fig/extract-design-system.ts +0 -947
  323. package/corpus/core/src/brand-kit/fig/fig-to-html.ts +0 -1906
  324. package/corpus/templates/design/actions/import-figma.ts +0 -55
  325. package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +0 -6
  326. package/corpus/templates/design/server/handlers/import-figma-system.ts +0 -86
  327. package/corpus/templates/design/server/routes/api/import-figma-system.post.ts +0 -1
  328. package/corpus/templates/slides/server/handlers/import-figma-system.ts +0 -101
  329. package/corpus/templates/slides/server/routes/api/import-figma-system.post.ts +0 -1
  330. package/dist/brand-kit/fig/decode.d.ts +0 -33
  331. package/dist/brand-kit/fig/decode.d.ts.map +0 -1
  332. package/dist/brand-kit/fig/decode.js +0 -358
  333. package/dist/brand-kit/fig/decode.js.map +0 -1
  334. package/dist/brand-kit/fig/extract-design-system.d.ts +0 -44
  335. package/dist/brand-kit/fig/extract-design-system.d.ts.map +0 -1
  336. package/dist/brand-kit/fig/extract-design-system.js +0 -752
  337. package/dist/brand-kit/fig/extract-design-system.js.map +0 -1
  338. package/dist/brand-kit/fig/fig-to-html.d.ts +0 -246
  339. package/dist/brand-kit/fig/fig-to-html.d.ts.map +0 -1
  340. package/dist/brand-kit/fig/fig-to-html.js +0 -1506
  341. package/dist/brand-kit/fig/fig-to-html.js.map +0 -1
@@ -17,12 +17,12 @@ import type {
17
17
  } from "../shared/api.js";
18
18
  import {
19
19
  buildBuilderCmsExecutionPlan,
20
+ resolveBuilderCmsWriteEffect,
20
21
  validateBuilderCmsExecutionDryRun,
21
22
  } from "./_builder-cms-write-adapter.js";
22
23
  import {
23
24
  findOpenSourceChangeSet,
24
- getContentDatabaseSourceSnapshot,
25
- getExistingSource,
25
+ getContentDatabaseSourceSnapshotForWrite,
26
26
  resolveDatabaseForSourceMutation,
27
27
  sourceChangeSetKey,
28
28
  } from "./_database-source-utils.js";
@@ -103,6 +103,10 @@ export function buildBuilderSourceReviewPayload(args: {
103
103
  riskLevel: changeSet.riskLevel,
104
104
  riskReasons: changeSet.riskReasons,
105
105
  conflictState: changeSet.conflictState,
106
+ effect: resolveBuilderCmsWriteEffect({
107
+ source: args.source,
108
+ changeSet,
109
+ }),
106
110
  execution: latestExecution,
107
111
  };
108
112
  });
@@ -270,6 +274,8 @@ async function upsertExecutionGate(args: {
270
274
  source: ContentDatabaseSource;
271
275
  changeSet: ContentDatabaseSourceChangeSet;
272
276
  pushModeConfirmation?: ContentDatabaseSourcePushMode;
277
+ publicationTransition?: PrepareBuilderSourceReviewRequest["publicationTransition"];
278
+ confirmUnpublish?: boolean;
273
279
  ownerEmail: string;
274
280
  now: string;
275
281
  }) {
@@ -277,6 +283,8 @@ async function upsertExecutionGate(args: {
277
283
  source: args.source,
278
284
  changeSet: args.changeSet,
279
285
  pushModeConfirmation: args.pushModeConfirmation,
286
+ publicationTransition: args.publicationTransition,
287
+ confirmUnpublish: args.confirmUnpublish,
280
288
  });
281
289
  const db = getDb();
282
290
  const [existing] = await db
@@ -360,10 +368,22 @@ export default defineAction({
360
368
  schema: z.object({
361
369
  databaseId: z.string().optional().describe("Database ID"),
362
370
  documentId: z.string().optional().describe("Database document/page ID"),
371
+ sourceId: z
372
+ .string()
373
+ .optional()
374
+ .describe("Target source ID (defaults to the primary source)"),
363
375
  pushModeConfirmation: z
364
376
  .enum(["autosave", "draft", "publish"])
365
377
  .optional()
366
378
  .describe("Explicit push mode confirmation for the planned write"),
379
+ publicationTransition: z
380
+ .enum(["publish", "unpublish"])
381
+ .optional()
382
+ .describe("Explicit publication transition to validate at write time"),
383
+ confirmUnpublish: z
384
+ .boolean()
385
+ .optional()
386
+ .describe("Required explicit confirmation for unpublish transitions"),
367
387
  }),
368
388
  run: async (
369
389
  args: PrepareBuilderSourceReviewRequest,
@@ -372,13 +392,13 @@ export default defineAction({
372
392
  if (!database) throw new Error("Database not found.");
373
393
  await assertAccess("document", database.documentId, "editor");
374
394
 
375
- const sourceRecord = await getExistingSource(database.id);
376
- if (!sourceRecord || sourceRecord.sourceType !== "builder-cms") {
395
+ const snapshot = await getContentDatabaseSourceSnapshotForWrite(
396
+ database,
397
+ args.sourceId,
398
+ );
399
+ if (!snapshot || snapshot.sourceType !== "builder-cms") {
377
400
  throw new Error("Attach a Builder CMS source before reviewing updates.");
378
401
  }
379
-
380
- const snapshot = await getContentDatabaseSourceSnapshot(database);
381
- if (!snapshot) throw new Error("Attach a source before reviewing updates.");
382
402
  const reviewableChanges = snapshot.changeSets.filter(
383
403
  (changeSet) =>
384
404
  changeSet.direction === "outbound" &&
@@ -397,7 +417,7 @@ export default defineAction({
397
417
  for (const changeSet of reviewableChanges) {
398
418
  approvedIds.push(
399
419
  await approveChangeSetForReview({
400
- sourceId: sourceRecord.id,
420
+ sourceId: snapshot.id,
401
421
  ownerEmail: database.ownerEmail,
402
422
  changeSet,
403
423
  reviewerEmail,
@@ -406,7 +426,10 @@ export default defineAction({
406
426
  );
407
427
  }
408
428
 
409
- const approvedSnapshot = await getContentDatabaseSourceSnapshot(database);
429
+ const approvedSnapshot = await getContentDatabaseSourceSnapshotForWrite(
430
+ database,
431
+ args.sourceId,
432
+ );
410
433
  if (!approvedSnapshot) throw new Error("Builder source disappeared.");
411
434
  const approvedChangeSets = approvedSnapshot.changeSets.filter(
412
435
  (changeSet) =>
@@ -417,6 +440,8 @@ export default defineAction({
417
440
  source: approvedSnapshot,
418
441
  changeSet,
419
442
  pushModeConfirmation: args.pushModeConfirmation,
443
+ publicationTransition: args.publicationTransition,
444
+ confirmUnpublish: args.confirmUnpublish,
420
445
  ownerEmail: database.ownerEmail,
421
446
  now,
422
447
  });
@@ -425,19 +450,26 @@ export default defineAction({
425
450
  await getDb()
426
451
  .update(schema.contentDatabaseSources)
427
452
  .set({ updatedAt: now })
428
- .where(eq(schema.contentDatabaseSources.id, sourceRecord.id));
453
+ .where(eq(schema.contentDatabaseSources.id, snapshot.id));
429
454
 
430
- const response = await getContentDatabaseResponse(database.id);
431
- const source = response.source;
432
- if (!source) throw new Error("Builder source disappeared.");
433
- const reviewedChangeSets = source.changeSets.filter((changeSet) =>
455
+ const reviewedSnapshot = await getContentDatabaseSourceSnapshotForWrite(
456
+ database,
457
+ args.sourceId,
458
+ );
459
+ if (!reviewedSnapshot) throw new Error("Builder source disappeared.");
460
+ // Build the review payload from the TARGET source snapshot, not
461
+ // response.source (which is always the primary). Re-read after the gate
462
+ // upsert so newly validated/blocked/stale execution rows are visible to
463
+ // the returned review payload.
464
+ const reviewedChangeSets = reviewedSnapshot.changeSets.filter((changeSet) =>
434
465
  approvedIds.includes(changeSet.id),
435
466
  );
467
+ const response = await getContentDatabaseResponse(database.id);
436
468
 
437
469
  return {
438
470
  ...response,
439
471
  review: buildBuilderSourceReviewPayload({
440
- source,
472
+ source: reviewedSnapshot,
441
473
  changeSets: reviewedChangeSets,
442
474
  }),
443
475
  };
@@ -4,8 +4,8 @@ import { z } from "zod";
4
4
 
5
5
  import type { ContentDatabaseSourceStatusResponse } from "../shared/api.js";
6
6
  import {
7
- getContentDatabaseSourceSnapshot,
8
- getExistingSource,
7
+ getContentDatabaseSourceSnapshotForWrite,
8
+ getExistingSourceForWrite,
9
9
  resyncBuilderCmsSourceSnapshot,
10
10
  resyncMockSourceSnapshot,
11
11
  resolveDatabaseForSourceMutation,
@@ -18,13 +18,17 @@ export default defineAction({
18
18
  schema: z.object({
19
19
  databaseId: z.string().optional().describe("Database ID"),
20
20
  documentId: z.string().optional().describe("Database document/page ID"),
21
+ sourceId: z
22
+ .string()
23
+ .optional()
24
+ .describe("Target source ID (defaults to the primary source)"),
21
25
  }),
22
26
  run: async (args): Promise<ContentDatabaseSourceStatusResponse> => {
23
27
  const database = await resolveDatabaseForSourceMutation(args);
24
28
  if (!database) throw new Error("Database not found.");
25
29
  await assertAccess("document", database.documentId, "editor");
26
30
 
27
- const source = await getExistingSource(database.id);
31
+ const source = await getExistingSourceForWrite(database.id, args.sourceId);
28
32
  if (!source) {
29
33
  return {
30
34
  database: serializeDatabase(database),
@@ -45,7 +49,10 @@ export default defineAction({
45
49
  } else {
46
50
  throw new Error(`Unsupported source type "${source.sourceType}".`);
47
51
  }
48
- const snapshot = await getContentDatabaseSourceSnapshot(database);
52
+ const snapshot = await getContentDatabaseSourceSnapshotForWrite(
53
+ database,
54
+ args.sourceId,
55
+ );
49
56
 
50
57
  return {
51
58
  database: serializeDatabase(database),
@@ -10,7 +10,7 @@ import type {
10
10
  ReviewContentDatabaseSourceChangeSetRequest,
11
11
  } from "../shared/api.js";
12
12
  import {
13
- getExistingSource,
13
+ getExistingSourceForWrite,
14
14
  resolveDatabaseForSourceMutation,
15
15
  } from "./_database-source-utils.js";
16
16
  import { getContentDatabaseResponse } from "./_database-utils.js";
@@ -28,6 +28,10 @@ export default defineAction({
28
28
  schema: z.object({
29
29
  databaseId: z.string().optional().describe("Database ID"),
30
30
  documentId: z.string().optional().describe("Database document/page ID"),
31
+ sourceId: z
32
+ .string()
33
+ .optional()
34
+ .describe("Target source ID (defaults to the primary source)"),
31
35
  changeSetId: z.string().describe("Source change-set ID"),
32
36
  decision: z
33
37
  .enum(["approve", "reject"])
@@ -41,7 +45,7 @@ export default defineAction({
41
45
  if (!database) throw new Error("Database not found.");
42
46
  await assertAccess("document", database.documentId, "editor");
43
47
 
44
- const source = await getExistingSource(database.id);
48
+ const source = await getExistingSourceForWrite(database.id, args.sourceId);
45
49
  if (!source) throw new Error("Attach a source before reviewing changes.");
46
50
 
47
51
  const [changeSet] = await getDb()
@@ -1,22 +1,31 @@
1
1
  import { defineAction } from "@agent-native/core";
2
2
  import { assertAccess } from "@agent-native/core/sharing";
3
- import { asc, eq } from "drizzle-orm";
3
+ import { eq } from "drizzle-orm";
4
4
  import { z } from "zod";
5
5
 
6
6
  import { getDb, schema } from "../server/db/index.js";
7
7
  import {
8
8
  type ContentDatabaseResponse,
9
9
  type ContentDatabaseSourcePushMode,
10
+ type ContentDatabaseSourceWriteMode,
10
11
  type SetContentDatabaseSourceWriteModeRequest,
11
12
  } from "../shared/api.js";
12
13
  import {
13
14
  buildBuilderCmsWriteModeJson,
14
15
  type BuilderCmsLiveWriteMode,
15
16
  } from "./_builder-cms-write-settings.js";
16
- import { resolveDatabaseForSourceMutation } from "./_database-source-utils.js";
17
+ import {
18
+ getExistingSourceForWrite,
19
+ resolveDatabaseForSourceMutation,
20
+ } from "./_database-source-utils.js";
17
21
  import { getContentDatabaseResponse } from "./_database-utils.js";
18
22
 
19
- const writeModeSchema = z.enum(["autosave", "draft", "publish"]);
23
+ const legacyWriteModeSchema = z.enum(["autosave", "draft", "publish"]);
24
+ const sourceWriteModeSchema = z.enum([
25
+ "read_only",
26
+ "stage_only",
27
+ "publish_updates",
28
+ ]);
20
29
 
21
30
  function executableWriteModes(
22
31
  modes: readonly ContentDatabaseSourcePushMode[] | undefined,
@@ -29,17 +38,31 @@ function executableWriteModes(
29
38
 
30
39
  export default defineAction({
31
40
  description:
32
- "Enable or disable live Builder CMS writes for one source. Live writes stay off by default and can only be enabled for the safe Builder test collection with explicit allowed write modes.",
41
+ "Set the tiered Builder CMS write mode for one source. Writes stay off by default and can only be enabled for the safe Builder test collection.",
33
42
  schema: z.object({
34
43
  databaseId: z.string().optional().describe("Database ID"),
35
44
  documentId: z.string().optional().describe("Database document/page ID"),
45
+ sourceId: z
46
+ .string()
47
+ .optional()
48
+ .describe("Target source ID (defaults to the primary source)"),
36
49
  liveWritesEnabled: z
37
50
  .boolean()
51
+ .optional()
38
52
  .describe("Whether this source may execute guarded live Builder writes"),
53
+ writeMode: sourceWriteModeSchema
54
+ .optional()
55
+ .describe("Tiered Builder write mode for this source"),
56
+ allowPublicationTransitions: z
57
+ .boolean()
58
+ .optional()
59
+ .describe(
60
+ "Allow explicit per-item publish/unpublish transitions in publish updates mode",
61
+ ),
39
62
  allowedWriteModes: z
40
- .array(writeModeSchema)
63
+ .array(legacyWriteModeSchema)
41
64
  .optional()
42
- .describe("Explicit Builder write modes allowed for this source"),
65
+ .describe("Legacy Builder write modes allowed for this source"),
43
66
  allowDraftWrites: z
44
67
  .boolean()
45
68
  .optional()
@@ -59,12 +82,7 @@ export default defineAction({
59
82
  await assertAccess("document", database.documentId, "editor");
60
83
 
61
84
  const db = getDb();
62
- const [source] = await db
63
- .select()
64
- .from(schema.contentDatabaseSources)
65
- .where(eq(schema.contentDatabaseSources.databaseId, database.id))
66
- .orderBy(asc(schema.contentDatabaseSources.createdAt))
67
- .limit(1);
85
+ const source = await getExistingSourceForWrite(database.id, args.sourceId);
68
86
  if (!source) {
69
87
  throw new Error(
70
88
  "Attach a Builder CMS source before changing write mode.",
@@ -82,6 +100,8 @@ export default defineAction({
82
100
  capabilitiesJson: source.capabilitiesJson,
83
101
  metadataJson: source.metadataJson,
84
102
  liveWritesEnabled: args.liveWritesEnabled,
103
+ writeMode: args.writeMode as ContentDatabaseSourceWriteMode | undefined,
104
+ allowPublicationTransitions: args.allowPublicationTransitions,
85
105
  allowedWriteModes: executableWriteModes(args.allowedWriteModes),
86
106
  allowDraftWrites: args.allowDraftWrites,
87
107
  allowPublishWrites: args.allowPublishWrites,
@@ -7,8 +7,7 @@ import { getDb, schema } from "../server/db/index.js";
7
7
  import type { ContentDatabaseResponse } from "../shared/api.js";
8
8
  import {
9
9
  findOpenSourceChangeSet,
10
- getContentDatabaseSourceSnapshot,
11
- getExistingSource,
10
+ getContentDatabaseSourceSnapshotForWrite,
12
11
  resolveDatabaseForSourceMutation,
13
12
  sourceChangeSetKey,
14
13
  } from "./_database-source-utils.js";
@@ -20,24 +19,29 @@ export default defineAction({
20
19
  schema: z.object({
21
20
  databaseId: z.string().optional().describe("Database ID"),
22
21
  documentId: z.string().optional().describe("Database document/page ID"),
22
+ sourceId: z
23
+ .string()
24
+ .optional()
25
+ .describe("Target source ID (defaults to the primary source)"),
23
26
  }),
24
27
  run: async (args): Promise<ContentDatabaseResponse> => {
25
28
  const database = await resolveDatabaseForSourceMutation(args);
26
29
  if (!database) throw new Error("Database not found.");
27
30
  await assertAccess("document", database.documentId, "editor");
28
31
 
29
- const source = await getExistingSource(database.id);
32
+ const source = await getContentDatabaseSourceSnapshotForWrite(
33
+ database,
34
+ args.sourceId,
35
+ );
30
36
  if (!source || source.sourceType !== "builder-cms") {
31
37
  throw new Error("Attach a Builder CMS source before staging a revision.");
32
38
  }
33
39
 
34
- const snapshot = await getContentDatabaseSourceSnapshot(database);
35
- const pendingOutboundChanges =
36
- snapshot?.changeSets.filter(
37
- (changeSet) =>
38
- changeSet.direction === "outbound" &&
39
- changeSet.state === "pending_push",
40
- ) ?? [];
40
+ const pendingOutboundChanges = source.changeSets.filter(
41
+ (changeSet) =>
42
+ changeSet.direction === "outbound" &&
43
+ changeSet.state === "pending_push",
44
+ );
41
45
 
42
46
  if (pendingOutboundChanges.length === 0) {
43
47
  throw new Error("No pending local Builder changes to stage.");
@@ -14,7 +14,7 @@ import {
14
14
  validateBuilderCmsExecutionDryRun,
15
15
  } from "./_builder-cms-write-adapter.js";
16
16
  import {
17
- getContentDatabaseSourceSnapshot,
17
+ getContentDatabaseSourceSnapshotForWrite,
18
18
  resolveDatabaseForSourceMutation,
19
19
  } from "./_database-source-utils.js";
20
20
  import { getContentDatabaseResponse } from "./_database-utils.js";
@@ -36,11 +36,27 @@ export default defineAction({
36
36
  schema: z.object({
37
37
  databaseId: z.string().optional().describe("Database ID"),
38
38
  documentId: z.string().optional().describe("Database document/page ID"),
39
+ sourceId: z
40
+ .string()
41
+ .optional()
42
+ .describe("Target source ID (defaults to the primary source)"),
39
43
  changeSetId: z.string().describe("Approved source change-set ID"),
40
44
  idempotencyKey: z
41
45
  .string()
42
46
  .optional()
43
47
  .describe("Optional execution idempotency key to validate"),
48
+ pushModeConfirmation: z
49
+ .enum(["autosave", "draft", "publish"])
50
+ .optional()
51
+ .describe("Explicit push mode confirmation for the validated write"),
52
+ publicationTransition: z
53
+ .enum(["publish", "unpublish"])
54
+ .optional()
55
+ .describe("Explicit publication transition to validate at write time"),
56
+ confirmUnpublish: z
57
+ .boolean()
58
+ .optional()
59
+ .describe("Required explicit confirmation for unpublish transitions"),
44
60
  }),
45
61
  run: async (
46
62
  args: ValidateBuilderSourceExecutionRequest,
@@ -49,7 +65,10 @@ export default defineAction({
49
65
  if (!database) throw new Error("Database not found.");
50
66
  await assertAccess("document", database.documentId, "editor");
51
67
 
52
- const source = await getContentDatabaseSourceSnapshot(database);
68
+ const source = await getContentDatabaseSourceSnapshotForWrite(
69
+ database,
70
+ args.sourceId,
71
+ );
53
72
  if (!source || source.sourceType !== "builder-cms") {
54
73
  throw new Error(
55
74
  "Attach a Builder CMS source before validating execution.",
@@ -64,7 +83,10 @@ export default defineAction({
64
83
  const plan = buildBuilderCmsExecutionPlan({
65
84
  source,
66
85
  changeSet,
67
- pushModeConfirmation: changeSet.pushMode ?? undefined,
86
+ pushModeConfirmation:
87
+ args.pushModeConfirmation ?? changeSet.pushMode ?? undefined,
88
+ publicationTransition: args.publicationTransition,
89
+ confirmUnpublish: args.confirmUnpublish,
68
90
  });
69
91
  const expectedKey = builderCmsExecutionIdempotencyKey({
70
92
  sourceId: source.id,
@@ -26,6 +26,8 @@ import {
26
26
  } from "@tabler/icons-react";
27
27
  import { useState } from "react";
28
28
 
29
+ import { ContentReferencePreview } from "@/components/editor/ContentReferencePreview";
30
+
29
31
  function SidecarBadge({ rawRef }: { rawRef: string }) {
30
32
  return (
31
33
  <div className="mt-2 truncate text-[11px] text-muted-foreground">
@@ -180,7 +182,15 @@ export function BuilderSymbolRead({
180
182
  </div>
181
183
  <div className="mt-1 text-xs text-muted-foreground">
182
184
  {data.model || "model unknown"} / {data.entry || "entry unknown"}
185
+ {data.dynamic ? " / dynamic" : ""}
183
186
  </div>
187
+ {data.source ? (
188
+ <ContentReferencePreview
189
+ sourcePath={data.source}
190
+ title="Symbol source"
191
+ className="mb-0"
192
+ />
193
+ ) : null}
184
194
  <SidecarBadge rawRef={data.rawRef} />
185
195
  </section>
186
196
  );
@@ -47,6 +47,7 @@ const BUBBLE_TOOLBAR_EXCLUDED_NODE_TYPES = new Set([
47
47
  "image",
48
48
  "video",
49
49
  "audio",
50
+ "contentReference",
50
51
  "localMdxComponent",
51
52
  ]);
52
53
 
@@ -0,0 +1,185 @@
1
+ import { useT } from "@agent-native/core/client";
2
+ import { IconExternalLink, IconFileText } from "@tabler/icons-react";
3
+ import { lazy, Suspense, useMemo } from "react";
4
+ import { useNavigate } from "react-router";
5
+
6
+ import { Button } from "@/components/ui/button";
7
+ import { useDocument, useDocuments } from "@/hooks/use-documents";
8
+ import { cn } from "@/lib/utils";
9
+
10
+ const ReadonlyReferenceEditor = lazy(async () => {
11
+ const module = await import("./VisualEditor");
12
+ return { default: module.VisualEditor };
13
+ });
14
+
15
+ const MAX_CONTENT_REFERENCE_PREVIEW_DEPTH = 1;
16
+
17
+ function trimSlashes(path: string) {
18
+ return path.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\/+$/, "");
19
+ }
20
+
21
+ function dirname(path: string) {
22
+ const normalized = trimSlashes(path);
23
+ const index = normalized.lastIndexOf("/");
24
+ return index >= 0 ? normalized.slice(0, index) : "";
25
+ }
26
+
27
+ function normalizePathParts(path: string) {
28
+ const parts: string[] = [];
29
+ for (const part of path.split("/")) {
30
+ if (!part || part === ".") continue;
31
+ if (part === "..") {
32
+ if (parts.length === 0) return null;
33
+ parts.pop();
34
+ continue;
35
+ }
36
+ parts.push(part);
37
+ }
38
+ return parts.join("/");
39
+ }
40
+
41
+ export function resolveContentReferencePath(
42
+ sourcePath: string | null | undefined,
43
+ currentPath?: string | null,
44
+ ) {
45
+ if (typeof sourcePath !== "string" || !sourcePath.trim()) return null;
46
+ const normalized = sourcePath.replace(/\\/g, "/").trim();
47
+ if (/^[a-z][a-z0-9+.-]*:/i.test(normalized)) return null;
48
+ const base =
49
+ normalized.startsWith("./") || normalized.startsWith("../")
50
+ ? dirname(currentPath ?? "")
51
+ : "";
52
+ return normalizePathParts(base ? `${base}/${normalized}` : normalized);
53
+ }
54
+
55
+ export function ContentReferencePreview({
56
+ sourcePath,
57
+ currentPath,
58
+ title,
59
+ className,
60
+ referenceDepth = 0,
61
+ }: {
62
+ sourcePath?: string | null;
63
+ currentPath?: string | null;
64
+ title?: string | null;
65
+ className?: string;
66
+ referenceDepth?: number;
67
+ }) {
68
+ const t = useT();
69
+ const navigate = useNavigate();
70
+ const documentsQuery = useDocuments();
71
+ const resolvedPath = useMemo(
72
+ () => resolveContentReferencePath(sourcePath, currentPath),
73
+ [currentPath, sourcePath],
74
+ );
75
+ const document = useMemo(() => {
76
+ if (!resolvedPath) return null;
77
+ return (documentsQuery.data ?? []).find(
78
+ (candidate) =>
79
+ candidate.source?.mode === "local-files" &&
80
+ trimSlashes(candidate.source.path ?? "") === resolvedPath,
81
+ );
82
+ }, [documentsQuery.data, resolvedPath]);
83
+ const documentQuery = useDocument(document?.id ?? null);
84
+ const body = documentQuery.data?.content ?? "";
85
+ const displayTitle =
86
+ title?.trim() ||
87
+ documentQuery.data?.title ||
88
+ document?.title ||
89
+ t("editor.reference.defaultTitle");
90
+ const isSelfReference =
91
+ !!resolvedPath &&
92
+ !!currentPath &&
93
+ resolvedPath === trimSlashes(currentPath);
94
+ const isNestedTooDeep = referenceDepth >= MAX_CONTENT_REFERENCE_PREVIEW_DEPTH;
95
+
96
+ return (
97
+ <section
98
+ className={cn(
99
+ "content-reference-preview my-4 rounded-md border border-dashed bg-muted/20 text-sm",
100
+ className,
101
+ )}
102
+ data-content-reference={resolvedPath ?? sourcePath ?? ""}
103
+ >
104
+ <div className="flex min-h-10 items-center gap-2 border-b px-3 py-2">
105
+ <IconFileText className="size-4 shrink-0 text-muted-foreground" />
106
+ <div className="min-w-0 flex-1">
107
+ <div className="truncate font-medium text-foreground">
108
+ {displayTitle}
109
+ </div>
110
+ <div className="truncate text-xs text-muted-foreground">
111
+ {resolvedPath ?? sourcePath ?? t("editor.reference.pathMissing")}
112
+ </div>
113
+ </div>
114
+ {document ? (
115
+ <Button
116
+ type="button"
117
+ size="sm"
118
+ variant="ghost"
119
+ className="h-8 gap-1.5 px-2 text-xs"
120
+ onClick={() =>
121
+ navigate(`/page/${document.id}`, { flushSync: true })
122
+ }
123
+ >
124
+ <IconExternalLink className="size-3.5" />
125
+ {t("editor.reference.open")}
126
+ </Button>
127
+ ) : null}
128
+ </div>
129
+ <div className="max-h-[520px] overflow-auto px-3 py-3">
130
+ {!resolvedPath ? (
131
+ <div className="text-sm text-muted-foreground">
132
+ {t("editor.reference.missingPath")}
133
+ </div>
134
+ ) : isSelfReference ? (
135
+ <div className="text-sm text-muted-foreground">
136
+ {t("editor.reference.selfReference")}
137
+ </div>
138
+ ) : documentsQuery.isLoading ? (
139
+ <div className="text-sm text-muted-foreground">
140
+ {t("editor.reference.loading")}
141
+ </div>
142
+ ) : !document ? (
143
+ <div className="text-sm text-muted-foreground">
144
+ {t("editor.reference.notFound")}
145
+ </div>
146
+ ) : documentQuery.isLoading ? (
147
+ <div className="text-sm text-muted-foreground">
148
+ {t("editor.reference.loading")}
149
+ </div>
150
+ ) : documentQuery.isError ? (
151
+ <div className="text-sm text-muted-foreground">
152
+ {t("editor.reference.loadError")}
153
+ </div>
154
+ ) : isNestedTooDeep ? (
155
+ <div className="text-sm text-muted-foreground">
156
+ {t("editor.reference.nestedSkipped")}
157
+ </div>
158
+ ) : body.trim() ? (
159
+ <Suspense
160
+ fallback={
161
+ <div className="text-sm text-muted-foreground">
162
+ {t("editor.reference.loading")}
163
+ </div>
164
+ }
165
+ >
166
+ <ReadonlyReferenceEditor
167
+ key={document.id}
168
+ documentId={document.id}
169
+ content={body}
170
+ onChange={() => {}}
171
+ editable={false}
172
+ localFileMode
173
+ localFilePath={resolvedPath}
174
+ referenceDepth={referenceDepth + 1}
175
+ />
176
+ </Suspense>
177
+ ) : (
178
+ <div className="text-sm text-muted-foreground">
179
+ {t("editor.reference.empty")}
180
+ </div>
181
+ )}
182
+ </div>
183
+ </section>
184
+ );
185
+ }