@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
- package/corpus/core/src/client/use-action.ts +39 -2
- package/corpus/core/src/client/use-db-sync.ts +44 -18
- package/corpus/core/src/db/create-get-db.ts +32 -2
- package/corpus/core/src/server/framework-request-handler.ts +52 -4
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
- package/corpus/templates/assets/AGENTS.md +23 -4
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
- package/corpus/templates/assets/actions/_helpers.ts +8 -1
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
- package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
- package/corpus/templates/assets/actions/generate-image.ts +342 -25
- package/corpus/templates/assets/actions/get-library.ts +4 -3
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
- package/corpus/templates/assets/actions/update-asset.ts +1 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
- package/corpus/templates/assets/actions/view-screen.ts +8 -1
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
- package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
- package/corpus/templates/assets/app/i18n-data.ts +650 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
- package/corpus/templates/assets/app/routes/library.tsx +43 -1
- package/corpus/templates/assets/server/lib/generation.ts +366 -44
- package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
- package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
- package/corpus/templates/assets/shared/api.ts +47 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
- package/corpus/templates/clips/app/routes/record.tsx +157 -68
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
- package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
- package/corpus/templates/content/AGENTS.md +52 -52
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
- package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
- package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
- package/corpus/templates/content/actions/_database-utils.ts +44 -4
- package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
- package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
- package/corpus/templates/content/actions/delete-content-database.ts +1 -0
- package/corpus/templates/content/actions/get-document.ts +11 -2
- package/corpus/templates/content/actions/list-content-databases.ts +57 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
- package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
- package/corpus/templates/content/actions/restore-content-database.ts +1 -0
- package/corpus/templates/content/actions/update-document.ts +43 -0
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
- package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
- package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
- package/corpus/templates/content/app/global.css +41 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
- package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
- package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
- package/corpus/templates/content/app/i18n-data.ts +355 -0
- package/corpus/templates/content/app/root.tsx +22 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
- package/corpus/templates/content/shared/api.ts +13 -0
- package/corpus/templates/content/shared/builder-mdx.ts +16 -3
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
- package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
- package/corpus/templates/design/AGENTS.md +9 -2
- package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
- package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
- package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
- package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
- package/corpus/templates/design/actions/create-component.ts +29 -60
- package/corpus/templates/design/actions/create-file.ts +9 -2
- package/corpus/templates/design/actions/duplicate-design.ts +31 -2
- package/corpus/templates/design/actions/edit-design.ts +36 -38
- package/corpus/templates/design/actions/generate-design-title.ts +87 -0
- package/corpus/templates/design/actions/generate-design.ts +53 -18
- package/corpus/templates/design/actions/generate-screens.ts +108 -4
- package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
- package/corpus/templates/design/actions/insert-asset.ts +34 -29
- package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
- package/corpus/templates/design/actions/present-design-variants.ts +7 -2
- package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
- package/corpus/templates/design/actions/update-file.ts +190 -75
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
- package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
- package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
- package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
- package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
- package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
- package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
- package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
- package/corpus/templates/design/app/components/design/types.ts +15 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
- package/corpus/templates/design/app/global.css +8 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
- package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
- package/corpus/templates/design/app/i18n-data.ts +1664 -152
- package/corpus/templates/design/app/i18n-motion.ts +563 -0
- package/corpus/templates/design/app/lib/design-import.ts +73 -0
- package/corpus/templates/design/app/pages/Index.tsx +41 -22
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
- package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
- package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
- package/corpus/templates/design/server/source-workspace.ts +139 -59
- package/corpus/templates/design/shared/board-file.ts +11 -2
- package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
- package/corpus/templates/design/shared/canvas-math.ts +354 -35
- package/corpus/templates/design/shared/code-layer.ts +277 -8
- package/corpus/templates/design/shared/design-state.ts +11 -4
- package/corpus/templates/design/shared/interaction-states.ts +548 -0
- package/corpus/templates/design/shared/motion-compiler.ts +186 -23
- package/corpus/templates/design/shared/motion-easing.ts +294 -0
- package/corpus/templates/design/shared/motion-timeline.ts +400 -34
- package/corpus/templates/design/shared/prompt-title.ts +95 -0
- package/corpus/templates/design/shared/responsive-classes.ts +448 -10
- package/corpus/templates/design/shared/screen-annotation.ts +46 -0
- package/corpus/templates/design/shared/shader-fills.ts +1009 -0
- package/corpus/templates/design/shared/shader-presets.ts +852 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
- package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
- package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +34 -3
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +37 -18
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/create-get-db.d.ts +25 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +25 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +37 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/package.json +1 -1
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
- package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseCssColor,
|
|
3
|
+
rgbaToCss,
|
|
4
|
+
withColorOpacity,
|
|
5
|
+
} from "@shared/color-utils";
|
|
6
|
+
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
7
|
+
|
|
8
|
+
import { Input } from "@/components/ui/input";
|
|
9
|
+
import { Label } from "@/components/ui/label";
|
|
10
|
+
import {
|
|
11
|
+
Select,
|
|
12
|
+
SelectContent,
|
|
13
|
+
SelectItem,
|
|
14
|
+
SelectTrigger,
|
|
15
|
+
SelectValue,
|
|
16
|
+
} from "@/components/ui/select";
|
|
17
|
+
import { Slider } from "@/components/ui/slider";
|
|
18
|
+
|
|
19
|
+
import type { StyleChangeMeta } from "../EditPanel";
|
|
20
|
+
import {
|
|
21
|
+
DesignColorPicker,
|
|
22
|
+
type DesignFillRowPatch,
|
|
23
|
+
type DesignGradientStop,
|
|
24
|
+
type DesignGradientStopPatch,
|
|
25
|
+
type DesignGradientType,
|
|
26
|
+
type ImageFillValue,
|
|
27
|
+
} from "../inspector";
|
|
28
|
+
import type { DesignPaintType } from "../inspector/DesignColorPicker";
|
|
29
|
+
import type { GlslShaderPanelContext } from "../inspector/GlslShaderPanel";
|
|
30
|
+
import {
|
|
31
|
+
buildFillRows,
|
|
32
|
+
buildGradientLayer,
|
|
33
|
+
defaultGradientLayer,
|
|
34
|
+
defaultGradientStops,
|
|
35
|
+
fillLayerId,
|
|
36
|
+
fillLayerIndex,
|
|
37
|
+
joinCssLayers,
|
|
38
|
+
parseGradientLayer,
|
|
39
|
+
SOLID_FILL_ID,
|
|
40
|
+
splitCssLayers,
|
|
41
|
+
} from "./fill-gradient-helpers";
|
|
42
|
+
import { colorHasVisibleAlpha, cssColorOrFallback } from "./position-helpers";
|
|
43
|
+
import { isMixedValue, MIXED_VALUE } from "./selection-helpers";
|
|
44
|
+
|
|
45
|
+
function normalizeLengthValue(raw: string, defaultUnit: string): string | null {
|
|
46
|
+
const trimmed = raw.trim();
|
|
47
|
+
// Empty / invalid input returns null so the caller reverts the field instead
|
|
48
|
+
// of committing an empty or garbage CSS value (e.g. fontSize:"" or
|
|
49
|
+
// flexBasis:"abc") to the element's inline style.
|
|
50
|
+
if (!trimmed) return null;
|
|
51
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) return `${trimmed}${defaultUnit}`;
|
|
52
|
+
// Validate free-form CSS so junk text never reaches the style. Fall back to
|
|
53
|
+
// accepting the value when CSS.supports is unavailable (SSR/tests) to keep
|
|
54
|
+
// prior behavior in non-DOM environments.
|
|
55
|
+
if (typeof CSS !== "undefined" && typeof CSS.supports === "function") {
|
|
56
|
+
const ok =
|
|
57
|
+
CSS.supports("width", trimmed) ||
|
|
58
|
+
CSS.supports("font-size", trimmed) ||
|
|
59
|
+
CSS.supports("flex-basis", trimmed);
|
|
60
|
+
return ok ? trimmed : null;
|
|
61
|
+
}
|
|
62
|
+
return trimmed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Compact input row: label + text input.
|
|
66
|
+
*
|
|
67
|
+
* For CSS length fields (font-size, padding, width, etc.) pass `defaultUnit`
|
|
68
|
+
* so the change is committed on blur/Enter and a bare number auto-appends the
|
|
69
|
+
* unit. Without that, intermediate keystrokes apply invalid CSS — typing "32"
|
|
70
|
+
* for a font-size silently fails because "32" alone isn't a valid length, and
|
|
71
|
+
* it never reaches "32px" because every keystroke re-applies the broken
|
|
72
|
+
* value.
|
|
73
|
+
*/
|
|
74
|
+
export function PropInput({
|
|
75
|
+
label,
|
|
76
|
+
value,
|
|
77
|
+
onChange,
|
|
78
|
+
placeholder,
|
|
79
|
+
type = "text",
|
|
80
|
+
defaultUnit,
|
|
81
|
+
}: {
|
|
82
|
+
label: string;
|
|
83
|
+
value: string;
|
|
84
|
+
onChange: (value: string) => void;
|
|
85
|
+
placeholder?: string;
|
|
86
|
+
type?: string;
|
|
87
|
+
defaultUnit?: string;
|
|
88
|
+
}) {
|
|
89
|
+
const [draft, setDraft] = useState(value);
|
|
90
|
+
const mixed = isMixedValue(value);
|
|
91
|
+
// Escape reverts and blurs; the blur handler must then skip its commit or it
|
|
92
|
+
// would re-commit the stale draft closure (mirrors ScrubInput's Escape path).
|
|
93
|
+
const skipNextBlurCommitRef = useRef(false);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
setDraft(value);
|
|
97
|
+
}, [value]);
|
|
98
|
+
|
|
99
|
+
const commit = () => {
|
|
100
|
+
if (isMixedValue(draft)) return;
|
|
101
|
+
if (defaultUnit === undefined) {
|
|
102
|
+
if (draft !== value) onChange(draft);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const next = normalizeLengthValue(draft, defaultUnit);
|
|
106
|
+
if (next === null) {
|
|
107
|
+
// Invalid or empty — revert the field to the last committed value.
|
|
108
|
+
setDraft(value);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (next !== draft) setDraft(next);
|
|
112
|
+
if (next !== value) onChange(next);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div className="flex items-center gap-1.5">
|
|
117
|
+
<FieldLabel>{label}</FieldLabel>
|
|
118
|
+
<Input
|
|
119
|
+
type={type}
|
|
120
|
+
value={draft}
|
|
121
|
+
onFocus={(e) => {
|
|
122
|
+
if (mixed) e.currentTarget.select();
|
|
123
|
+
}}
|
|
124
|
+
onChange={(e) => {
|
|
125
|
+
setDraft(e.target.value);
|
|
126
|
+
// For length fields, defer the live update until blur/Enter so that
|
|
127
|
+
// invalid intermediate strings ("3", "32", "32p") don't get applied
|
|
128
|
+
// and discarded by the browser. Free-text fields (without
|
|
129
|
+
// defaultUnit) keep the responsive live-update behavior.
|
|
130
|
+
if (defaultUnit === undefined) onChange(e.target.value);
|
|
131
|
+
}}
|
|
132
|
+
onBlur={() => {
|
|
133
|
+
if (skipNextBlurCommitRef.current) {
|
|
134
|
+
skipNextBlurCommitRef.current = false;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
commit();
|
|
138
|
+
}}
|
|
139
|
+
onKeyDown={(e) => {
|
|
140
|
+
if (e.key === "Enter") {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
commit();
|
|
143
|
+
(e.currentTarget as HTMLInputElement).blur();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (e.key === "Escape") {
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
// Revert the draft to the last committed value and blur, matching
|
|
149
|
+
// ScrubInput's Escape behavior.
|
|
150
|
+
setDraft(value);
|
|
151
|
+
skipNextBlurCommitRef.current = true;
|
|
152
|
+
(e.currentTarget as HTMLInputElement).blur();
|
|
153
|
+
}
|
|
154
|
+
}}
|
|
155
|
+
placeholder={placeholder}
|
|
156
|
+
className="h-6 min-w-0 rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] shadow-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)] md:!text-[11px]"
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Compact color input: label + design-editor picker popover. */
|
|
163
|
+
export function ColorInput({
|
|
164
|
+
label,
|
|
165
|
+
value,
|
|
166
|
+
onChange,
|
|
167
|
+
backgroundImage,
|
|
168
|
+
backgroundSize,
|
|
169
|
+
backgroundRepeat,
|
|
170
|
+
backgroundPosition,
|
|
171
|
+
onBackgroundImageChange,
|
|
172
|
+
onImageFillChange,
|
|
173
|
+
blendMode,
|
|
174
|
+
onBlendModeChange,
|
|
175
|
+
supportsLayeredFills = false,
|
|
176
|
+
documentColors,
|
|
177
|
+
supportedPaintTypes,
|
|
178
|
+
pickerKey,
|
|
179
|
+
glslShaderContext,
|
|
180
|
+
}: {
|
|
181
|
+
label: string;
|
|
182
|
+
value: string;
|
|
183
|
+
onChange: (value: string, meta?: StyleChangeMeta) => void;
|
|
184
|
+
backgroundImage?: string;
|
|
185
|
+
backgroundSize?: string;
|
|
186
|
+
backgroundRepeat?: string;
|
|
187
|
+
backgroundPosition?: string;
|
|
188
|
+
onBackgroundImageChange?: (value: string) => void;
|
|
189
|
+
onImageFillChange?: (value: ImageFillValue) => void;
|
|
190
|
+
blendMode?: string;
|
|
191
|
+
onBlendModeChange?: (value: string) => void;
|
|
192
|
+
supportsLayeredFills?: boolean;
|
|
193
|
+
/** Hex strings already in use on the page — forwarded to the color picker swatch grid. */
|
|
194
|
+
documentColors?: string[];
|
|
195
|
+
/**
|
|
196
|
+
* Restricts which paint-type tabs the popover renders. Omit for the full
|
|
197
|
+
* set (solid + gradients + image + …). Pass `["solid"]` for properties
|
|
198
|
+
* with no clean gradient/image equivalent (e.g. CSS border/outline
|
|
199
|
+
* strokes) so the tab is hidden instead of clickable-but-discarded.
|
|
200
|
+
*/
|
|
201
|
+
supportedPaintTypes?: DesignPaintType[];
|
|
202
|
+
pickerKey?: string;
|
|
203
|
+
/**
|
|
204
|
+
* Persistence context for the code-backed GLSL Shader paint type. When
|
|
205
|
+
* provided, the picker's Shader tab opens the GlslShaderPanel (Created by
|
|
206
|
+
* you / Create new (AI) / Presets) which persists real GLSL source into
|
|
207
|
+
* the screen HTML. Omit to fall back to the legacy shader presets panel.
|
|
208
|
+
*/
|
|
209
|
+
glslShaderContext?: GlslShaderPanelContext;
|
|
210
|
+
}) {
|
|
211
|
+
const [draft, setDraft] = useState(value);
|
|
212
|
+
const [selectedFillId, setSelectedFillId] = useState(SOLID_FILL_ID);
|
|
213
|
+
const [selectedStopId, setSelectedStopId] = useState<string | undefined>();
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
setDraft(value);
|
|
217
|
+
}, [value]);
|
|
218
|
+
|
|
219
|
+
const backgroundLayers = splitCssLayers(backgroundImage || "");
|
|
220
|
+
const backgroundSizeLayers = splitCssLayers(backgroundSize || "");
|
|
221
|
+
const backgroundRepeatLayers = splitCssLayers(backgroundRepeat || "");
|
|
222
|
+
const backgroundPositionLayers = splitCssLayers(backgroundPosition || "");
|
|
223
|
+
const selectedLayerIndex = fillLayerIndex(selectedFillId);
|
|
224
|
+
const selectedGradient =
|
|
225
|
+
selectedLayerIndex !== null
|
|
226
|
+
? parseGradientLayer(backgroundLayers[selectedLayerIndex] || "")
|
|
227
|
+
: null;
|
|
228
|
+
const fallbackGradientIndex = backgroundLayers.findIndex((layer) =>
|
|
229
|
+
Boolean(parseGradientLayer(layer)),
|
|
230
|
+
);
|
|
231
|
+
const activeGradientIndex =
|
|
232
|
+
selectedGradient && selectedLayerIndex !== null
|
|
233
|
+
? selectedLayerIndex
|
|
234
|
+
: fallbackGradientIndex >= 0
|
|
235
|
+
? fallbackGradientIndex
|
|
236
|
+
: null;
|
|
237
|
+
const activeGradient =
|
|
238
|
+
activeGradientIndex !== null
|
|
239
|
+
? parseGradientLayer(backgroundLayers[activeGradientIndex] || "")
|
|
240
|
+
: null;
|
|
241
|
+
const activeStopIds =
|
|
242
|
+
activeGradient?.stops.map((stop) => stop.id).join("|") ?? "";
|
|
243
|
+
|
|
244
|
+
useEffect(() => {
|
|
245
|
+
if (
|
|
246
|
+
selectedFillId !== SOLID_FILL_ID &&
|
|
247
|
+
(selectedLayerIndex === null ||
|
|
248
|
+
selectedLayerIndex >= backgroundLayers.length)
|
|
249
|
+
) {
|
|
250
|
+
setSelectedFillId(SOLID_FILL_ID);
|
|
251
|
+
}
|
|
252
|
+
}, [backgroundLayers.length, selectedFillId, selectedLayerIndex]);
|
|
253
|
+
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
if (!activeStopIds) {
|
|
256
|
+
if (selectedStopId) setSelectedStopId(undefined);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const stopIds = activeStopIds.split("|").filter(Boolean);
|
|
260
|
+
if (!selectedStopId || !stopIds.includes(selectedStopId)) {
|
|
261
|
+
setSelectedStopId(stopIds[0]);
|
|
262
|
+
}
|
|
263
|
+
}, [activeStopIds, selectedStopId]);
|
|
264
|
+
|
|
265
|
+
// PF12: `phase` defaults to "commit" so every discrete/one-shot caller
|
|
266
|
+
// (swatch clicks, paint-type switches, hex commit, fill-row edits) keeps
|
|
267
|
+
// committing immediately as before. Only the raw per-tick `onChange` wired
|
|
268
|
+
// to DesignColorPicker below passes "preview" explicitly — the picker's own
|
|
269
|
+
// `onChangeComplete` re-invokes setNext with the same final value tagged
|
|
270
|
+
// "commit" once the gesture ends (see the DesignColorPicker render below).
|
|
271
|
+
const setNext = (next: string, phase: "preview" | "commit" = "commit") => {
|
|
272
|
+
// Guard rail for callers that don't wire onBackgroundImageChange (i.e.
|
|
273
|
+
// supportsLayeredFills is false, e.g. the text-fill "color" row): the
|
|
274
|
+
// picker manages gradient/image paint-type selection as *local* UI state
|
|
275
|
+
// independent of props (see DesignColorPicker's localPaintType), so a
|
|
276
|
+
// user can still open the Gradient/Image tab there even when this
|
|
277
|
+
// ColorInput never offered layered fills. When that happens,
|
|
278
|
+
// emitPaintValue falls back to this onChange with a full gradient/url()
|
|
279
|
+
// CSS string, which is invalid for a plain color property (color /
|
|
280
|
+
// backgroundColor) and gets silently dropped by the browser — but not
|
|
281
|
+
// before clobbering the last-known-good value in this component's own
|
|
282
|
+
// state. Reject anything that doesn't parse as a plain solid color in
|
|
283
|
+
// that case instead of forwarding it.
|
|
284
|
+
if (!supportsLayeredFills && !parseCssColor(next)) return;
|
|
285
|
+
setDraft(next);
|
|
286
|
+
onChange(next, { phase });
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const replaceBackgroundLayer = (index: number, nextLayer: string) => {
|
|
290
|
+
if (!onBackgroundImageChange) return;
|
|
291
|
+
const nextLayers = [...backgroundLayers];
|
|
292
|
+
nextLayers[index] = nextLayer;
|
|
293
|
+
onBackgroundImageChange(joinCssLayers(nextLayers));
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const removeBackgroundLayer = (index: number) => {
|
|
297
|
+
if (!onBackgroundImageChange) return;
|
|
298
|
+
const nextLayers = backgroundLayers.filter(
|
|
299
|
+
(_, layerIndex) => layerIndex !== index,
|
|
300
|
+
);
|
|
301
|
+
onBackgroundImageChange(joinCssLayers(nextLayers));
|
|
302
|
+
setSelectedFillId(SOLID_FILL_ID);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const handlePaintValueChange = (nextValue: string) => {
|
|
306
|
+
if (!supportsLayeredFills || !onBackgroundImageChange) {
|
|
307
|
+
setNext(nextValue);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const selectedLayer = fillLayerIndex(selectedFillId);
|
|
312
|
+
if (selectedLayer !== null) {
|
|
313
|
+
replaceBackgroundLayer(selectedLayer, nextValue);
|
|
314
|
+
const gradient = parseGradientLayer(nextValue);
|
|
315
|
+
if (gradient) setSelectedStopId(gradient.stops[0]?.id);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
onBackgroundImageChange(joinCssLayers([nextValue, ...backgroundLayers]));
|
|
320
|
+
setSelectedFillId(fillLayerId(0));
|
|
321
|
+
const gradient = parseGradientLayer(nextValue);
|
|
322
|
+
setSelectedStopId(gradient?.stops[0]?.id);
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const fillRows = supportsLayeredFills
|
|
326
|
+
? buildFillRows(
|
|
327
|
+
draft || value || "#000000",
|
|
328
|
+
backgroundLayers,
|
|
329
|
+
selectedFillId,
|
|
330
|
+
)
|
|
331
|
+
: undefined;
|
|
332
|
+
|
|
333
|
+
const handleFillChange = (id: string, patch: DesignFillRowPatch) => {
|
|
334
|
+
if (id === SOLID_FILL_ID) {
|
|
335
|
+
if (patch.value !== undefined) setNext(patch.value);
|
|
336
|
+
if (patch.opacity !== undefined) {
|
|
337
|
+
const parsed = parseCssColor(patch.value ?? draft);
|
|
338
|
+
if (parsed) setNext(rgbaToCss(withColorOpacity(parsed, patch.opacity)));
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const index = fillLayerIndex(id);
|
|
344
|
+
if (index === null || !onBackgroundImageChange) return;
|
|
345
|
+
const currentLayer = backgroundLayers[index] || "";
|
|
346
|
+
if (patch.value !== undefined) {
|
|
347
|
+
replaceBackgroundLayer(index, patch.value);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
if (patch.opacity === undefined) return;
|
|
351
|
+
const gradient = parseGradientLayer(currentLayer);
|
|
352
|
+
if (!gradient) return;
|
|
353
|
+
replaceBackgroundLayer(
|
|
354
|
+
index,
|
|
355
|
+
buildGradientLayer(
|
|
356
|
+
gradient.type,
|
|
357
|
+
gradient.stops.map((stop) => ({
|
|
358
|
+
...stop,
|
|
359
|
+
opacity: patch.opacity,
|
|
360
|
+
})),
|
|
361
|
+
gradient.prefix,
|
|
362
|
+
),
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const handleAddFill = onBackgroundImageChange
|
|
367
|
+
? () => {
|
|
368
|
+
const nextLayers = [
|
|
369
|
+
defaultGradientLayer("linear", draft || value || "#000000"),
|
|
370
|
+
...backgroundLayers,
|
|
371
|
+
];
|
|
372
|
+
onBackgroundImageChange(joinCssLayers(nextLayers));
|
|
373
|
+
setSelectedFillId(fillLayerId(0));
|
|
374
|
+
setSelectedStopId("stop-0");
|
|
375
|
+
}
|
|
376
|
+
: undefined;
|
|
377
|
+
|
|
378
|
+
const handleRemoveFill = onBackgroundImageChange
|
|
379
|
+
? (id: string) => {
|
|
380
|
+
const index = fillLayerIndex(id);
|
|
381
|
+
if (index === null) return;
|
|
382
|
+
removeBackgroundLayer(index);
|
|
383
|
+
}
|
|
384
|
+
: undefined;
|
|
385
|
+
|
|
386
|
+
const handleGradientTypeChange =
|
|
387
|
+
activeGradient && activeGradientIndex !== null
|
|
388
|
+
? (type: DesignGradientType) => {
|
|
389
|
+
replaceBackgroundLayer(
|
|
390
|
+
activeGradientIndex,
|
|
391
|
+
buildGradientLayer(type, activeGradient.stops),
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
: undefined;
|
|
395
|
+
|
|
396
|
+
const handleGradientStopChange =
|
|
397
|
+
activeGradient && activeGradientIndex !== null
|
|
398
|
+
? (id: string, patch: DesignGradientStopPatch) => {
|
|
399
|
+
const nextStops = activeGradient.stops.map((stop) =>
|
|
400
|
+
stop.id === id ? { ...stop, ...patch } : stop,
|
|
401
|
+
);
|
|
402
|
+
replaceBackgroundLayer(
|
|
403
|
+
activeGradientIndex,
|
|
404
|
+
buildGradientLayer(
|
|
405
|
+
activeGradient.type,
|
|
406
|
+
nextStops,
|
|
407
|
+
activeGradient.prefix,
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
: undefined;
|
|
412
|
+
|
|
413
|
+
const handleAddGradientStop = onBackgroundImageChange
|
|
414
|
+
? () => {
|
|
415
|
+
if (activeGradient && activeGradientIndex !== null) {
|
|
416
|
+
const nextStop: DesignGradientStop = {
|
|
417
|
+
id: `stop-${activeGradient.stops.length}`,
|
|
418
|
+
color: draft || "#000000",
|
|
419
|
+
position: 50,
|
|
420
|
+
opacity: 100,
|
|
421
|
+
};
|
|
422
|
+
replaceBackgroundLayer(
|
|
423
|
+
activeGradientIndex,
|
|
424
|
+
buildGradientLayer(
|
|
425
|
+
activeGradient.type,
|
|
426
|
+
[...activeGradient.stops, nextStop],
|
|
427
|
+
activeGradient.prefix,
|
|
428
|
+
),
|
|
429
|
+
);
|
|
430
|
+
setSelectedStopId(nextStop.id);
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
onBackgroundImageChange(
|
|
435
|
+
joinCssLayers([
|
|
436
|
+
defaultGradientLayer("linear", draft || value || "#000000"),
|
|
437
|
+
...backgroundLayers,
|
|
438
|
+
]),
|
|
439
|
+
);
|
|
440
|
+
setSelectedFillId(fillLayerId(0));
|
|
441
|
+
setSelectedStopId("stop-0");
|
|
442
|
+
}
|
|
443
|
+
: undefined;
|
|
444
|
+
|
|
445
|
+
const handleRemoveGradientStop =
|
|
446
|
+
activeGradient && activeGradientIndex !== null
|
|
447
|
+
? (id: string) => {
|
|
448
|
+
if (activeGradient.stops.length <= 2) return;
|
|
449
|
+
const nextStops = activeGradient.stops.filter(
|
|
450
|
+
(stop) => stop.id !== id,
|
|
451
|
+
);
|
|
452
|
+
replaceBackgroundLayer(
|
|
453
|
+
activeGradientIndex,
|
|
454
|
+
buildGradientLayer(
|
|
455
|
+
activeGradient.type,
|
|
456
|
+
nextStops,
|
|
457
|
+
activeGradient.prefix,
|
|
458
|
+
),
|
|
459
|
+
);
|
|
460
|
+
setSelectedStopId(nextStops[0]?.id);
|
|
461
|
+
}
|
|
462
|
+
: undefined;
|
|
463
|
+
|
|
464
|
+
const selectedPaintType: DesignPaintType =
|
|
465
|
+
selectedFillId !== SOLID_FILL_ID
|
|
466
|
+
? selectedGradient
|
|
467
|
+
? selectedGradient.type
|
|
468
|
+
: "image"
|
|
469
|
+
: colorHasVisibleAlpha(draft || value)
|
|
470
|
+
? "solid"
|
|
471
|
+
: "none";
|
|
472
|
+
const pickerValue =
|
|
473
|
+
selectedLayerIndex !== null
|
|
474
|
+
? (backgroundLayers[selectedLayerIndex] ?? draft ?? value ?? "#000000")
|
|
475
|
+
: draft || "#000000";
|
|
476
|
+
const selectedBackgroundLayerValue = (layers: string[]): string | undefined =>
|
|
477
|
+
selectedLayerIndex !== null ? layers[selectedLayerIndex] : undefined;
|
|
478
|
+
const handlePaintTypeChange = (type: DesignPaintType) => {
|
|
479
|
+
const selectedLayer = fillLayerIndex(selectedFillId);
|
|
480
|
+
if (type === "solid") {
|
|
481
|
+
if (selectedLayer !== null) removeBackgroundLayer(selectedLayer);
|
|
482
|
+
setSelectedFillId(SOLID_FILL_ID);
|
|
483
|
+
setNext(cssColorOrFallback(draft || value, "#000000"));
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (type === "none") {
|
|
487
|
+
if (selectedLayer !== null) {
|
|
488
|
+
removeBackgroundLayer(selectedLayer);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
setNext("transparent");
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (!onBackgroundImageChange) return;
|
|
495
|
+
|
|
496
|
+
if (
|
|
497
|
+
type !== "linear" &&
|
|
498
|
+
type !== "radial" &&
|
|
499
|
+
type !== "angular" &&
|
|
500
|
+
type !== "diamond"
|
|
501
|
+
) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const nextType: DesignGradientType = type;
|
|
505
|
+
const layerIndex = selectedLayer ?? activeGradientIndex;
|
|
506
|
+
if (layerIndex !== null) {
|
|
507
|
+
const currentGradient = parseGradientLayer(
|
|
508
|
+
backgroundLayers[layerIndex] || "",
|
|
509
|
+
);
|
|
510
|
+
const stops =
|
|
511
|
+
currentGradient?.stops ?? defaultGradientStops(draft || value);
|
|
512
|
+
replaceBackgroundLayer(layerIndex, buildGradientLayer(nextType, stops));
|
|
513
|
+
setSelectedFillId(fillLayerId(layerIndex));
|
|
514
|
+
setSelectedStopId(stops[0]?.id);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
onBackgroundImageChange(
|
|
519
|
+
joinCssLayers([
|
|
520
|
+
defaultGradientLayer(nextType, draft || value || "#000000"),
|
|
521
|
+
...backgroundLayers,
|
|
522
|
+
]),
|
|
523
|
+
);
|
|
524
|
+
setSelectedFillId(fillLayerId(0));
|
|
525
|
+
setSelectedStopId("stop-0");
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
if (isMixedValue(value)) {
|
|
529
|
+
return (
|
|
530
|
+
<button
|
|
531
|
+
type="button"
|
|
532
|
+
className="flex h-6 w-full items-center rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] text-muted-foreground"
|
|
533
|
+
onClick={() => onChange("#000000")}
|
|
534
|
+
>
|
|
535
|
+
{MIXED_VALUE}
|
|
536
|
+
</button>
|
|
537
|
+
);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<DesignColorPicker
|
|
542
|
+
key={pickerKey}
|
|
543
|
+
label={label}
|
|
544
|
+
value={pickerValue}
|
|
545
|
+
// PF12: `onChange` fires on every SV/hue/alpha drag tick — tag those as
|
|
546
|
+
// "preview" so the caller can skip the expensive source commit and only
|
|
547
|
+
// update the live iframe preview. `onChangeComplete` fires exactly once
|
|
548
|
+
// per gesture (drag-end, hex commit, keyboard nudge, swatch click,
|
|
549
|
+
// paint-type switch) with the same final value, tagged "commit" so the
|
|
550
|
+
// authoritative source write always happens exactly once.
|
|
551
|
+
onChange={(v) => setNext(v, "preview")}
|
|
552
|
+
onChangeComplete={(v) => setNext(v, "commit")}
|
|
553
|
+
onPaintValueChange={
|
|
554
|
+
supportsLayeredFills ? handlePaintValueChange : undefined
|
|
555
|
+
}
|
|
556
|
+
onImageFillChange={onImageFillChange}
|
|
557
|
+
backgroundImage={selectedBackgroundLayerValue(backgroundLayers)}
|
|
558
|
+
backgroundSize={selectedBackgroundLayerValue(backgroundSizeLayers)}
|
|
559
|
+
backgroundRepeat={selectedBackgroundLayerValue(backgroundRepeatLayers)}
|
|
560
|
+
backgroundPosition={selectedBackgroundLayerValue(
|
|
561
|
+
backgroundPositionLayers,
|
|
562
|
+
)}
|
|
563
|
+
blendMode={blendMode}
|
|
564
|
+
onBlendModeChange={onBlendModeChange}
|
|
565
|
+
showBlendMode={Boolean(onBlendModeChange)}
|
|
566
|
+
fillRows={fillRows}
|
|
567
|
+
selectedFillId={selectedFillId}
|
|
568
|
+
onFillSelect={supportsLayeredFills ? setSelectedFillId : undefined}
|
|
569
|
+
onFillChange={supportsLayeredFills ? handleFillChange : undefined}
|
|
570
|
+
onAddFill={supportsLayeredFills ? handleAddFill : undefined}
|
|
571
|
+
onRemoveFill={supportsLayeredFills ? handleRemoveFill : undefined}
|
|
572
|
+
paintType={selectedPaintType}
|
|
573
|
+
onPaintTypeChange={handlePaintTypeChange}
|
|
574
|
+
gradientType={activeGradient?.type}
|
|
575
|
+
onGradientTypeChange={handleGradientTypeChange}
|
|
576
|
+
gradientStops={activeGradient?.stops}
|
|
577
|
+
selectedStopId={selectedStopId}
|
|
578
|
+
onGradientStopSelect={setSelectedStopId}
|
|
579
|
+
onGradientStopChange={handleGradientStopChange}
|
|
580
|
+
onAddGradientStop={
|
|
581
|
+
supportsLayeredFills ? handleAddGradientStop : undefined
|
|
582
|
+
}
|
|
583
|
+
onRemoveGradientStop={handleRemoveGradientStop}
|
|
584
|
+
documentColors={documentColors}
|
|
585
|
+
supportedPaintTypes={supportedPaintTypes}
|
|
586
|
+
glslShaderContext={glslShaderContext}
|
|
587
|
+
/>
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Select dropdown */
|
|
592
|
+
export function PropSelect({
|
|
593
|
+
label,
|
|
594
|
+
value,
|
|
595
|
+
onChange,
|
|
596
|
+
options,
|
|
597
|
+
}: {
|
|
598
|
+
label: string;
|
|
599
|
+
value: string;
|
|
600
|
+
onChange: (value: string) => void;
|
|
601
|
+
options: { value: string; label: string }[];
|
|
602
|
+
}) {
|
|
603
|
+
return (
|
|
604
|
+
<div className="flex items-center gap-1.5">
|
|
605
|
+
<FieldLabel>{label}</FieldLabel>
|
|
606
|
+
<Select value={value} onValueChange={onChange}>
|
|
607
|
+
<SelectTrigger className="h-6 min-w-0 rounded-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 !text-[11px] shadow-none focus:ring-1 focus:ring-[var(--design-editor-accent-color)]">
|
|
608
|
+
<SelectValue />
|
|
609
|
+
</SelectTrigger>
|
|
610
|
+
<SelectContent>
|
|
611
|
+
{options.map((opt) => (
|
|
612
|
+
<SelectItem
|
|
613
|
+
key={opt.value}
|
|
614
|
+
value={opt.value}
|
|
615
|
+
className="!text-[11px]"
|
|
616
|
+
>
|
|
617
|
+
{opt.label}
|
|
618
|
+
</SelectItem>
|
|
619
|
+
))}
|
|
620
|
+
</SelectContent>
|
|
621
|
+
</Select>
|
|
622
|
+
</div>
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/** Slider with label and value display */
|
|
627
|
+
export function PropSlider({
|
|
628
|
+
label,
|
|
629
|
+
value,
|
|
630
|
+
onChange,
|
|
631
|
+
min = 0,
|
|
632
|
+
max = 100,
|
|
633
|
+
step = 1,
|
|
634
|
+
unit = "",
|
|
635
|
+
}: {
|
|
636
|
+
label: string;
|
|
637
|
+
value: number;
|
|
638
|
+
onChange: (value: number) => void;
|
|
639
|
+
min?: number;
|
|
640
|
+
max?: number;
|
|
641
|
+
step?: number;
|
|
642
|
+
unit?: string;
|
|
643
|
+
}) {
|
|
644
|
+
return (
|
|
645
|
+
<div className="flex items-center gap-1.5">
|
|
646
|
+
<FieldLabel>{label}</FieldLabel>
|
|
647
|
+
<Slider
|
|
648
|
+
value={[value]}
|
|
649
|
+
onValueChange={([v]) => onChange(v)}
|
|
650
|
+
min={min}
|
|
651
|
+
max={max}
|
|
652
|
+
step={step}
|
|
653
|
+
className="flex-1"
|
|
654
|
+
/>
|
|
655
|
+
<span className="w-12 text-right !text-[11px] tabular-nums text-muted-foreground">
|
|
656
|
+
{value}
|
|
657
|
+
{unit}
|
|
658
|
+
</span>
|
|
659
|
+
</div>
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* design-editor inspector section. Matches the design editor "Design" panel chrome:
|
|
665
|
+
* - NO left collapse chevron (the design editor uses none).
|
|
666
|
+
* - A thin divider line above each section.
|
|
667
|
+
* - A bold left-aligned title.
|
|
668
|
+
* - Right-aligned action icons (add layer, toggles, styles, etc.).
|
|
669
|
+
*
|
|
670
|
+
* The title is still clickable to collapse the body (design sections collapse
|
|
671
|
+
* on title click) but renders no chevron glyph, just the same way.
|
|
672
|
+
*/
|
|
673
|
+
export function PanelSection({
|
|
674
|
+
title,
|
|
675
|
+
actions,
|
|
676
|
+
children,
|
|
677
|
+
defaultCollapsed = false,
|
|
678
|
+
}: {
|
|
679
|
+
title: string;
|
|
680
|
+
actions?: ReactNode;
|
|
681
|
+
children?: ReactNode;
|
|
682
|
+
defaultCollapsed?: boolean;
|
|
683
|
+
}) {
|
|
684
|
+
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
685
|
+
|
|
686
|
+
return (
|
|
687
|
+
<section className="shrink-0 border-t border-[var(--design-editor-control-border)] first:border-t-0">
|
|
688
|
+
<div className="flex min-h-9 items-center gap-2 px-3">
|
|
689
|
+
<button
|
|
690
|
+
type="button"
|
|
691
|
+
className="flex min-w-0 flex-1 cursor-pointer items-center bg-transparent text-left"
|
|
692
|
+
onClick={() => setCollapsed((c) => !c)}
|
|
693
|
+
aria-expanded={!collapsed}
|
|
694
|
+
>
|
|
695
|
+
<h3 className="min-w-0 flex-1 truncate !text-[11px] font-semibold text-foreground">
|
|
696
|
+
{title}
|
|
697
|
+
</h3>
|
|
698
|
+
</button>
|
|
699
|
+
{actions ? (
|
|
700
|
+
<div className="flex shrink-0 items-center gap-0.5">{actions}</div>
|
|
701
|
+
) : null}
|
|
702
|
+
</div>
|
|
703
|
+
{!collapsed && children ? (
|
|
704
|
+
<div className="space-y-1.5 px-3 pb-3 pt-0.5 !text-[11px]">
|
|
705
|
+
{children}
|
|
706
|
+
</div>
|
|
707
|
+
) : null}
|
|
708
|
+
</section>
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export function FieldLabel({ children }: { children: ReactNode }) {
|
|
713
|
+
return (
|
|
714
|
+
<Label className="w-[64px] shrink-0 !text-[11px] font-medium text-muted-foreground">
|
|
715
|
+
{children}
|
|
716
|
+
</Label>
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
export function SubsectionLabel({ children }: { children: ReactNode }) {
|
|
721
|
+
return (
|
|
722
|
+
<p className="!text-[11px] font-medium text-muted-foreground">{children}</p>
|
|
723
|
+
);
|
|
724
|
+
}
|