@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
@@ -1,17 +1,30 @@
1
1
  /**
2
2
  * Responsive-aware Tailwind class model for the Design Studio (§6.4).
3
3
  *
4
- * The canvas is mobile-first: the base (unprefixed) class is what renders on
5
- * the narrowest breakpoint; each larger breakpoint layers overrides upward,
6
- * matching Tailwind's min-width cascade.
4
+ * Two cascades coexist in one className:
7
5
  *
8
- * Frame widths snap to Tailwind's canonical breakpoint thresholds:
9
- * < 640 → base (unprefixed)
10
- * 640–767 sm:
11
- * 768–1023 md:
12
- * 1024–1279lg:
13
- * 12801535xl:
14
- * 1536 2xl:
6
+ * 1. **Mobile-first min-width prefixes** (`sm:`/`md:`/`lg:`/`xl:`/`2xl:`)
7
+ * Tailwind's canonical cascade. The base (unprefixed) class renders on the
8
+ * narrowest viewport; each larger breakpoint layers overrides upward.
9
+ * Frame widths snap to Tailwind's canonical thresholds:
10
+ * < 640 base (unprefixed)
11
+ * 640767sm:
12
+ * 768–1023md:
13
+ * 1024–1279 → lg:
14
+ * 1280–1535 → xl:
15
+ * ≥ 1536 → 2xl:
16
+ *
17
+ * 2. **Framer-style desktop-down max-width scopes** (`max-[809px]:` arbitrary
18
+ * variants) — the breakpoint-bar editing model. The PRIMARY frame is the
19
+ * base: unprefixed classes cascade down to every narrower breakpoint
20
+ * unless a narrower frame writes a `max-[<bound>px]:` override, which
21
+ * applies at that width range and below. The bound is derived from the
22
+ * next-wider frame in the design's breakpoint set (Framer semantics) — see
23
+ * `breakpointUpperBoundPx`.
24
+ *
25
+ * Max-width-scoped tokens are OPAQUE to the legacy prefix helpers
26
+ * (`parseClassGroups` / `setPropertyClass` / …) so the two cascades never
27
+ * clobber each other; use the `*MaxWidth*` helpers for the scoped layer.
15
28
  *
16
29
  * All helpers are pure (no DB, no DOM, no side-effects).
17
30
  */
@@ -75,6 +88,27 @@ const ALL_PREFIXES: ReadonlyArray<TailwindBreakpointPrefix> = [
75
88
  /** Regex that matches a responsive prefix at the start of a class token. */
76
89
  const PREFIX_RE = /^(2xl|xl|lg|md|sm):/;
77
90
 
91
+ /**
92
+ * Regex that matches a Framer-style max-width arbitrary variant at the start
93
+ * of a class token (e.g. `"max-[809px]:text-sm"`).
94
+ */
95
+ const MAX_WIDTH_VARIANT_RE = /^max-\[(\d+)px\]:/;
96
+
97
+ /**
98
+ * Tailwind's core max-* variants and the inclusive pixel bound each applies
99
+ * below (max-md: compiles to `@media not all and (min-width: 768px)`, i.e.
100
+ * effective at widths ≤ 767).
101
+ */
102
+ const CORE_MAX_VARIANT_BOUNDS: Readonly<Record<string, number>> = {
103
+ "max-sm": 639,
104
+ "max-md": 767,
105
+ "max-lg": 1023,
106
+ "max-xl": 1279,
107
+ "max-2xl": 1535,
108
+ };
109
+
110
+ const CORE_MAX_VARIANT_RE = /^max-(2xl|xl|lg|md|sm):/;
111
+
78
112
  // ---------------------------------------------------------------------------
79
113
  // Parsing
80
114
  // ---------------------------------------------------------------------------
@@ -114,6 +148,11 @@ export function parseClassGroups(className: string): BreakpointClassGroups {
114
148
 
115
149
  const tokens = className.trim().split(/\s+/).filter(Boolean);
116
150
  for (const token of tokens) {
151
+ // Max-width-scoped tokens belong to the desktop-down cascade — they are
152
+ // NOT base values and must not pollute the min-width groups (a
153
+ // `max-[809px]:text-sm` token is an override below 810px, not the base
154
+ // font size). They are handled by the `*MaxWidth*` helpers instead.
155
+ if (parseMaxWidthClassToken(token)) continue;
117
156
  const { prefix } = parseClassToken(token);
118
157
  groups[prefix].push(token);
119
158
  }
@@ -337,6 +376,11 @@ export function setPropertyClass(
337
376
  const next: string[] = [];
338
377
 
339
378
  for (const token of tokens) {
379
+ // Max-width-scoped tokens are opaque to the min-width prefix cascade.
380
+ if (parseMaxWidthClassToken(token)) {
381
+ next.push(token);
382
+ continue;
383
+ }
340
384
  const parsed = parseClassToken(token);
341
385
  if (parsed.prefix === prefix && utilityStem(parsed.utility) === stem) {
342
386
  if (!replaced) {
@@ -372,6 +416,8 @@ export function removePropertyClass(
372
416
  const tokens = className.trim().split(/\s+/).filter(Boolean);
373
417
  return tokens
374
418
  .filter((token) => {
419
+ // Max-width-scoped tokens are opaque to the min-width prefix cascade.
420
+ if (parseMaxWidthClassToken(token)) return true;
375
421
  const parsed = parseClassToken(token);
376
422
  return !(
377
423
  parsed.prefix === prefix && utilityStem(parsed.utility) === stem
@@ -450,3 +496,395 @@ export function resetToBase(
450
496
  if (prefix === "base") return className;
451
497
  return removePropertyClass(className, prefix, stem);
452
498
  }
499
+
500
+ // ---------------------------------------------------------------------------
501
+ // Framer-style desktop-down max-width scopes (§6.4 breakpoint bar)
502
+ // ---------------------------------------------------------------------------
503
+
504
+ /** A parsed max-width-scoped class token (e.g. `"max-[809px]:text-sm"`). */
505
+ export interface ParsedMaxWidthClassToken {
506
+ /** Original token string. */
507
+ raw: string;
508
+ /** Inclusive upper viewport-width bound in pixels the utility applies at. */
509
+ boundPx: number;
510
+ /** The utility after the variant (e.g. `"text-sm"`). */
511
+ utility: string;
512
+ }
513
+
514
+ /**
515
+ * Parse a max-width-scoped token. Accepts arbitrary `max-[NNNpx]:` variants
516
+ * (the form this module writes) and Tailwind's core `max-sm:`…`max-2xl:`
517
+ * variants (mapped to their canonical bounds) so hand-authored documents are
518
+ * still recognised. Returns `null` for every other token, including plain
519
+ * `max-w-*` sizing utilities.
520
+ */
521
+ export function parseMaxWidthClassToken(
522
+ token: string,
523
+ ): ParsedMaxWidthClassToken | null {
524
+ const arbitrary = MAX_WIDTH_VARIANT_RE.exec(token);
525
+ if (arbitrary) {
526
+ const boundPx = Number.parseInt(arbitrary[1], 10);
527
+ if (!Number.isFinite(boundPx) || boundPx <= 0) return null;
528
+ return { raw: token, boundPx, utility: token.slice(arbitrary[0].length) };
529
+ }
530
+ const core = CORE_MAX_VARIANT_RE.exec(token);
531
+ if (core) {
532
+ const boundPx = CORE_MAX_VARIANT_BOUNDS[`max-${core[1]}`];
533
+ if (boundPx === undefined) return null;
534
+ return { raw: token, boundPx, utility: token.slice(core[0].length) };
535
+ }
536
+ return null;
537
+ }
538
+
539
+ /**
540
+ * Build a max-width-scoped class token: `maxWidthClassToken(809, "text-sm")`
541
+ * → `"max-[809px]:text-sm"`. Tailwind's arbitrary-variant JIT compiles this
542
+ * to `@media (max-width: 809px) { … }`.
543
+ */
544
+ export function maxWidthClassToken(boundPx: number, utility: string): string {
545
+ return `max-[${Math.round(boundPx)}px]:${utility}`;
546
+ }
547
+
548
+ /**
549
+ * Return all max-width-scoped tokens for `stem` at exactly `boundPx`.
550
+ */
551
+ export function getMaxWidthPropertyClasses(
552
+ className: string,
553
+ boundPx: number,
554
+ stem: string,
555
+ ): string[] {
556
+ return className
557
+ .trim()
558
+ .split(/\s+/)
559
+ .filter(Boolean)
560
+ .filter((token) => {
561
+ const parsed = parseMaxWidthClassToken(token);
562
+ return (
563
+ parsed !== null &&
564
+ parsed.boundPx === boundPx &&
565
+ utilityStem(parsed.utility) === stem
566
+ );
567
+ });
568
+ }
569
+
570
+ /**
571
+ * Set the utility for its property stem at the `boundPx` max-width scope,
572
+ * replacing any existing same-stem token at the SAME bound and leaving every
573
+ * other token (base, min-width prefixed, other bounds) untouched.
574
+ */
575
+ export function setMaxWidthPropertyClass(
576
+ className: string,
577
+ boundPx: number,
578
+ utility: string,
579
+ ): string {
580
+ const stem = utilityStem(utility);
581
+ const newToken = maxWidthClassToken(boundPx, utility);
582
+ const tokens = className.trim().split(/\s+/).filter(Boolean);
583
+ let replaced = false;
584
+ const next: string[] = [];
585
+
586
+ for (const token of tokens) {
587
+ const parsed = parseMaxWidthClassToken(token);
588
+ if (
589
+ parsed !== null &&
590
+ parsed.boundPx === boundPx &&
591
+ utilityStem(parsed.utility) === stem
592
+ ) {
593
+ if (!replaced) {
594
+ next.push(newToken);
595
+ replaced = true;
596
+ }
597
+ // drop duplicate same-stem tokens at the same bound
598
+ } else {
599
+ next.push(token);
600
+ }
601
+ }
602
+
603
+ if (!replaced) next.push(newToken);
604
+ return next.join(" ");
605
+ }
606
+
607
+ /**
608
+ * Remove all max-width-scoped tokens for `stem` at exactly `boundPx`,
609
+ * falling back to the base value (desktop-down cascade).
610
+ */
611
+ export function removeMaxWidthPropertyClass(
612
+ className: string,
613
+ boundPx: number,
614
+ stem: string,
615
+ ): string {
616
+ return className
617
+ .trim()
618
+ .split(/\s+/)
619
+ .filter(Boolean)
620
+ .filter((token) => {
621
+ const parsed = parseMaxWidthClassToken(token);
622
+ return !(
623
+ parsed !== null &&
624
+ parsed.boundPx === boundPx &&
625
+ utilityStem(parsed.utility) === stem
626
+ );
627
+ })
628
+ .join(" ");
629
+ }
630
+
631
+ /**
632
+ * All max-width-scoped overrides for `stem` in `className`, sorted widest
633
+ * bound first (the CSS emission / cascade order).
634
+ */
635
+ export function maxWidthOverridesForStem(
636
+ className: string,
637
+ stem: string,
638
+ ): Array<{ boundPx: number; utility: string; token: string }> {
639
+ const overrides: Array<{ boundPx: number; utility: string; token: string }> =
640
+ [];
641
+ for (const token of className.trim().split(/\s+/).filter(Boolean)) {
642
+ const parsed = parseMaxWidthClassToken(token);
643
+ if (parsed && utilityStem(parsed.utility) === stem) {
644
+ overrides.push({
645
+ boundPx: parsed.boundPx,
646
+ utility: parsed.utility,
647
+ token,
648
+ });
649
+ }
650
+ }
651
+ return overrides.sort((a, b) => b.boundPx - a.boundPx);
652
+ }
653
+
654
+ /**
655
+ * Framer-style scope bound for editing at `activeWidthPx`.
656
+ *
657
+ * A breakpoint's overrides apply from just below the NEXT-WIDER frame down
658
+ * to zero (narrower breakpoints layer their own overrides on top). The bound
659
+ * is therefore `min(width of every wider frame, base width) - 1`.
660
+ *
661
+ * Returns `null` when there is no wider frame — the active frame IS the
662
+ * widest context, so edits belong to the base (unscoped) layer.
663
+ *
664
+ * @example
665
+ * // breakpoints 390 / 810, primary frame 1280
666
+ * breakpointUpperBoundPx([390, 810], 810, 1280) // → 1279
667
+ * breakpointUpperBoundPx([390, 810], 390, 1280) // → 809
668
+ * breakpointUpperBoundPx([390, 810], 1280, 1280) // → null (base)
669
+ */
670
+ export function breakpointUpperBoundPx(
671
+ breakpointWidths: readonly number[],
672
+ activeWidthPx: number,
673
+ baseWidthPx?: number | null,
674
+ ): number | null {
675
+ const candidates = breakpointWidths.filter(
676
+ (width) => Number.isFinite(width) && width > activeWidthPx,
677
+ );
678
+ if (
679
+ baseWidthPx != null &&
680
+ Number.isFinite(baseWidthPx) &&
681
+ baseWidthPx > activeWidthPx
682
+ ) {
683
+ candidates.push(baseWidthPx);
684
+ }
685
+ if (candidates.length === 0) return null;
686
+ return Math.round(Math.min(...candidates)) - 1;
687
+ }
688
+
689
+ // ---------------------------------------------------------------------------
690
+ // Class-vs-media write decision (single write path)
691
+ // ---------------------------------------------------------------------------
692
+
693
+ /**
694
+ * CSS property → the Tailwind utility stems that may express it. Shared by
695
+ * the inspector and the apply-visual-edit action so both derive the SAME
696
+ * class-vs-media decision for a given (property, value) pair.
697
+ */
698
+ const CSS_PROPERTY_UTILITY_STEMS: Readonly<Record<string, string[]>> = {
699
+ color: ["text-color"],
700
+ "background-color": ["background-color"],
701
+ background: ["background-color", "background-image"],
702
+ "font-size": ["font-size"],
703
+ "font-weight": ["font-weight"],
704
+ "font-family": ["font-family"],
705
+ "text-align": ["text-align"],
706
+ display: ["display"],
707
+ position: ["position"],
708
+ width: ["w"],
709
+ height: ["h"],
710
+ opacity: ["opacity"],
711
+ "border-radius": ["rounded"],
712
+ padding: ["p"],
713
+ "padding-left": ["px", "pl"],
714
+ "padding-right": ["px", "pr"],
715
+ "padding-top": ["py", "pt"],
716
+ "padding-bottom": ["py", "pb"],
717
+ margin: ["m"],
718
+ "margin-left": ["mx", "ml"],
719
+ "margin-right": ["mx", "mr"],
720
+ "margin-top": ["my", "mt"],
721
+ "margin-bottom": ["my", "mb"],
722
+ gap: ["gap"],
723
+ "column-gap": ["gap-x"],
724
+ "row-gap": ["gap-y"],
725
+ };
726
+
727
+ /** Convert a camelCase CSS property name to kebab-case. */
728
+ export function normalizeCssPropertyName(property: string): string {
729
+ return property.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
730
+ }
731
+
732
+ /** Utility stems that may express the given CSS property (kebab or camel). */
733
+ export function utilityStemsForCssProperty(property: string): string[] {
734
+ const normalized = normalizeCssPropertyName(property);
735
+ return CSS_PROPERTY_UTILITY_STEMS[normalized] ?? [normalized];
736
+ }
737
+
738
+ /**
739
+ * Heuristic: does `value` look like a single Tailwind utility token rather
740
+ * than a raw CSS value? Rejects whitespace, declarations, and obvious CSS
741
+ * function/color syntax.
742
+ */
743
+ export function looksLikeTailwindUtility(value: string): boolean {
744
+ const trimmed = value.trim();
745
+ if (!trimmed || /\s/.test(trimmed)) return false;
746
+ if (/[;{}]/.test(trimmed) || /\/\*/.test(trimmed)) return false;
747
+ if (/^(?:#|rgb\(|rgba\(|hsl\(|hsla\(|var\(|calc\()/i.test(trimmed)) {
748
+ return false;
749
+ }
750
+ if (trimmed.includes(":")) return false;
751
+ return /^[!-]?[a-z0-9][a-z0-9[\]()./%_-]*$/i.test(trimmed);
752
+ }
753
+
754
+ /**
755
+ * True when `value` is a Tailwind utility whose stem actually expresses the
756
+ * given CSS property — the gate for writing a breakpoint override as a
757
+ * responsive class instead of a managed `@media` rule.
758
+ */
759
+ export function responsiveUtilityMatchesStyleProperty(
760
+ property: string,
761
+ value: string,
762
+ ): boolean {
763
+ if (!looksLikeTailwindUtility(value)) return false;
764
+ const normalizedProperty = normalizeCssPropertyName(property);
765
+ const stem = utilityStem(value.trim());
766
+ const allowed = CSS_PROPERTY_UTILITY_STEMS[normalizedProperty];
767
+ return allowed ? allowed.includes(stem) : stem === normalizedProperty;
768
+ }
769
+
770
+ /** How a single (property, value) edit should be persisted for a scope. */
771
+ export type BreakpointStyleWritePlan =
772
+ | {
773
+ /** No wider frame exists — write to the base (unscoped) layer. */
774
+ mode: "base";
775
+ }
776
+ | {
777
+ /** The value is a Tailwind utility — write a max-width-scoped class. */
778
+ mode: "class";
779
+ boundPx: number;
780
+ utility: string;
781
+ token: string;
782
+ }
783
+ | {
784
+ /**
785
+ * The value can't be a utility class (raw CSS value, e.g. an exact px
786
+ * position from a canvas drag) — write a rule into the managed
787
+ * `<style data-agent-native-breakpoints>` block.
788
+ */
789
+ mode: "media";
790
+ maxWidthPx: number;
791
+ property: string;
792
+ value: string;
793
+ };
794
+
795
+ /**
796
+ * THE single write-path decision for breakpoint-scoped style edits: given a
797
+ * CSS property, its new value, and the active scope's upper bound (from
798
+ * `breakpointUpperBoundPx`), decide whether the edit is a base write, a
799
+ * responsive utility class, or a managed `@media` rule.
800
+ */
801
+ export function planBreakpointStyleWrite(args: {
802
+ property: string;
803
+ value: string;
804
+ upperBoundPx: number | null;
805
+ }): BreakpointStyleWritePlan {
806
+ const { property, value, upperBoundPx } = args;
807
+ if (upperBoundPx == null) return { mode: "base" };
808
+ const trimmed = value.trim();
809
+ if (responsiveUtilityMatchesStyleProperty(property, trimmed)) {
810
+ return {
811
+ mode: "class",
812
+ boundPx: upperBoundPx,
813
+ utility: trimmed,
814
+ token: maxWidthClassToken(upperBoundPx, trimmed),
815
+ };
816
+ }
817
+ return {
818
+ mode: "media",
819
+ maxWidthPx: upperBoundPx,
820
+ property: normalizeCssPropertyName(property),
821
+ value: trimmed,
822
+ };
823
+ }
824
+
825
+ // ---------------------------------------------------------------------------
826
+ // Cascade resolution (inspector / indicator support)
827
+ // ---------------------------------------------------------------------------
828
+
829
+ /**
830
+ * Resolve which utility for `stem` is effective at `viewportWidthPx`,
831
+ * following both cascades the way the rendered CSS does:
832
+ *
833
+ * 1. Among max-width scopes whose bound ≥ width, the NARROWEST bound wins
834
+ * (managed emission order puts narrower ranges later in the sheet).
835
+ * 2. Otherwise the largest satisfied min-width prefix wins (Tailwind
836
+ * mobile-first).
837
+ * 3. Otherwise the base token.
838
+ *
839
+ * Heuristic for indicators and agent inspection — the browser's real cascade
840
+ * also involves stylesheet order for hand-authored documents.
841
+ */
842
+ export function effectiveUtilityAtWidth(
843
+ className: string,
844
+ stem: string,
845
+ viewportWidthPx: number,
846
+ ): {
847
+ utility: string;
848
+ source: "max-width" | "prefix" | "base";
849
+ boundPx?: number;
850
+ prefix?: TailwindBreakpointPrefix;
851
+ } | null {
852
+ const maxMatches = maxWidthOverridesForStem(className, stem).filter(
853
+ (override) => override.boundPx >= viewportWidthPx,
854
+ );
855
+ if (maxMatches.length > 0) {
856
+ // Narrowest applicable bound wins.
857
+ const winner = maxMatches[maxMatches.length - 1];
858
+ return {
859
+ utility: winner.utility,
860
+ source: "max-width",
861
+ boundPx: winner.boundPx,
862
+ };
863
+ }
864
+
865
+ const groups = parseClassGroups(className);
866
+ const satisfied = BREAKPOINT_MIN_WIDTHS.filter(
867
+ ({ minPx }) => viewportWidthPx >= minPx,
868
+ );
869
+ for (const { prefix } of satisfied) {
870
+ const tokens = groups[prefix].filter((token) => {
871
+ const { utility } = parseClassToken(token);
872
+ return utilityStem(utility) === stem;
873
+ });
874
+ if (tokens.length > 0) {
875
+ const { utility } = parseClassToken(tokens[tokens.length - 1]);
876
+ return { utility, source: "prefix", prefix };
877
+ }
878
+ }
879
+
880
+ const baseTokens = groups.base.filter(
881
+ (token) => utilityStem(parseClassToken(token).utility) === stem,
882
+ );
883
+ if (baseTokens.length > 0) {
884
+ return {
885
+ utility: baseTokens[baseTokens.length - 1],
886
+ source: "base",
887
+ };
888
+ }
889
+ return null;
890
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Server-side entry point for stamping `data-agent-native-node-id` on newly
3
+ * persisted screens.
4
+ *
5
+ * `ensureCodeLayerNodeIdsInHtml` (shared/code-layer.ts) already implements a
6
+ * safe, idempotent, whole-document annotation pass — it walks the full
7
+ * element tree, skips non-visual tags (head/script/style/meta/link/title/
8
+ * template/noscript) and SVG internals, preserves any existing clean id, and
9
+ * mints fresh ids for everything else using the editor's existing `an-<hash>`
10
+ * format. Today it only runs client-side (DesignCanvas.tsx / DesignEditor.tsx)
11
+ * as a live-annotation effect, which means a design is only ever fully
12
+ * annotated the first time a human opens it in the editor — until then every
13
+ * id-keyed operation (move layer, style commit, motion/interaction targeting)
14
+ * silently fails on a freshly generated or imported screen.
15
+ *
16
+ * `annotateScreenHtmlForPersist` reuses that exact function so every
17
+ * generation/import/create persistence path can stamp ids at creation time,
18
+ * before the client ever sees the screen — new designs are born annotated
19
+ * instead of depending on a client-side backfill.
20
+ */
21
+ import { ensureCodeLayerNodeIdsInHtml } from "./code-layer.js";
22
+
23
+ /**
24
+ * Stamp missing `data-agent-native-node-id` attributes on an HTML screen
25
+ * before it is persisted. Idempotent (a no-op when every element already has
26
+ * a clean id) and a no-op for non-HTML file types (JSX is source-edited, not
27
+ * DOM-projected; CSS/asset files have no elements to annotate).
28
+ *
29
+ * Never throws: annotation is a best-effort enhancement, not a correctness
30
+ * requirement, so a defensive parse failure must never block a save. Worst
31
+ * case, the existing client-side `ensureCodeLayerNodeIdsInHtml` effect
32
+ * backfills ids the first time the screen is opened, exactly as it does
33
+ * today for designs generated before this pass existed.
34
+ */
35
+ export function annotateScreenHtmlForPersist(
36
+ content: string,
37
+ fileType: string | null | undefined,
38
+ ): string {
39
+ if ((fileType ?? "html") !== "html") return content;
40
+ if (typeof content !== "string" || !content.trim()) return content;
41
+ try {
42
+ return ensureCodeLayerNodeIdsInHtml(content).content;
43
+ } catch {
44
+ return content;
45
+ }
46
+ }