@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
@@ -0,0 +1,64 @@
1
+ import { inArray } from "drizzle-orm";
2
+
3
+ import { schema } from "../server/db/index.js";
4
+
5
+ export async function assertPresetSkeletonAssetsValid(input: {
6
+ db: any;
7
+ libraryId: string;
8
+ settings: unknown;
9
+ }) {
10
+ const spec =
11
+ input.settings && typeof input.settings === "object"
12
+ ? (input.settings as { skeletonSpec?: unknown }).skeletonSpec
13
+ : null;
14
+ if (!spec || typeof spec !== "object") return;
15
+ const skeleton = spec as {
16
+ background?: { type?: unknown; assetId?: unknown };
17
+ mask?: { type?: unknown; assetId?: unknown };
18
+ };
19
+ const backgroundAssetId =
20
+ skeleton.background?.type === "asset" &&
21
+ typeof skeleton.background.assetId === "string"
22
+ ? skeleton.background.assetId
23
+ : "";
24
+ const maskAssetId =
25
+ skeleton.mask?.type === "asset" && typeof skeleton.mask.assetId === "string"
26
+ ? skeleton.mask.assetId
27
+ : "";
28
+ if (!backgroundAssetId || !maskAssetId) return;
29
+
30
+ const rows = await input.db
31
+ .select({
32
+ id: schema.assets.id,
33
+ libraryId: schema.assets.libraryId,
34
+ width: schema.assets.width,
35
+ height: schema.assets.height,
36
+ })
37
+ .from(schema.assets)
38
+ .where(inArray(schema.assets.id, [backgroundAssetId, maskAssetId]));
39
+ const background = rows.find((asset: any) => asset.id === backgroundAssetId);
40
+ const mask = rows.find((asset: any) => asset.id === maskAssetId);
41
+ if (
42
+ !background ||
43
+ !mask ||
44
+ background.libraryId !== input.libraryId ||
45
+ mask.libraryId !== input.libraryId
46
+ ) {
47
+ throw new Error(
48
+ "Skeleton image and mask must belong to this asset library.",
49
+ );
50
+ }
51
+ if (
52
+ typeof background.width !== "number" ||
53
+ typeof background.height !== "number" ||
54
+ typeof mask.width !== "number" ||
55
+ typeof mask.height !== "number"
56
+ ) {
57
+ return;
58
+ }
59
+ if (background.width !== mask.width || background.height !== mask.height) {
60
+ throw new Error(
61
+ "Skeleton inpainting mask must be the same pixel size as the background plate.",
62
+ );
63
+ }
64
+ }
@@ -13,7 +13,9 @@ import {
13
13
  IMAGE_MODELS,
14
14
  IMAGE_SIZES,
15
15
  } from "../shared/api.js";
16
+ import { generationPresetSettingsSchema } from "./_generation-preset-settings.js";
16
17
  import { serializeGenerationPreset } from "./_helpers.js";
18
+ import { assertPresetSkeletonAssetsValid } from "./_preset-skeleton-validation.js";
17
19
 
18
20
  export default defineAction({
19
21
  description:
@@ -38,13 +40,14 @@ export default defineAction({
38
40
  .describe(
39
41
  "When true, images generated with this preset composite the library's canonical logo (no-op if the library has no canonical logo).",
40
42
  ),
41
- settings: z.record(z.string(), z.unknown()).optional(),
43
+ settings: generationPresetSettingsSchema.optional(),
42
44
  sortOrder: z.coerce.number().optional(),
43
45
  }),
44
46
  run: async (args) => {
47
+ const db = getDb();
45
48
  await assertAccess("asset-library", args.libraryId, "editor");
46
49
  if (args.collectionId) {
47
- const [collection] = await getDb()
50
+ const [collection] = await db
48
51
  .select()
49
52
  .from(schema.assetCollections)
50
53
  .where(eq(schema.assetCollections.id, args.collectionId))
@@ -53,6 +56,11 @@ export default defineAction({
53
56
  throw new Error("Collection does not belong to this asset library.");
54
57
  }
55
58
  }
59
+ await assertPresetSkeletonAssetsValid({
60
+ db,
61
+ libraryId: args.libraryId,
62
+ settings: args.settings,
63
+ });
56
64
  const now = nowIso();
57
65
  const row = {
58
66
  id: nanoid(),
@@ -78,7 +86,7 @@ export default defineAction({
78
86
  createdAt: now,
79
87
  updatedAt: now,
80
88
  };
81
- await getDb().insert(schema.assetGenerationPresets).values(row);
89
+ await db.insert(schema.assetGenerationPresets).values(row);
82
90
  return serializeGenerationPreset(row);
83
91
  },
84
92
  });
@@ -62,7 +62,7 @@ export default defineAction({
62
62
  .enum(ASPECT_RATIOS)
63
63
  .optional()
64
64
  .describe(
65
- "Slot aspect ratio. When a presetId is set, omit this — the preset's aspect ratio is used unless the user explicitly asks for a different ratio for this slot.",
65
+ "Slot aspect ratio. When a presetId is set, omit this — the preset's aspect ratio is used unless the user explicitly asks for a different ratio for this slot. Note: gpt-image-2 supports only 1:1, 2:3, and 3:2; use a Gemini model for other ratios.",
66
66
  ),
67
67
  imageSize: z.enum(IMAGE_SIZES).optional(),
68
68
  categories: z.array(z.enum(IMAGE_CATEGORIES)).optional(),
@@ -82,7 +82,12 @@ export default defineAction({
82
82
  .describe(
83
83
  "Internal UI state scope for live candidate slots. Usually omitted; embedded picker UIs pass a browser-tab scope.",
84
84
  ),
85
- model: z.enum(IMAGE_MODELS).optional(),
85
+ model: z
86
+ .enum(IMAGE_MODELS)
87
+ .optional()
88
+ .describe(
89
+ "Image model applied to every slot. Omit to use the user's picker default. Gemini models accept any aspectRatio; gpt-image-2 supports ONLY 1:1, 2:3, and 3:2, so don't select it when any slot needs a different ratio — use a Gemini model instead.",
90
+ ),
86
91
  tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
87
92
  intent: z.enum(GENERATION_INTENTS).default("generate"),
88
93
  styleStrength: z.enum(STYLE_STRENGTHS).default("balanced"),
@@ -23,9 +23,20 @@ import {
23
23
  isImageGenerationSetupError,
24
24
  resolveImageModelForRequest,
25
25
  selectReferences,
26
+ type ReferenceForGeneration,
26
27
  } from "../server/lib/generation.js";
27
- import { compositeLogo } from "../server/lib/image-processing.js";
28
+ import {
29
+ applyPresetSkeleton,
30
+ compositeLogo,
31
+ maskFromManualMaskAlpha,
32
+ maskFromPlateAlpha,
33
+ } from "../server/lib/image-processing.js";
28
34
  import { nowIso, parseJson, stringifyJson } from "../server/lib/json.js";
35
+ import {
36
+ clampCutoutAspectRatio,
37
+ normalizePresetSkeletonSpec,
38
+ skeletonUsesCanonicalLogo,
39
+ } from "../server/lib/preset-skeleton.js";
29
40
  import { getObject } from "../server/lib/storage.js";
30
41
  import {
31
42
  ASPECT_RATIOS,
@@ -35,8 +46,11 @@ import {
35
46
  IMAGE_QUALITY_TIERS,
36
47
  IMAGE_SIZES,
37
48
  STYLE_STRENGTHS,
49
+ supportedAspectRatiosForModel,
50
+ type AspectRatio,
38
51
  type ImageCategory,
39
52
  type ImageQualityTier,
53
+ type PresetSkeletonSpec,
40
54
  type StyleBrief,
41
55
  } from "../shared/api.js";
42
56
  import {
@@ -84,7 +98,7 @@ export default defineAction({
84
98
  .enum(ASPECT_RATIOS)
85
99
  .optional()
86
100
  .describe(
87
- "Image aspect ratio. When a presetId is set, omit this — the preset's aspect ratio is used. Pass a value only when there is no preset, or when the user explicitly asks for a ratio different from the preset's.",
101
+ "Image aspect ratio. When a presetId is set, omit this — the preset's aspect ratio is used. Pass a value only when there is no preset, or when the user explicitly asks for a ratio different from the preset's. Note: OpenAI image models support only 1:1, 2:3, and 3:2; gpt-image-1 is used for transparent cutout skeleton subjects, while other ratios (16:9, 9:16, 4:5, 21:9, …) require a Gemini model.",
88
102
  ),
89
103
  imageSize: z
90
104
  .enum(IMAGE_SIZES)
@@ -92,7 +106,12 @@ export default defineAction({
92
106
  .describe(
93
107
  "Output resolution tier. When a presetId is set, omit this — the preset's size is used unless the user explicitly requests a different one.",
94
108
  ),
95
- model: z.enum(IMAGE_MODELS).optional(),
109
+ model: z
110
+ .enum(IMAGE_MODELS)
111
+ .optional()
112
+ .describe(
113
+ "Image model. Omit to use the user's picker default. Gemini models accept any aspectRatio; gpt-image-2 is the normal opaque OpenAI default, while gpt-image-1 is reserved for transparent cutout skeletons. OpenAI image models support ONLY 1:1, 2:3, and 3:2 — do not pair them with other ratios, choose a Gemini model (e.g. gemini-3-pro-image) for those instead.",
114
+ ),
96
115
  tier: z.enum(IMAGE_QUALITY_TIERS).optional(),
97
116
  intent: z.enum(GENERATION_INTENTS).default("generate"),
98
117
  styleStrength: z.enum(STYLE_STRENGTHS).default("balanced"),
@@ -256,25 +275,32 @@ export default defineAction({
256
275
  ...parseJson<StyleBrief>(library.styleBrief, {}),
257
276
  ...parseJson<StyleBrief>(collection?.styleBrief, {}),
258
277
  };
259
- const resolvedAspectRatio = (args.aspectRatio ??
278
+ const presetSettings = parseJson<{
279
+ tier?: ImageQualityTier;
280
+ includeLogo?: boolean;
281
+ skeletonSpec?: unknown;
282
+ }>(preset?.settings, {});
283
+ const skeletonSpec = normalizePresetSkeletonSpec(
284
+ presetSettings.skeletonSpec,
285
+ );
286
+ const isSkeletonCutout = skeletonSpec?.contentMode === "cutout";
287
+ const outputAspectRatio = (args.aspectRatio ??
260
288
  preset?.aspectRatio ??
261
289
  collection?.defaultAspectRatio ??
262
- "16:9") as (typeof ASPECT_RATIOS)[number];
290
+ "16:9") as AspectRatio;
263
291
  const resolvedImageSize = (args.imageSize ??
264
292
  preset?.imageSize ??
265
293
  collection?.defaultImageSize ??
266
294
  "2K") as (typeof IMAGE_SIZES)[number];
267
- const presetSettings = parseJson<{
268
- tier?: ImageQualityTier;
269
- includeLogo?: boolean;
270
- }>(preset?.settings, {});
271
295
  const resolvedTier = args.tier ?? presetSettings.tier;
272
- const resolvedIncludeLogo =
296
+ const requestedIncludeLogo =
273
297
  args.includeLogo ?? presetSettings.includeLogo ?? false;
298
+ const resolvedIncludeLogo =
299
+ requestedIncludeLogo && !skeletonUsesCanonicalLogo(skeletonSpec);
274
300
  const category = (args.categories?.[0] ??
275
301
  preset?.category ??
276
302
  collection?.category) as ImageCategory | undefined;
277
- const resolvedModel = resolveImageModelForRequest({
303
+ const selectedModel = resolveImageModelForRequest({
278
304
  explicitModel: args.model,
279
305
  imageModelDefault,
280
306
  explicitTier: args.tier,
@@ -283,9 +309,65 @@ export default defineAction({
283
309
  presetModel: preset?.model as (typeof IMAGE_MODELS)[number] | undefined,
284
310
  embeddedText: args.embeddedText,
285
311
  }) as (typeof IMAGE_MODELS)[number];
312
+ if (
313
+ isSkeletonCutout &&
314
+ args.model &&
315
+ args.model !== "gpt-image-1" &&
316
+ args.model !== "gpt-image-2"
317
+ ) {
318
+ throw new Error(
319
+ "Cutout skeleton presets require gpt-image-1 for transparent output or gpt-image-2 for mask inpainting. Remove the explicit model override or choose one of those models.",
320
+ );
321
+ }
322
+ const resolvedModel = (
323
+ isSkeletonCutout && selectedModel !== "gpt-image-2"
324
+ ? "gpt-image-1"
325
+ : selectedModel
326
+ ) as (typeof IMAGE_MODELS)[number];
327
+ if (isSkeletonCutout && selectedModel !== resolvedModel) {
328
+ logSkeletonGeneration("model_overridden", {
329
+ presetId: preset?.id,
330
+ selectedModel,
331
+ model: resolvedModel,
332
+ });
333
+ }
286
334
  const resolvedCategories =
287
335
  args.categories ??
288
336
  (preset?.category ? ([preset.category] as any) : undefined);
337
+ const skeletonReferenceExclusionIds =
338
+ skeletonCompositeAssetIds(skeletonSpec);
339
+ const skeletonAssets = skeletonSpec
340
+ ? await loadSkeletonAssets({
341
+ spec: skeletonSpec,
342
+ libraryId: args.libraryId,
343
+ canonicalLogoAssetId: library.canonicalLogoAssetId,
344
+ appendCanonicalLogo:
345
+ resolvedIncludeLogo &&
346
+ !(isSkeletonCutout && resolvedModel === "gpt-image-2"),
347
+ })
348
+ : null;
349
+ const useSkeletonInpaint =
350
+ isSkeletonCutout &&
351
+ Boolean(skeletonAssets?.backgroundAsset) &&
352
+ resolvedModel === "gpt-image-2";
353
+ const resolvedAspectRatio =
354
+ isSkeletonCutout && !useSkeletonInpaint
355
+ ? clampCutoutAspectRatio({
356
+ aspectRatio: outputAspectRatio,
357
+ supported: supportedAspectRatiosForModel("gpt-image-1"),
358
+ })
359
+ : outputAspectRatio;
360
+ if (
361
+ isSkeletonCutout &&
362
+ !useSkeletonInpaint &&
363
+ outputAspectRatio !== resolvedAspectRatio
364
+ ) {
365
+ logSkeletonGeneration("aspect_ratio_clamped", {
366
+ presetId: preset?.id,
367
+ outputAspectRatio,
368
+ subjectAspectRatio: resolvedAspectRatio,
369
+ });
370
+ }
289
371
  const promptForRun = applyPromptTemplate(
290
372
  preset?.promptTemplate,
291
373
  args.prompt,
@@ -302,21 +384,37 @@ export default defineAction({
302
384
  .filter(Boolean)
303
385
  .join("\n")
304
386
  : "";
305
- const references = await selectReferences({
306
- libraryId: args.libraryId,
307
- collectionId: resolvedCollectionId,
308
- categories: resolvedCategories,
309
- referenceAssetIds: args.referenceAssetIds,
310
- sourceAssetId: args.sourceAssetId,
311
- subjectAssetId: args.subjectAssetId,
312
- intent: args.intent,
313
- limit:
387
+ let references: ReferenceForGeneration[];
388
+ if (useSkeletonInpaint) {
389
+ references = await inpaintReferencesForSkeleton(skeletonAssets);
390
+ } else {
391
+ const backgroundPlateReference =
392
+ backgroundPlateReferenceForSkeleton(skeletonAssets);
393
+ const baseReferenceLimit =
314
394
  args.intent !== "restyle" &&
315
395
  preset?.referencePolicy === "explicit" &&
316
396
  !args.referenceAssetIds?.length
317
397
  ? 0
318
- : DEFAULT_GENERATION_REFERENCE_LIMIT,
319
- });
398
+ : DEFAULT_GENERATION_REFERENCE_LIMIT;
399
+ const referenceLimit =
400
+ backgroundPlateReference && baseReferenceLimit > 0
401
+ ? baseReferenceLimit + 1
402
+ : baseReferenceLimit;
403
+ references = await selectReferences({
404
+ libraryId: args.libraryId,
405
+ collectionId: resolvedCollectionId,
406
+ categories: resolvedCategories,
407
+ referenceAssetIds: args.referenceAssetIds,
408
+ excludeAssetIds: skeletonReferenceExclusionIds,
409
+ sourceAssetId: args.sourceAssetId,
410
+ subjectAssetId: args.subjectAssetId,
411
+ intent: args.intent,
412
+ limit: referenceLimit,
413
+ });
414
+ if (backgroundPlateReference) {
415
+ references.unshift(backgroundPlateReference);
416
+ }
417
+ }
320
418
  const compiledPrompt = compilePrompt({
321
419
  libraryTitle: library.title,
322
420
  styleBrief,
@@ -328,6 +426,13 @@ export default defineAction({
328
426
  textPlacement: args.textPlacement,
329
427
  referenceCount: references.length,
330
428
  includeLogo: resolvedIncludeLogo,
429
+ skeletonContentMode: skeletonSpec?.contentMode ?? null,
430
+ hasBackgroundPlate: Boolean(
431
+ isSkeletonCutout &&
432
+ !useSkeletonInpaint &&
433
+ skeletonAssets?.backgroundAsset,
434
+ ),
435
+ skeletonInpaint: useSkeletonInpaint,
331
436
  aspectRatio: resolvedAspectRatio,
332
437
  imageSize: resolvedImageSize,
333
438
  category,
@@ -371,6 +476,10 @@ export default defineAction({
371
476
  imageSize: resolvedImageSize,
372
477
  groundingMode: args.groundingMode,
373
478
  includeLogo: resolvedIncludeLogo,
479
+ requestedIncludeLogo,
480
+ skeletonSpec: skeletonSpec ?? null,
481
+ outputAspectRatio,
482
+ subjectAspectRatio: resolvedAspectRatio,
374
483
  categories: resolvedCategories ?? [],
375
484
  collectionId: resolvedCollectionId ?? null,
376
485
  presetId: preset?.id ?? null,
@@ -394,6 +503,10 @@ export default defineAction({
394
503
  styleStrength: args.styleStrength,
395
504
  tier: resolvedTier,
396
505
  includeLogo: resolvedIncludeLogo,
506
+ requestedIncludeLogo,
507
+ skeletonSpec: skeletonSpec ?? null,
508
+ outputAspectRatio,
509
+ subjectAspectRatio: resolvedAspectRatio,
397
510
  categories: resolvedCategories ?? [],
398
511
  presetId: preset?.id,
399
512
  sessionId: session?.id,
@@ -409,7 +522,7 @@ export default defineAction({
409
522
  prompt: args.prompt,
410
523
  compiledPrompt,
411
524
  model: resolvedModel,
412
- aspectRatio: resolvedAspectRatio,
525
+ aspectRatio: outputAspectRatio,
413
526
  imageSize: resolvedImageSize,
414
527
  groundingMode: args.groundingMode,
415
528
  referenceAssetIds: stringifyJson(references.map((ref) => ref.id)),
@@ -455,8 +568,13 @@ export default defineAction({
455
568
  compiledPrompt,
456
569
  references,
457
570
  model: resolvedModel,
571
+ mode: useSkeletonInpaint ? "edit" : undefined,
458
572
  aspectRatio: resolvedAspectRatio,
459
573
  imageSize: resolvedImageSize,
574
+ background:
575
+ isSkeletonCutout && !useSkeletonInpaint
576
+ ? "transparent"
577
+ : undefined,
460
578
  groundingMode: args.groundingMode,
461
579
  intent: args.intent,
462
580
  styleStrength: args.styleStrength,
@@ -470,7 +588,17 @@ export default defineAction({
470
588
  await deleteAppState("image-generation-setup").catch(() => {});
471
589
  let image = generated.image;
472
590
  let mimeType = generated.mimeType;
473
- if (resolvedIncludeLogo && library.canonicalLogoAssetId) {
591
+ if (skeletonAssets && !useSkeletonInpaint) {
592
+ image = await applyPresetSkeleton({
593
+ subject: image,
594
+ spec: skeletonAssets.spec,
595
+ canvasAspectRatio: outputAspectRatio,
596
+ backgroundAsset: skeletonAssets.backgroundAsset,
597
+ canonicalLogo: skeletonAssets.canonicalLogo,
598
+ foregroundAssets: skeletonAssets.foregroundAssets,
599
+ });
600
+ mimeType = "image/png";
601
+ } else if (resolvedIncludeLogo && library.canonicalLogoAssetId) {
474
602
  const [logo] = await db
475
603
  .select()
476
604
  .from(schema.assets)
@@ -534,7 +662,7 @@ export default defineAction({
534
662
  status: "candidate",
535
663
  prompt: args.prompt,
536
664
  model: generated.model,
537
- aspectRatio: resolvedAspectRatio,
665
+ aspectRatio: outputAspectRatio,
538
666
  imageSize: resolvedImageSize,
539
667
  generationRunId: runId,
540
668
  metadata: {
@@ -547,6 +675,10 @@ export default defineAction({
547
675
  styleStrength: args.styleStrength,
548
676
  tier: resolvedTier,
549
677
  includeLogo: resolvedIncludeLogo,
678
+ requestedIncludeLogo,
679
+ skeletonSpec: skeletonSpec ?? null,
680
+ outputAspectRatio,
681
+ subjectAspectRatio: resolvedAspectRatio,
550
682
  presetId: preset?.id,
551
683
  sessionId: session?.id,
552
684
  generated: true,
@@ -592,6 +724,10 @@ export default defineAction({
592
724
  styleStrength: args.styleStrength,
593
725
  tier: resolvedTier,
594
726
  includeLogo: resolvedIncludeLogo,
727
+ requestedIncludeLogo,
728
+ skeletonSpec: skeletonSpec ?? null,
729
+ outputAspectRatio,
730
+ subjectAspectRatio: resolvedAspectRatio,
595
731
  categories: resolvedCategories ?? [],
596
732
  referenceSelection,
597
733
  settingsUsed,
@@ -667,3 +803,184 @@ export default defineAction({
667
803
  }
668
804
  },
669
805
  });
806
+
807
+ function skeletonCompositeAssetIds(
808
+ spec: PresetSkeletonSpec | null,
809
+ ): string[] | undefined {
810
+ if (!spec) return undefined;
811
+ const ids = new Set<string>();
812
+ if (spec.background.type === "asset") ids.add(spec.background.assetId);
813
+ if (spec.mask?.type === "asset") ids.add(spec.mask.assetId);
814
+ for (const layer of spec.foreground ?? []) {
815
+ if (layer.source !== "canonicalLogo") ids.add(layer.source.assetId);
816
+ }
817
+ return ids.size ? [...ids] : undefined;
818
+ }
819
+
820
+ function backgroundPlateReferenceForSkeleton(
821
+ skeletonAssets: LoadedSkeletonAssets | null,
822
+ ): ReferenceForGeneration | null {
823
+ if (
824
+ !skeletonAssets?.backgroundAsset ||
825
+ !skeletonAssets.backgroundAssetId ||
826
+ !skeletonAssets.backgroundMimeType
827
+ ) {
828
+ return null;
829
+ }
830
+ return {
831
+ id: skeletonAssets.backgroundAssetId,
832
+ role: "background_reference",
833
+ category: "skeleton",
834
+ mimeType: skeletonAssets.backgroundMimeType,
835
+ data: skeletonAssets.backgroundAsset.toString("base64"),
836
+ selectionReason: "explicit",
837
+ };
838
+ }
839
+
840
+ async function inpaintReferencesForSkeleton(
841
+ skeletonAssets: LoadedSkeletonAssets | null,
842
+ ): Promise<ReferenceForGeneration[]> {
843
+ if (
844
+ !skeletonAssets?.backgroundAsset ||
845
+ !skeletonAssets.backgroundAssetId ||
846
+ !skeletonAssets.backgroundMimeType
847
+ ) {
848
+ throw new Error("Skeleton inpainting requires a background plate image.");
849
+ }
850
+ const mask = skeletonAssets.maskAsset
851
+ ? await maskFromManualMaskAlpha({
852
+ mask: skeletonAssets.maskAsset,
853
+ plate: skeletonAssets.backgroundAsset,
854
+ })
855
+ : await maskFromPlateAlpha(skeletonAssets.backgroundAsset);
856
+ return [
857
+ {
858
+ id: skeletonAssets.backgroundAssetId,
859
+ role: "edit_target",
860
+ category: "skeleton",
861
+ mimeType: skeletonAssets.backgroundMimeType,
862
+ data: skeletonAssets.backgroundAsset.toString("base64"),
863
+ selectionReason: "explicit",
864
+ },
865
+ {
866
+ id:
867
+ skeletonAssets.maskAssetId ??
868
+ `${skeletonAssets.backgroundAssetId}:mask`,
869
+ role: "mask",
870
+ category: "skeleton",
871
+ mimeType: "image/png",
872
+ data: mask.toString("base64"),
873
+ selectionReason: "explicit",
874
+ },
875
+ ];
876
+ }
877
+
878
+ type LoadedSkeletonAssets = {
879
+ spec: PresetSkeletonSpec;
880
+ backgroundAssetId?: string;
881
+ backgroundMimeType?: string;
882
+ backgroundAsset?: Buffer;
883
+ maskAssetId?: string;
884
+ maskAsset?: Buffer;
885
+ canonicalLogo?: Buffer;
886
+ foregroundAssets: Record<string, Buffer>;
887
+ };
888
+
889
+ async function loadSkeletonAssets(input: {
890
+ spec: PresetSkeletonSpec;
891
+ libraryId: string;
892
+ canonicalLogoAssetId?: string | null;
893
+ appendCanonicalLogo: boolean;
894
+ }): Promise<LoadedSkeletonAssets> {
895
+ const db = getDb();
896
+ const spec = appendCanonicalLogoToSkeleton(
897
+ input.spec,
898
+ input.appendCanonicalLogo,
899
+ );
900
+ const assetIds = new Set<string>();
901
+ if (spec.background.type === "asset") assetIds.add(spec.background.assetId);
902
+ if (spec.mask?.type === "asset") assetIds.add(spec.mask.assetId);
903
+ for (const layer of spec.foreground ?? []) {
904
+ if (layer.source !== "canonicalLogo") assetIds.add(layer.source.assetId);
905
+ }
906
+ if (input.canonicalLogoAssetId) assetIds.add(input.canonicalLogoAssetId);
907
+
908
+ const buffers = new Map<string, Buffer>();
909
+ const mimeTypes = new Map<string, string>();
910
+ for (const assetId of assetIds) {
911
+ const [asset] = await db
912
+ .select()
913
+ .from(schema.assets)
914
+ .where(eq(schema.assets.id, assetId))
915
+ .limit(1);
916
+ if (!asset) {
917
+ if (assetId === input.canonicalLogoAssetId) continue;
918
+ throw new Error("Skeleton asset not found.");
919
+ }
920
+ if (asset.libraryId !== input.libraryId) {
921
+ throw new Error("Skeleton assets must belong to the selected brand kit.");
922
+ }
923
+ if (!asset.mimeType.startsWith("image/")) {
924
+ throw new Error("Skeleton assets must be images.");
925
+ }
926
+ buffers.set(assetId, await getObject(asset.objectKey));
927
+ mimeTypes.set(assetId, asset.mimeType);
928
+ }
929
+
930
+ const foregroundAssets: Record<string, Buffer> = {};
931
+ for (const layer of spec.foreground ?? []) {
932
+ if (layer.source !== "canonicalLogo") {
933
+ const buffer = buffers.get(layer.source.assetId);
934
+ if (buffer) foregroundAssets[layer.source.assetId] = buffer;
935
+ }
936
+ }
937
+
938
+ return {
939
+ spec,
940
+ backgroundAssetId:
941
+ spec.background.type === "asset" ? spec.background.assetId : undefined,
942
+ backgroundMimeType:
943
+ spec.background.type === "asset"
944
+ ? mimeTypes.get(spec.background.assetId)
945
+ : undefined,
946
+ backgroundAsset:
947
+ spec.background.type === "asset"
948
+ ? buffers.get(spec.background.assetId)
949
+ : undefined,
950
+ maskAssetId: spec.mask?.type === "asset" ? spec.mask.assetId : undefined,
951
+ maskAsset:
952
+ spec.mask?.type === "asset" ? buffers.get(spec.mask.assetId) : undefined,
953
+ canonicalLogo: input.canonicalLogoAssetId
954
+ ? buffers.get(input.canonicalLogoAssetId)
955
+ : undefined,
956
+ foregroundAssets,
957
+ };
958
+ }
959
+
960
+ function appendCanonicalLogoToSkeleton(
961
+ spec: PresetSkeletonSpec,
962
+ appendCanonicalLogo: boolean,
963
+ ): PresetSkeletonSpec {
964
+ if (!appendCanonicalLogo || skeletonUsesCanonicalLogo(spec)) return spec;
965
+ return {
966
+ ...spec,
967
+ foreground: [
968
+ ...(spec.foreground ?? []),
969
+ { source: "canonicalLogo", x: 0.805, y: 0.06, w: 0.16 },
970
+ ],
971
+ };
972
+ }
973
+
974
+ function logSkeletonGeneration(
975
+ event: string,
976
+ fields: Record<string, unknown>,
977
+ ): void {
978
+ if (process.env.NODE_ENV === "test") return;
979
+ const detail = Object.entries(fields)
980
+ .filter(([, value]) => value !== undefined)
981
+ .map(([key, value]) => `${key}=${value}`)
982
+ .join(" ");
983
+ console.info(
984
+ `[assets] preset-skeleton ${event}${detail ? ` ${detail}` : ""}`,
985
+ );
986
+ }
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
 
5
5
  import { getDb, schema } from "../server/db/index.js";
6
6
  import {
7
- requireLibrary,
7
+ requireLibraryAccess,
8
8
  serializeAssets,
9
9
  serializeGenerationRun,
10
10
  serializeLibrary,
@@ -19,7 +19,8 @@ export default defineAction({
19
19
  http: { method: "GET" },
20
20
  readOnly: true,
21
21
  run: async ({ id }, ctx) => {
22
- const library = await requireLibrary(id, ctx);
22
+ const access = await requireLibraryAccess(id, ctx);
23
+ const library = access.resource;
23
24
  const db = getDb();
24
25
  const [collections, folders, assets, runs] = await Promise.all([
25
26
  db
@@ -42,7 +43,7 @@ export default defineAction({
42
43
  .orderBy(desc(schema.assetGenerationRuns.createdAt)),
43
44
  ]);
44
45
  return {
45
- library: serializeLibrary(library),
46
+ library: serializeLibrary({ ...library, accessRole: access.role }),
46
47
  collections,
47
48
  folders,
48
49
  assets: serializeAssets(assets),
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
 
5
5
  export default defineAction({
6
6
  description:
7
- "Navigate the Assets UI. Views (internal keys, with the surface they open): create, picker (the embedded image Library picker), libraries (the unified Library workspace), library (a single Brand Kit inside Library), asset, generation-session, generation-run, extensions, audit, settings. Use threadId to open a specific create/chat thread; use libraryId, assetId, sessionId, runId, or extensionId where appropriate.",
7
+ "Navigate the Assets UI. Views (internal keys, with the surface they open): create, picker (the embedded image Library picker), libraries (the unified Library workspace), library (a single Brand Kit inside Library), preset (a generation preset editor), asset, generation-session, generation-run, extensions, audit, settings. Use threadId to open a specific create/chat thread; use libraryId, presetId, assetId, sessionId, runId, or extensionId where appropriate.",
8
8
  schema: z.object({
9
9
  view: z
10
10
  .enum([
@@ -12,6 +12,7 @@ export default defineAction({
12
12
  "picker",
13
13
  "libraries",
14
14
  "library",
15
+ "preset",
15
16
  "asset",
16
17
  "image",
17
18
  "generation-session",