@agent-native/core 0.90.1 → 0.90.2

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 (295) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  5. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
  6. package/corpus/core/src/client/use-action.ts +39 -2
  7. package/corpus/core/src/client/use-db-sync.ts +44 -18
  8. package/corpus/core/src/db/create-get-db.ts +32 -2
  9. package/corpus/core/src/server/framework-request-handler.ts +52 -4
  10. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
  11. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
  12. package/corpus/templates/assets/AGENTS.md +23 -4
  13. package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
  14. package/corpus/templates/assets/actions/_helpers.ts +8 -1
  15. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
  16. package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
  17. package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
  18. package/corpus/templates/assets/actions/generate-image.ts +342 -25
  19. package/corpus/templates/assets/actions/get-library.ts +4 -3
  20. package/corpus/templates/assets/actions/navigate.ts +2 -1
  21. package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
  22. package/corpus/templates/assets/actions/update-asset.ts +1 -0
  23. package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
  24. package/corpus/templates/assets/actions/view-screen.ts +8 -1
  25. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
  26. package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
  27. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
  28. package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
  29. package/corpus/templates/assets/app/i18n-data.ts +650 -0
  30. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
  31. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
  32. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
  33. package/corpus/templates/assets/app/routes/library.tsx +43 -1
  34. package/corpus/templates/assets/server/lib/generation.ts +366 -44
  35. package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
  36. package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
  37. package/corpus/templates/assets/shared/api.ts +47 -0
  38. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
  39. package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
  40. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
  41. package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
  42. package/corpus/templates/clips/app/routes/record.tsx +157 -68
  43. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
  44. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
  45. package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
  46. package/corpus/templates/content/AGENTS.md +52 -52
  47. package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
  48. package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
  49. package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
  50. package/corpus/templates/content/actions/_database-utils.ts +44 -4
  51. package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
  52. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
  53. package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
  54. package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
  55. package/corpus/templates/content/actions/delete-content-database.ts +1 -0
  56. package/corpus/templates/content/actions/get-document.ts +11 -2
  57. package/corpus/templates/content/actions/list-content-databases.ts +57 -9
  58. package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
  59. package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
  60. package/corpus/templates/content/actions/restore-content-database.ts +1 -0
  61. package/corpus/templates/content/actions/update-document.ts +43 -0
  62. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
  63. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
  64. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
  65. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
  66. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
  67. package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
  68. package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
  69. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
  70. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
  71. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
  72. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
  73. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
  74. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
  75. package/corpus/templates/content/app/global.css +41 -0
  76. package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
  77. package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
  78. package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
  79. package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
  80. package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
  81. package/corpus/templates/content/app/i18n-data.ts +355 -0
  82. package/corpus/templates/content/app/root.tsx +22 -0
  83. package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
  84. package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
  85. package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
  86. package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
  87. package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
  88. package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
  89. package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
  90. package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
  93. package/corpus/templates/content/shared/api.ts +13 -0
  94. package/corpus/templates/content/shared/builder-mdx.ts +16 -3
  95. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
  96. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
  97. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
  98. package/corpus/templates/design/AGENTS.md +9 -2
  99. package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
  100. package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
  101. package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
  102. package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
  103. package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
  104. package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
  105. package/corpus/templates/design/actions/create-component.ts +29 -60
  106. package/corpus/templates/design/actions/create-file.ts +9 -2
  107. package/corpus/templates/design/actions/duplicate-design.ts +31 -2
  108. package/corpus/templates/design/actions/edit-design.ts +36 -38
  109. package/corpus/templates/design/actions/generate-design-title.ts +87 -0
  110. package/corpus/templates/design/actions/generate-design.ts +53 -18
  111. package/corpus/templates/design/actions/generate-screens.ts +108 -4
  112. package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
  113. package/corpus/templates/design/actions/insert-asset.ts +34 -29
  114. package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
  115. package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
  116. package/corpus/templates/design/actions/present-design-variants.ts +7 -2
  117. package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
  118. package/corpus/templates/design/actions/update-file.ts +190 -75
  119. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
  120. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
  121. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
  122. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
  123. package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
  124. package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
  125. package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
  126. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
  127. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
  129. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
  130. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
  131. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
  132. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
  133. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
  134. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
  135. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
  136. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
  137. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
  138. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
  139. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
  140. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
  141. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
  142. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
  143. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
  144. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
  145. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
  146. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
  147. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
  148. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
  149. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
  150. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
  151. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
  152. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
  153. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
  154. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
  155. package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
  156. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
  157. package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
  158. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
  159. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
  160. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
  161. package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
  162. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
  163. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
  164. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
  165. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
  166. package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
  167. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
  168. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
  169. package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
  170. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
  171. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
  172. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
  173. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
  174. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
  178. package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
  179. package/corpus/templates/design/app/components/design/types.ts +15 -0
  180. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
  181. package/corpus/templates/design/app/global.css +8 -8
  182. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
  183. package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
  184. package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
  185. package/corpus/templates/design/app/i18n-data.ts +1664 -152
  186. package/corpus/templates/design/app/i18n-motion.ts +563 -0
  187. package/corpus/templates/design/app/lib/design-import.ts +73 -0
  188. package/corpus/templates/design/app/pages/Index.tsx +41 -22
  189. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
  190. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
  191. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
  192. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
  193. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
  194. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
  195. package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
  196. package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
  197. package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
  198. package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
  199. package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
  200. package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
  201. package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
  202. package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
  249. package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
  250. package/corpus/templates/design/server/source-workspace.ts +139 -59
  251. package/corpus/templates/design/shared/board-file.ts +11 -2
  252. package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
  253. package/corpus/templates/design/shared/canvas-math.ts +354 -35
  254. package/corpus/templates/design/shared/code-layer.ts +277 -8
  255. package/corpus/templates/design/shared/design-state.ts +11 -4
  256. package/corpus/templates/design/shared/interaction-states.ts +548 -0
  257. package/corpus/templates/design/shared/motion-compiler.ts +186 -23
  258. package/corpus/templates/design/shared/motion-easing.ts +294 -0
  259. package/corpus/templates/design/shared/motion-timeline.ts +400 -34
  260. package/corpus/templates/design/shared/prompt-title.ts +95 -0
  261. package/corpus/templates/design/shared/responsive-classes.ts +448 -10
  262. package/corpus/templates/design/shared/screen-annotation.ts +46 -0
  263. package/corpus/templates/design/shared/shader-fills.ts +1009 -0
  264. package/corpus/templates/design/shared/shader-presets.ts +852 -4
  265. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
  266. package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
  267. package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
  268. package/dist/client/AgentPanel.js +1 -1
  269. package/dist/client/AgentPanel.js.map +1 -1
  270. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  271. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
  272. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  273. package/dist/client/use-action.d.ts +3 -0
  274. package/dist/client/use-action.d.ts.map +1 -1
  275. package/dist/client/use-action.js +34 -3
  276. package/dist/client/use-action.js.map +1 -1
  277. package/dist/client/use-db-sync.d.ts +4 -0
  278. package/dist/client/use-db-sync.d.ts.map +1 -1
  279. package/dist/client/use-db-sync.js +37 -18
  280. package/dist/client/use-db-sync.js.map +1 -1
  281. package/dist/collab/routes.d.ts +1 -1
  282. package/dist/collab/struct-routes.d.ts +1 -1
  283. package/dist/db/create-get-db.d.ts +25 -0
  284. package/dist/db/create-get-db.d.ts.map +1 -1
  285. package/dist/db/create-get-db.js +25 -2
  286. package/dist/db/create-get-db.js.map +1 -1
  287. package/dist/observability/routes.d.ts +3 -3
  288. package/dist/secrets/routes.d.ts +9 -9
  289. package/dist/server/framework-request-handler.d.ts.map +1 -1
  290. package/dist/server/framework-request-handler.js +37 -1
  291. package/dist/server/framework-request-handler.js.map +1 -1
  292. package/package.json +1 -1
  293. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
  294. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
  295. package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
@@ -30,10 +30,12 @@ type DatabaseMembershipRow = {
30
30
  item: typeof schema.contentDatabaseItems.$inferSelect;
31
31
  database: typeof schema.contentDatabases.$inferSelect;
32
32
  sourceId?: string | null;
33
+ bodyHydrationQueueId?: string | null;
33
34
  };
34
35
 
35
36
  export function serializeBodyHydration(
36
37
  item: typeof schema.contentDatabaseItems.$inferSelect,
38
+ options: { queued?: boolean } = {},
37
39
  ): ContentDatabaseBodyHydration {
38
40
  const status = item.bodyHydrationStatus;
39
41
  return {
@@ -43,7 +45,9 @@ export function serializeBodyHydration(
43
45
  status === "hydrated" ||
44
46
  status === "error"
45
47
  ? status
46
- : "hydrated",
48
+ : options.queued
49
+ ? "pending"
50
+ : "hydrated",
47
51
  attemptedAt: item.bodyHydrationAttemptedAt,
48
52
  error: item.bodyHydrationError,
49
53
  version: item.bodyHydrationVersion,
@@ -59,7 +63,9 @@ export function serializeDatabaseMembership(
59
63
  databaseTitle: row.database.title || "Untitled database",
60
64
  position: row.item.position,
61
65
  sourceId: row.sourceId ?? null,
62
- bodyHydration: serializeBodyHydration(row.item),
66
+ bodyHydration: serializeBodyHydration(row.item, {
67
+ queued: !!row.bodyHydrationQueueId,
68
+ }),
63
69
  };
64
70
  }
65
71
 
@@ -190,17 +196,43 @@ export async function getContentDatabaseResponse(
190
196
  )
191
197
  : [];
192
198
  const documentById = new Map(documents.map((doc) => [doc.id, doc]));
199
+ const queuedBodyHydrationItemIds =
200
+ items.length > 0
201
+ ? new Set(
202
+ (
203
+ await db
204
+ .select({
205
+ databaseItemId:
206
+ schema.contentDatabaseBodyHydrationQueue.databaseItemId,
207
+ })
208
+ .from(schema.contentDatabaseBodyHydrationQueue)
209
+ .where(
210
+ inArray(
211
+ schema.contentDatabaseBodyHydrationQueue.databaseItemId,
212
+ items.map((item) => item.id),
213
+ ),
214
+ )
215
+ ).map((row) => row.databaseItemId),
216
+ )
217
+ : new Set<string>();
193
218
 
194
219
  const serializedItems = [];
195
220
  for (const item of items) {
196
221
  const document = documentById.get(item.documentId);
197
222
  if (!document) continue;
223
+ const bodyHydrationQueued = queuedBodyHydrationItemIds.has(item.id);
198
224
  serializedItems.push({
199
225
  id: item.id,
200
226
  databaseId: item.databaseId,
201
- document: serializeDocument(document, { item, database }),
227
+ document: serializeDocument(document, {
228
+ item,
229
+ database,
230
+ bodyHydrationQueueId: bodyHydrationQueued ? item.id : null,
231
+ }),
202
232
  position: item.position,
203
- bodyHydration: serializeBodyHydration(item),
233
+ bodyHydration: serializeBodyHydration(item, {
234
+ queued: bodyHydrationQueued,
235
+ }),
204
236
  properties: await listPropertiesForDatabase(databaseId, document),
205
237
  });
206
238
  }
@@ -312,6 +344,7 @@ export async function getDatabaseItemByDocumentId(
312
344
  item: schema.contentDatabaseItems,
313
345
  database: schema.contentDatabases,
314
346
  sourceId: schema.contentDatabaseSourceRows.sourceId,
347
+ bodyHydrationQueueId: schema.contentDatabaseBodyHydrationQueue.id,
315
348
  })
316
349
  .from(schema.contentDatabaseItems)
317
350
  .innerJoin(
@@ -325,6 +358,13 @@ export async function getDatabaseItemByDocumentId(
325
358
  schema.contentDatabaseItems.id,
326
359
  ),
327
360
  )
361
+ .leftJoin(
362
+ schema.contentDatabaseBodyHydrationQueue,
363
+ eq(
364
+ schema.contentDatabaseBodyHydrationQueue.databaseItemId,
365
+ schema.contentDatabaseItems.id,
366
+ ),
367
+ )
328
368
  .where(and(...clauses));
329
369
  return row ?? null;
330
370
  }
@@ -24,7 +24,11 @@ export async function getNotionDocumentOwner(documentId: string) {
24
24
  }
25
25
 
26
26
  export function resolveDocumentId(args: { documentId?: string; id?: string }) {
27
- const documentId = args.documentId || args.id;
28
- if (!documentId) throw new Error("documentId is required");
27
+ const documentId = args.documentId?.trim() || args.id?.trim();
28
+ if (!documentId) {
29
+ throw Object.assign(new Error("documentId is required"), {
30
+ statusCode: 400,
31
+ });
32
+ }
29
33
  return documentId;
30
34
  }
@@ -80,6 +80,14 @@ export default defineAction({
80
80
  databaseId: z.string().optional().describe("Database ID"),
81
81
  documentId: z.string().optional().describe("Database document/page ID"),
82
82
  sourceFieldId: z.string().describe("Source field mapping ID"),
83
+ sourceId: z
84
+ .string()
85
+ .optional()
86
+ .describe("Source ID for stale field ID fallback"),
87
+ sourceFieldKey: z
88
+ .string()
89
+ .optional()
90
+ .describe("Stable source field key for stale field ID fallback"),
83
91
  }),
84
92
  run: async (
85
93
  args: AddContentDatabaseSourceFieldPropertyRequest,
@@ -89,10 +97,24 @@ export default defineAction({
89
97
  await assertAccess("document", database.documentId, "editor");
90
98
 
91
99
  const db = getDb();
92
- const [field] = await db
100
+ let [field] = await db
93
101
  .select()
94
102
  .from(schema.contentDatabaseSourceFields)
95
103
  .where(eq(schema.contentDatabaseSourceFields.id, args.sourceFieldId));
104
+ if (!field && args.sourceId && args.sourceFieldKey) {
105
+ [field] = await db
106
+ .select()
107
+ .from(schema.contentDatabaseSourceFields)
108
+ .where(
109
+ and(
110
+ eq(schema.contentDatabaseSourceFields.sourceId, args.sourceId),
111
+ eq(
112
+ schema.contentDatabaseSourceFields.sourceFieldKey,
113
+ args.sourceFieldKey,
114
+ ),
115
+ ),
116
+ );
117
+ }
96
118
  if (!field) throw new Error("Source field not found.");
97
119
 
98
120
  const [source] = await db
@@ -12,6 +12,7 @@ import { sanitizeNormalizationFormula } from "../shared/properties.js";
12
12
  import {
13
13
  readBuilderCmsContentEntries,
14
14
  readBuilderCmsModelFields,
15
+ type BuilderCmsReadResult,
15
16
  } from "./_builder-cms-read-client.js";
16
17
  import type { BuilderCmsSourceEntry } from "./_builder-cms-source-adapter.js";
17
18
  import {
@@ -42,6 +43,54 @@ import {
42
43
  const sourceTypeSchema = z
43
44
  .enum(["mock-local", "builder-cms", "local-table"])
44
45
  .default("mock-local");
46
+ const BUILDER_CMS_ATTACH_INITIAL_PAGES = 1;
47
+
48
+ export async function readInitialBuilderCmsAttachEntries(
49
+ sourceTable: string,
50
+ readEntries: typeof readBuilderCmsContentEntries = readBuilderCmsContentEntries,
51
+ ) {
52
+ return readEntries({
53
+ model: sourceTable,
54
+ maxPages: BUILDER_CMS_ATTACH_INITIAL_PAGES,
55
+ });
56
+ }
57
+
58
+ function builderReadHasMore(read: BuilderCmsReadResult | null | undefined) {
59
+ return read?.state === "live" && read.progress?.hasMore === true;
60
+ }
61
+
62
+ function builderReadActiveSourceRowIds(
63
+ read: BuilderCmsReadResult | null | undefined,
64
+ ) {
65
+ if (read?.state !== "live" || read.progress?.hasMore !== true) {
66
+ return undefined;
67
+ }
68
+ return read.entries.map((entry) => entry.id);
69
+ }
70
+
71
+ function builderReadSyncState(read: BuilderCmsReadResult) {
72
+ if (read.state === "error") return "error";
73
+ return builderReadHasMore(read) ? "refreshing" : "linked";
74
+ }
75
+
76
+ export function builderCmsAttachReadMetadata(read: BuilderCmsReadResult) {
77
+ const sourceFetchState: "idle" | "fetching" | "error" = builderReadHasMore(
78
+ read,
79
+ )
80
+ ? "fetching"
81
+ : read.state === "error"
82
+ ? "error"
83
+ : "idle";
84
+ const syncState: "linked" | "refreshing" | "error" =
85
+ builderReadSyncState(read);
86
+
87
+ return {
88
+ progress: read.progress,
89
+ sourceFetchState,
90
+ activeReadSourceRowIds: builderReadActiveSourceRowIds(read),
91
+ syncState,
92
+ };
93
+ }
45
94
 
46
95
  // Per-source key mapping the UI commits after the canonical-key confirm step.
47
96
  const normalizationFormulaSchema = z
@@ -187,9 +236,10 @@ export default defineAction({
187
236
  }
188
237
  let entries: BuilderCmsSourceEntry[];
189
238
  let modelFields: BuilderCmsModelFieldSummary[];
239
+ let builderRead: BuilderCmsReadResult | null = null;
190
240
  if (sourceType === "builder-cms") {
191
- const read = await readBuilderCmsContentEntries({ model: sourceTable });
192
- entries = read.state === "live" ? read.entries : [];
241
+ builderRead = await readInitialBuilderCmsAttachEntries(sourceTable);
242
+ entries = builderRead.state === "live" ? builderRead.entries : [];
193
243
  modelFields = await readBuilderCmsModelFields({ model: sourceTable });
194
244
  } else if (sourceType === "local-table") {
195
245
  // sourceTable carries the target database id for a local-table source.
@@ -224,6 +274,19 @@ export default defineAction({
224
274
  sampleEntry: entries[0],
225
275
  now,
226
276
  });
277
+ if (sourceType === "builder-cms" && builderRead) {
278
+ await updateBuilderCmsSourceReadMetadata({
279
+ sourceId: secondaryId,
280
+ sourceTable,
281
+ readState: builderRead.state,
282
+ entryCount: builderRead.entries.length,
283
+ matchedRowCount: entries.length,
284
+ fetchedAt: builderRead.fetchedAt,
285
+ now,
286
+ message: builderRead.message,
287
+ ...builderCmsAttachReadMetadata(builderRead),
288
+ });
289
+ }
227
290
  await writeSourceFederation({
228
291
  sourceId: secondaryId,
229
292
  federation: identityFederation(
@@ -263,9 +326,8 @@ export default defineAction({
263
326
  if (await databaseSourceExistsForTable(database.id, sourceTable)) {
264
327
  throw new Error(`"${sourceTable}" is already attached as a source.`);
265
328
  }
266
- const additionalRead = await readBuilderCmsContentEntries({
267
- model: sourceTable,
268
- });
329
+ const additionalRead =
330
+ await readInitialBuilderCmsAttachEntries(sourceTable);
269
331
  const additionalEntries =
270
332
  additionalRead.state === "live" ? additionalRead.entries : [];
271
333
  const additionalModelFields = await readBuilderCmsModelFields({
@@ -284,8 +346,12 @@ export default defineAction({
284
346
  const priorDocumentIds = new Set(
285
347
  beforeSetup.response.items.map((item) => item.document.id),
286
348
  );
349
+ let importedEntriesByDocumentId = new Map<
350
+ string,
351
+ BuilderCmsSourceEntry
352
+ >();
287
353
  if (additionalRead.state === "live") {
288
- await importBuilderCmsEntriesAsDatabaseItems({
354
+ const importResult = await importBuilderCmsEntriesAsDatabaseItems({
289
355
  database,
290
356
  entries: additionalEntries,
291
357
  now,
@@ -293,6 +359,7 @@ export default defineAction({
293
359
  existingSourceRows: [],
294
360
  skipTitleDedup: true,
295
361
  });
362
+ importedEntriesByDocumentId = importResult.importedEntriesByDocumentId;
296
363
  }
297
364
  const additionalSetup = await sourceSetupPayload(database.id);
298
365
  // Only the items this collection just created — exclude the primary's.
@@ -309,6 +376,9 @@ export default defineAction({
309
376
  existingRows: [],
310
377
  })
311
378
  : undefined;
379
+ for (const [documentId, entry] of importedEntriesByDocumentId) {
380
+ additionalEntriesByDocumentId?.set(documentId, entry);
381
+ }
312
382
  await seedMockSourceFields({
313
383
  sourceId: additionalSourceId,
314
384
  ownerEmail: database.ownerEmail,
@@ -347,7 +417,7 @@ export default defineAction({
347
417
  fetchedAt: additionalRead.fetchedAt,
348
418
  now,
349
419
  message: additionalRead.message,
350
- syncState: "linked",
420
+ ...builderCmsAttachReadMetadata(additionalRead),
351
421
  });
352
422
  await ensureDatabaseSourceProperty({ database, now });
353
423
 
@@ -374,9 +444,7 @@ export default defineAction({
374
444
  });
375
445
  const builderRead =
376
446
  sourceType === "builder-cms"
377
- ? await readBuilderCmsContentEntries({
378
- model: sourceTable,
379
- })
447
+ ? await readInitialBuilderCmsAttachEntries(sourceTable)
380
448
  : null;
381
449
  const builderEntries =
382
450
  builderRead?.state === "live" ? builderRead.entries : [];
@@ -386,14 +454,16 @@ export default defineAction({
386
454
  model: sourceTable,
387
455
  })
388
456
  : [];
457
+ let importedEntriesByDocumentId = new Map<string, BuilderCmsSourceEntry>();
389
458
  if (builderRead?.state === "live") {
390
- await importBuilderCmsEntriesAsDatabaseItems({
459
+ const importResult = await importBuilderCmsEntriesAsDatabaseItems({
391
460
  database,
392
461
  entries: builderEntries,
393
462
  now,
394
463
  sourceTable,
395
464
  existingSourceRows,
396
465
  });
466
+ importedEntriesByDocumentId = importResult.importedEntriesByDocumentId;
397
467
  }
398
468
 
399
469
  const refreshedSetup = await sourceSetupPayload(database.id);
@@ -407,6 +477,9 @@ export default defineAction({
407
477
  existingRows: existingSourceRows,
408
478
  })
409
479
  : undefined;
480
+ for (const [documentId, entry] of importedEntriesByDocumentId) {
481
+ builderEntriesByDocumentId?.set(documentId, entry);
482
+ }
410
483
 
411
484
  await seedMockSourceFields({
412
485
  sourceId,
@@ -447,7 +520,7 @@ export default defineAction({
447
520
  fetchedAt: builderRead.fetchedAt,
448
521
  now,
449
522
  message: builderRead.message,
450
- syncState: "linked",
523
+ ...builderCmsAttachReadMetadata(builderRead),
451
524
  });
452
525
  }
453
526
 
@@ -343,8 +343,12 @@ export default defineAction({
343
343
  .where(eq(schema.contentDatabaseSourceRows.sourceId, source.id));
344
344
  await clearSourceFederation(source.id, now);
345
345
 
346
+ let importedEntriesByDocumentId = new Map<
347
+ string,
348
+ BuilderCmsSourceEntry
349
+ >();
346
350
  if (read.state === "live") {
347
- await importBuilderCmsEntriesAsDatabaseItems({
351
+ const importResult = await importBuilderCmsEntriesAsDatabaseItems({
348
352
  database,
349
353
  entries,
350
354
  now,
@@ -352,6 +356,7 @@ export default defineAction({
352
356
  existingSourceRows: [],
353
357
  skipTitleDedup: true,
354
358
  });
359
+ importedEntriesByDocumentId = importResult.importedEntriesByDocumentId;
355
360
  }
356
361
  const refreshedSetup = await sourceSetupPayload(database.id);
357
362
  const builderEntriesByDocumentId =
@@ -364,6 +369,9 @@ export default defineAction({
364
369
  existingRows: [],
365
370
  })
366
371
  : new Map<string, BuilderCmsSourceEntry>();
372
+ for (const [documentId, entry] of importedEntriesByDocumentId) {
373
+ builderEntriesByDocumentId.set(documentId, entry);
374
+ }
367
375
  await seedMockSourceFields({
368
376
  sourceId: source.id,
369
377
  ownerEmail: database.ownerEmail,
@@ -29,6 +29,7 @@ export default defineAction({
29
29
  return {
30
30
  success: true,
31
31
  databaseId,
32
+ documentId: database.documentId,
32
33
  deletedAt,
33
34
  };
34
35
  },
@@ -49,9 +49,18 @@ export default defineAction({
49
49
  }
50
50
 
51
51
  const access = await resolveAccess("document", args.id);
52
- if (!access) throw new Error(`Document "${args.id}" not found`);
52
+ // Not-found is a deterministic client-state condition (deleted or
53
+ // inaccessible document still referenced by an open tab) — 404, not a
54
+ // 500 that floods the console as Internal Server Error.
55
+ if (!access) {
56
+ throw Object.assign(new Error(`Document "${args.id}" not found`), {
57
+ statusCode: 404,
58
+ });
59
+ }
53
60
  if (await isSoftDeletedDatabaseDocument(args.id)) {
54
- throw new Error(`Document "${args.id}" not found`);
61
+ throw Object.assign(new Error(`Document "${args.id}" not found`), {
62
+ statusCode: 404,
63
+ });
55
64
  }
56
65
  const doc = access.resource;
57
66
  const database = await getDatabaseByDocumentId(doc.id);
@@ -19,6 +19,10 @@ export default defineAction({
19
19
  .string()
20
20
  .optional()
21
21
  .describe("Database id to omit from the results."),
22
+ excludeDatabaseIds: z
23
+ .array(z.string())
24
+ .optional()
25
+ .describe("Database ids to omit from the results."),
22
26
  query: z.string().optional().describe("Optional title search text."),
23
27
  limit: z.coerce
24
28
  .number()
@@ -34,6 +38,12 @@ export default defineAction({
34
38
  const db = getDb();
35
39
  const query = args.query?.trim();
36
40
  const pattern = query ? `%${escapeLike(query.toLowerCase())}%` : null;
41
+ const excludedDatabaseIds = new Set(
42
+ [
43
+ args.excludeDatabaseId?.trim(),
44
+ ...(args.excludeDatabaseIds ?? []).map((id) => id.trim()),
45
+ ].filter((id): id is string => !!id),
46
+ );
37
47
  // The same access + discovery filter the sidebar uses, so the picker shows
38
48
  // owned AND shared/org databases and never a trashed/hidden one.
39
49
  const queryBuilder = db
@@ -52,8 +62,11 @@ export default defineAction({
52
62
  accessFilter(schema.documents, schema.documentShares),
53
63
  documentDiscoveryFilter(),
54
64
  isNull(schema.contentDatabases.deletedAt),
55
- args.excludeDatabaseId
56
- ? ne(schema.contentDatabases.id, args.excludeDatabaseId)
65
+ excludedDatabaseIds.size === 1
66
+ ? ne(
67
+ schema.contentDatabases.id,
68
+ Array.from(excludedDatabaseIds)[0]!,
69
+ )
57
70
  : undefined,
58
71
  pattern
59
72
  ? sql`lower(${schema.documents.title}) LIKE ${pattern} ESCAPE '\\'`
@@ -66,13 +79,48 @@ export default defineAction({
66
79
  ? await queryBuilder.limit(args.limit)
67
80
  : await queryBuilder;
68
81
 
69
- const databases = rows.map((row) => ({
70
- databaseId: row.id,
71
- documentId: row.documentId,
72
- // The document's live title (matches the sidebar) rather than the
73
- // possibly-stale content_databases.title.
74
- title: row.title ?? "Untitled database",
75
- }));
82
+ const localTableSources =
83
+ excludedDatabaseIds.size > 0
84
+ ? await db
85
+ .select({
86
+ databaseId: schema.contentDatabaseSources.databaseId,
87
+ sourceTable: schema.contentDatabaseSources.sourceTable,
88
+ })
89
+ .from(schema.contentDatabaseSources)
90
+ .where(eq(schema.contentDatabaseSources.sourceType, "local-table"))
91
+ : [];
92
+ const localTableTargetByDatabaseId = new Map(
93
+ localTableSources.map((source) => [
94
+ source.databaseId,
95
+ source.sourceTable,
96
+ ]),
97
+ );
98
+ const sourceChainIncludesExcludedDatabase = (databaseId: string) => {
99
+ const seen = new Set<string>();
100
+ let current: string | undefined = databaseId;
101
+ while (current && !seen.has(current)) {
102
+ if (excludedDatabaseIds.has(current)) return true;
103
+ seen.add(current);
104
+ current = localTableTargetByDatabaseId.get(current);
105
+ }
106
+ return false;
107
+ };
108
+
109
+ const databases = rows
110
+ // Exclusion ids may be database ids OR database document ids — the
111
+ // settings panel only has the document id before any source exists.
112
+ .filter(
113
+ (row) =>
114
+ !excludedDatabaseIds.has(row.documentId) &&
115
+ !sourceChainIncludesExcludedDatabase(row.id),
116
+ )
117
+ .map((row) => ({
118
+ databaseId: row.id,
119
+ documentId: row.documentId,
120
+ // The document's live title (matches the sidebar) rather than the
121
+ // possibly-stale content_databases.title.
122
+ title: row.title ?? "Untitled database",
123
+ }));
76
124
 
77
125
  return { databases };
78
126
  },
@@ -28,6 +28,8 @@ import {
28
28
  } from "./_database-source-utils.js";
29
29
  import { getContentDatabaseResponse } from "./_database-utils.js";
30
30
 
31
+ export const BUILDER_SOURCE_REVIEW_PREPARE_LIMIT = 100;
32
+
31
33
  function riskRank(level: ContentDatabaseSourceRiskLevel) {
32
34
  if (level === "high") return 3;
33
35
  if (level === "medium") return 2;
@@ -41,6 +43,12 @@ function maxRisk(
41
43
  return riskRank(next) > riskRank(current) ? next : current;
42
44
  }
43
45
 
46
+ function reviewPreparePriority(changeSet: ContentDatabaseSourceChangeSet) {
47
+ if (changeSet.state === "pending_push") return 0;
48
+ if (changeSet.state === "staged_revision") return 1;
49
+ return 2;
50
+ }
51
+
44
52
  function parsePayload(value: string) {
45
53
  try {
46
54
  const parsed = JSON.parse(value) as unknown;
@@ -146,6 +154,8 @@ export function buildBuilderSourceReviewPayload(args: {
146
154
  summary,
147
155
  sourceName: args.source.sourceName,
148
156
  sourceTable: args.source.sourceTable,
157
+ totalRowCount: args.changeSets.length,
158
+ preparedRowLimit: args.changeSets.length,
149
159
  pushMode,
150
160
  dryRunOnly: !args.source.capabilities.liveWritesEnabled,
151
161
  liveWritesEnabled: args.source.capabilities.liveWritesEnabled,
@@ -399,16 +409,19 @@ export default defineAction({
399
409
  if (!snapshot || snapshot.sourceType !== "builder-cms") {
400
410
  throw new Error("Attach a Builder CMS source before reviewing updates.");
401
411
  }
402
- const reviewableChanges = snapshot.changeSets.filter(
412
+ const allReviewableChanges = snapshot.changeSets.filter(
403
413
  (changeSet) =>
404
414
  changeSet.direction === "outbound" &&
405
415
  (changeSet.state === "pending_push" ||
406
416
  changeSet.state === "staged_revision" ||
407
417
  changeSet.state === "approved"),
408
418
  );
409
- if (reviewableChanges.length === 0) {
419
+ if (allReviewableChanges.length === 0) {
410
420
  throw new Error("No pending local Builder changes to review.");
411
421
  }
422
+ const reviewableChanges = [...allReviewableChanges]
423
+ .sort((a, b) => reviewPreparePriority(a) - reviewPreparePriority(b))
424
+ .slice(0, BUILDER_SOURCE_REVIEW_PREPARE_LIMIT);
412
425
 
413
426
  const now = new Date().toISOString();
414
427
  const reviewerEmail =
@@ -466,12 +479,16 @@ export default defineAction({
466
479
  );
467
480
  const response = await getContentDatabaseResponse(database.id);
468
481
 
482
+ const review = buildBuilderSourceReviewPayload({
483
+ source: reviewedSnapshot,
484
+ changeSets: reviewedChangeSets,
485
+ });
486
+ review.totalRowCount = allReviewableChanges.length;
487
+ review.preparedRowLimit = BUILDER_SOURCE_REVIEW_PREPARE_LIMIT;
488
+
469
489
  return {
470
490
  ...response,
471
- review: buildBuilderSourceReviewPayload({
472
- source: reviewedSnapshot,
473
- changeSets: reviewedChangeSets,
474
- }),
491
+ review,
475
492
  };
476
493
  },
477
494
  });
@@ -14,7 +14,7 @@ import { serializeDatabase } from "./_property-utils.js";
14
14
 
15
15
  export default defineAction({
16
16
  description:
17
- "Refresh the local read-only source status envelope for a content database. Mock-local and Builder CMS fixture sources resync field mappings and row identity without external API calls or provider writes.",
17
+ "Refresh the local read-only source status envelope for a content database. Mock-local and Builder CMS fixture sources resync field mappings and row identity without provider writes. For Builder CMS sources, set fullRefresh to true when you need to read every available page in one action call instead of continuing one partial page.",
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"),
@@ -22,6 +22,12 @@ export default defineAction({
22
22
  .string()
23
23
  .optional()
24
24
  .describe("Target source ID (defaults to the primary source)"),
25
+ fullRefresh: z
26
+ .boolean()
27
+ .optional()
28
+ .describe(
29
+ "For Builder CMS sources, read all available pages in this refresh instead of one continuation page.",
30
+ ),
25
31
  }),
26
32
  run: async (args): Promise<ContentDatabaseSourceStatusResponse> => {
27
33
  const database = await resolveDatabaseForSourceMutation(args);
@@ -42,7 +48,12 @@ export default defineAction({
42
48
  if (source.sourceType === "mock-local") {
43
49
  await resyncMockSourceSnapshot({ database, source, now });
44
50
  } else if (source.sourceType === "builder-cms") {
45
- await resyncBuilderCmsSourceSnapshot({ database, source, now });
51
+ await resyncBuilderCmsSourceSnapshot({
52
+ database,
53
+ source,
54
+ now,
55
+ runFullRefresh: args.fullRefresh === true,
56
+ });
46
57
  } else if (source.sourceType === "local-table") {
47
58
  // Read-only federated secondary; its rows are re-read on demand, nothing
48
59
  // to resync against the primary's local snapshot here.
@@ -54,11 +65,23 @@ export default defineAction({
54
65
  args.sourceId,
55
66
  );
56
67
 
68
+ const builderProgress =
69
+ snapshot?.sourceType === "builder-cms" ? snapshot.metadata : null;
70
+ const builderFetching =
71
+ builderProgress?.sourceFetchState === "fetching" ||
72
+ snapshot?.syncState === "refreshing";
73
+ const builderFetched =
74
+ typeof builderProgress?.lastReadFetchedEntryCount === "number"
75
+ ? builderProgress.lastReadFetchedEntryCount
76
+ : undefined;
77
+
57
78
  return {
58
79
  database: serializeDatabase(database),
59
80
  mode: "source-backed",
60
81
  summary: snapshot
61
- ? `${snapshot.sourceName} resynced locally; field mappings and row identity now reflect the current database snapshot.`
82
+ ? builderFetching
83
+ ? `${snapshot.sourceName} fetched ${builderFetched ?? "some"} rows. Run refresh again to continue loading the remaining Builder rows.`
84
+ : `${snapshot.sourceName} resynced locally; field mappings and row identity now reflect the current database snapshot.`
62
85
  : "Source metadata refreshed.",
63
86
  source: snapshot,
64
87
  };
@@ -63,6 +63,7 @@ export default defineAction({
63
63
  return {
64
64
  success: true,
65
65
  databaseId,
66
+ documentId: ownership.database.documentId,
66
67
  deletedAt: null,
67
68
  };
68
69
  },