@agent-native/core 0.80.8 → 0.80.9
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 +2 -2
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +6 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +10 -2
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +10 -2
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +7 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +9 -2
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +6 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +6 -0
- package/corpus/core/docs/content/template-design.mdx +9 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/mcp.ts +10 -0
- package/corpus/core/src/cli/skills.ts +58 -12
- package/corpus/core/src/client/error-format.ts +25 -0
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +13 -11
- package/corpus/core/src/client/guided-questions.tsx +32 -4
- package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +33 -1
- package/corpus/core/src/client/resources/BuiltinCapabilityDetail.tsx +8 -0
- package/corpus/core/src/client/resources/use-builtin-capabilities.ts +4 -2
- package/corpus/core/src/client/session-replay.ts +34 -0
- package/corpus/core/src/client/sharing/ShareButton.tsx +106 -21
- package/corpus/core/src/coding-tools/index.ts +1 -1
- package/corpus/core/src/mcp/index.ts +2 -0
- package/corpus/core/src/mcp/screen-memory-stdio.ts +290 -0
- package/corpus/core/src/mcp-client/builtin-capabilities.ts +13 -1
- package/corpus/core/src/mcp-client/index.ts +9 -0
- package/corpus/core/src/mcp-client/screen-memory-local.ts +461 -0
- package/corpus/core/src/onboarding/types.ts +7 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +25 -0
- package/corpus/core/src/server/agents-bundle.ts +35 -7
- package/corpus/core/src/styles/agent-native.css +5 -0
- package/corpus/core/src/templates/default/app/global.css +35 -35
- package/corpus/templates/analytics/AGENTS.md +6 -0
- package/corpus/templates/analytics/actions/github-repo-files.ts +9 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +4 -4
- package/corpus/templates/analytics/app/global.css +144 -40
- package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +1 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +4 -4
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -9
- package/corpus/templates/analytics/changelog/2026-06-29-agents-can-search-and-read-connected-github-repositories-whe.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-sessions-and-explorer-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
- package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +1 -1
- package/corpus/templates/assets/app/global.css +99 -35
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -2
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +4 -4
- package/corpus/templates/assets/app/routes/library.tsx +4 -4
- package/corpus/templates/assets/changelog/2026-06-29-asset-library-and-detail-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/brain/app/global.css +63 -35
- package/corpus/templates/brain/app/routes/search.tsx +1 -1
- package/corpus/templates/brain/app/routes/settings.tsx +1 -1
- package/corpus/templates/brain/app/routes/sources.tsx +2 -2
- package/corpus/templates/brain/changelog/2026-06-29-search-settings-and-source-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +4 -4
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +3 -2
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +4 -2
- package/corpus/templates/calendar/app/global.css +69 -35
- package/corpus/templates/calendar/changelog/2026-06-29-event-and-find-time-layouts-adapt-when-the-agent-sidebar-is.md +6 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/chat/app/global.css +35 -35
- package/corpus/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/AGENTS.md +13 -0
- package/corpus/templates/clips/actions/get-screen-memory-status.ts +12 -0
- package/corpus/templates/clips/actions/query-screen-memory-context.ts +29 -0
- package/corpus/templates/clips/actions/view-screen.ts +2 -2
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/global.css +47 -35
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +3 -3
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +45 -18
- package/corpus/templates/clips/changelog/2026-06-29-meeting-detail-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-29-screen-memory-keeps-a-local-rolling-screen-buffer-for-agent-context.md +5 -0
- package/corpus/templates/clips/changelog/2026-06-29-video-chat-is-easier-to-find-on-recording-pages-and-s3-setup.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/styles.css +39 -39
- package/corpus/templates/clips/desktop/src/app.tsx +290 -1
- package/corpus/templates/clips/desktop/src/shared/config.ts +83 -0
- package/corpus/templates/clips/desktop/src/styles.css +67 -52
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +50 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +17 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +13 -13
- package/corpus/templates/clips/desktop/src-tauri/src/screen_memory.rs +990 -0
- package/corpus/templates/clips/server/plugins/onboarding.ts +2 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +3 -3
- package/corpus/templates/content/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/content/app/global.css +29 -15
- package/corpus/templates/content/changelog/2026-06-29-database-gallery-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +25 -20
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +8 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +13 -0
- package/corpus/templates/design/AGENTS.md +20 -12
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1054 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +24 -4
- package/corpus/templates/design/actions/export-coding-handoff.ts +12 -4
- package/corpus/templates/design/actions/generate-design.ts +6 -21
- package/corpus/templates/design/actions/generate-screens.ts +43 -2
- package/corpus/templates/design/actions/get-design.ts +1 -0
- package/corpus/templates/design/actions/present-design-variants.ts +355 -30
- package/corpus/templates/design/actions/view-screen.ts +75 -31
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +329 -69
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +479 -77
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +380 -216
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1050 -196
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +83 -206
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +1 -1
- package/corpus/templates/design/app/components/design/index.ts +0 -1
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +225 -133
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +122 -11
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +24 -5
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -1
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +37 -12
- package/corpus/templates/design/app/components/design/inspector/index.ts +1 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +12 -5
- package/corpus/templates/design/app/components/design/types.ts +9 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +23 -5
- package/corpus/templates/design/app/global.css +77 -49
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +8 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +45 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2602 -657
- package/corpus/templates/design/app/root.tsx +1 -1
- package/corpus/templates/design/changelog/2026-06-29-containers-no-longer-block-selecting-nested-elements-and-nor.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-agents-now-generate-and-refine-variants-from-stronger.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-questions-now-appear-as-a-cleaner-canvas-intake-with-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-variant-directions-now-appear-as-board-screens-with-chat-buttons.md +5 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-no-longer-snaps-back-to-the-previous-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-frame-resize-measurements-now-stay-beside-the-cursor-while-d.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-inspector-number-fields-have-cleaner-tooltips-and-draggable-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-locking-layer-nesting-generated-question-intake-and-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-names-in-the-sidebar-can-scroll-horizontally-instead-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layer-nesting-in-the-sidebar-now-uses-tighter-spacing-so-nam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-layers-no-longer-repeat-document-wrapper-rows-when-a-screen-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-overview-previews-now-keep-element-hover-and-selection-tied-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-pressing-escape-now-cancels-a-stuck-screen-drag-in-the-overv.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-only-highlights-screens-when-the-frame-or-ti.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-previews-now-resize-to-match-the-selected-de.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-now-uses-a-pointer-cursor-instead-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-selection-outlines-now-stay-locked-to-the-se.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zoom-now-reports-real-screen-scale-separatel.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-feels-smoother-and-selection-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-zooming-now-stays-on-the-overview-canvas-ins.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-selecting-a-nested-layer-from-all-screens-now-keeps-you-in-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-share-now-groups-link-sharing-exports-and-agent-handoff.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-design-editor-loading-skeleton-now-uses-a-softer-charcoa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-device-preview-menu-now-sits-next-to-the-preview-button-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-inspector-stays-read-only-while-an-empty-design-is-still.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-missing-design-view-now-matches-the-rest-of-the-editor-c.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-icon-and-tool-option-menus-better-match-the-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-typography-controls-now-keep-the-font-name-visible-and-move-.md +6 -0
- package/corpus/templates/design/e2e/README.md +3 -3
- package/corpus/templates/design/e2e/global-setup.ts +11 -22
- package/corpus/templates/design/e2e/helpers.ts +122 -11
- package/corpus/templates/design/playwright.config.ts +9 -3
- package/corpus/templates/design/server/lib/coding-handoff.ts +126 -2
- package/corpus/templates/design/server/plugins/core-routes.ts +1 -2
- package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
- package/corpus/templates/design/shared/code-layer.ts +125 -22
- package/corpus/templates/design/shared/generation-session.ts +41 -0
- package/corpus/templates/dispatch/app/global.css +79 -35
- package/corpus/templates/dispatch/app/routes/integrations.tsx +8 -7
- package/corpus/templates/dispatch/changelog/2026-06-29-integration-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/forms/app/components/layout/Layout.tsx +6 -2
- package/corpus/templates/forms/app/global.css +64 -35
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +2 -2
- package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +3 -3
- package/corpus/templates/forms/changelog/2026-06-29-form-list-and-insight-layouts-adapt-when-the-agent-sidebar.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +3 -1
- package/corpus/templates/macros/app/global.css +25 -9
- package/corpus/templates/macros/changelog/2026-06-29-macro-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +6 -2
- package/corpus/templates/mail/app/global.css +42 -36
- package/corpus/templates/mail/app/pages/InboxPage.tsx +1 -1
- package/corpus/templates/mail/changelog/2026-06-29-the-contact-panel-now-adapts-to-the-available-mail-pane-width.md +6 -0
- package/corpus/templates/plan/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +6 -7
- package/corpus/templates/plan/app/global.css +74 -42
- package/corpus/templates/plan/changelog/2026-06-29-plan-document-blocks-adapt-to-the-available-document-width.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-29-plan-titles-and-summaries-can-be-edited-inline-without-focus.md +6 -0
- package/corpus/templates/slides/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/slides/app/global.css +49 -33
- package/corpus/templates/slides/changelog/2026-06-29-slide-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/corpus/templates/videos/app/components/layout/Layout.tsx +3 -1
- package/corpus/templates/videos/app/global.css +35 -35
- package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +6 -0
- package/dist/cli/mcp.d.ts.map +1 -1
- package/dist/cli/mcp.js +10 -0
- package/dist/cli/mcp.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +58 -12
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +17 -0
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +3 -4
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/guided-questions.d.ts +4 -0
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +16 -7
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +26 -1
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.d.ts.map +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.js +1 -1
- package/dist/client/resources/BuiltinCapabilityDetail.js.map +1 -1
- package/dist/client/resources/use-builtin-capabilities.d.ts +1 -1
- package/dist/client/resources/use-builtin-capabilities.d.ts.map +1 -1
- package/dist/client/resources/use-builtin-capabilities.js +2 -1
- package/dist/client/resources/use-builtin-capabilities.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +31 -0
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +14 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +36 -7
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/coding-tools/index.js +1 -1
- package/dist/coding-tools/index.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts +6 -0
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -0
- package/dist/mcp/screen-memory-stdio.js +213 -0
- package/dist/mcp/screen-memory-stdio.js.map +1 -0
- package/dist/mcp-client/builtin-capabilities.d.ts +1 -1
- package/dist/mcp-client/builtin-capabilities.d.ts.map +1 -1
- package/dist/mcp-client/builtin-capabilities.js +10 -0
- package/dist/mcp-client/builtin-capabilities.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -0
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -0
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/screen-memory-local.d.ts +61 -0
- package/dist/mcp-client/screen-memory-local.d.ts.map +1 -0
- package/dist/mcp-client/screen-memory-local.js +336 -0
- package/dist/mcp-client/screen-memory-local.js.map +1 -0
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- package/dist/onboarding/types.d.ts +7 -0
- package/dist/onboarding/types.d.ts.map +1 -1
- package/dist/onboarding/types.js.map +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +26 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agents-bundle.d.ts.map +1 -1
- package/dist/server/agents-bundle.js +22 -7
- package/dist/server/agents-bundle.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +5 -0
- package/dist/templates/default/app/global.css +35 -35
- package/docs/content/locales/ar-SA/template-design.mdx +6 -0
- package/docs/content/locales/de-DE/template-design.mdx +8 -0
- package/docs/content/locales/es-ES/template-design.mdx +10 -2
- package/docs/content/locales/fr-FR/template-design.mdx +10 -2
- package/docs/content/locales/hi-IN/template-design.mdx +7 -0
- package/docs/content/locales/ja-JP/template-design.mdx +7 -0
- package/docs/content/locales/ko-KR/template-design.mdx +7 -0
- package/docs/content/locales/pt-BR/template-design.mdx +9 -2
- package/docs/content/locales/zh-CN/template-design.mdx +6 -0
- package/docs/content/locales/zh-TW/template-design.mdx +6 -0
- package/docs/content/template-design.mdx +9 -2
- package/package.json +1 -1
- package/src/templates/default/app/global.css +35 -35
- package/corpus/templates/design/app/components/design/VariantGrid.tsx +0 -189
- package/corpus/templates/design/app/components/design/VariantHandoffCard.tsx +0 -115
- package/corpus/templates/design/app/hooks/use-variant-flow.ts +0 -342
|
@@ -151,6 +151,12 @@ export interface AutoLayoutMatrixProps {
|
|
|
151
151
|
axis: AutoLayoutSizingAxis,
|
|
152
152
|
sizing: AutoLayoutSizing,
|
|
153
153
|
) => void;
|
|
154
|
+
/**
|
|
155
|
+
* Invoked when the user directly edits the resolved size (scrub or type) in
|
|
156
|
+
* fixed-sizing mode. `value` is the new size in CSS pixels. Optional —
|
|
157
|
+
* when omitted the resolved-size display is read-only (mode-picker only).
|
|
158
|
+
*/
|
|
159
|
+
onChildSizeChange?: (axis: AutoLayoutSizingAxis, value: number) => void;
|
|
154
160
|
/**
|
|
155
161
|
* Set or clear a min/max constraint on an axis. `value === null` clears it.
|
|
156
162
|
* Optional — when omitted the "Add min/max…" rows and constraint sub-rows are
|
|
@@ -244,6 +250,7 @@ export function AutoLayoutMatrix({
|
|
|
244
250
|
onChildMinMaxChange,
|
|
245
251
|
onApplyVariable,
|
|
246
252
|
onDisplayChange,
|
|
253
|
+
onChildSizeChange,
|
|
247
254
|
availableChildSizing,
|
|
248
255
|
labels,
|
|
249
256
|
disabled = false,
|
|
@@ -371,6 +378,11 @@ export function AutoLayoutMatrix({
|
|
|
371
378
|
labels={copy}
|
|
372
379
|
disabled={disabled}
|
|
373
380
|
onChange={(next) => onChildSizingChange("horizontal", next)}
|
|
381
|
+
onSizeChange={
|
|
382
|
+
onChildSizeChange
|
|
383
|
+
? (px) => onChildSizeChange("horizontal", px)
|
|
384
|
+
: undefined
|
|
385
|
+
}
|
|
374
386
|
onMinMaxChange={onChildMinMaxChange}
|
|
375
387
|
onApplyVariable={onApplyVariable}
|
|
376
388
|
/>
|
|
@@ -387,6 +399,11 @@ export function AutoLayoutMatrix({
|
|
|
387
399
|
labels={copy}
|
|
388
400
|
disabled={disabled}
|
|
389
401
|
onChange={(next) => onChildSizingChange("vertical", next)}
|
|
402
|
+
onSizeChange={
|
|
403
|
+
onChildSizeChange
|
|
404
|
+
? (px) => onChildSizeChange("vertical", px)
|
|
405
|
+
: undefined
|
|
406
|
+
}
|
|
390
407
|
onMinMaxChange={onChildMinMaxChange}
|
|
391
408
|
onApplyVariable={onApplyVariable}
|
|
392
409
|
/>
|
|
@@ -417,36 +434,35 @@ export function AutoLayoutMatrix({
|
|
|
417
434
|
</div>
|
|
418
435
|
</div>
|
|
419
436
|
|
|
420
|
-
{
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
</div>
|
|
437
|
+
{!isBlock ? (
|
|
438
|
+
<div className="grid grid-cols-[78px_1fr] items-start gap-3">
|
|
439
|
+
<div className="space-y-1.5">
|
|
440
|
+
<ControlLabel>
|
|
441
|
+
{"Alignment" /* i18n-ignore design inspector label */}
|
|
442
|
+
</ControlLabel>
|
|
443
|
+
<CompactAlignmentMatrix
|
|
444
|
+
value={value.alignment}
|
|
445
|
+
onChange={onAlignmentChange}
|
|
446
|
+
direction={value.direction}
|
|
447
|
+
disabled={disabled}
|
|
448
|
+
onDistribute={onDistribute}
|
|
449
|
+
/>
|
|
450
|
+
</div>
|
|
435
451
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
452
|
+
<div className="space-y-1.5">
|
|
453
|
+
<ControlLabel>{copy.gap}</ControlLabel>
|
|
454
|
+
<GapField
|
|
455
|
+
value={value.gap}
|
|
456
|
+
onGapChange={onGapChange}
|
|
457
|
+
onDistribute={onDistribute}
|
|
458
|
+
label={copy.gap}
|
|
459
|
+
disabled={disabled}
|
|
460
|
+
direction={value.direction}
|
|
461
|
+
gapMode={value.spaceBetween ? "auto" : "fixed"}
|
|
462
|
+
/>
|
|
463
|
+
</div>
|
|
448
464
|
</div>
|
|
449
|
-
|
|
465
|
+
) : null}
|
|
450
466
|
|
|
451
467
|
{/* ── Padding ── */}
|
|
452
468
|
<div className="space-y-1.5">
|
|
@@ -860,16 +876,15 @@ function GapField({
|
|
|
860
876
|
{/* [gap-icon] value [▾] in one control surface */}
|
|
861
877
|
<div
|
|
862
878
|
className={cn(
|
|
863
|
-
"flex h-7 min-w-0 flex-1 items-center rounded-md bg-[var(--design-editor-control-bg)]
|
|
879
|
+
"flex h-7 min-w-0 flex-1 items-center rounded-md bg-[var(--design-editor-control-bg)]",
|
|
864
880
|
disabled && "opacity-40",
|
|
865
881
|
)}
|
|
866
882
|
>
|
|
867
|
-
<span className="flex shrink-0 items-center text-muted-foreground">
|
|
868
|
-
<IconGap className="size-3.5" />
|
|
869
|
-
</span>
|
|
870
883
|
<ScrubInput
|
|
871
884
|
label={label}
|
|
872
885
|
ariaLabel={label}
|
|
886
|
+
tooltipLabel={label}
|
|
887
|
+
icon={IconGap}
|
|
873
888
|
value={value}
|
|
874
889
|
onChange={(next) => onGapChange(next)}
|
|
875
890
|
unit="px"
|
|
@@ -878,7 +893,7 @@ function GapField({
|
|
|
878
893
|
precision={1}
|
|
879
894
|
disabled={disabled}
|
|
880
895
|
className="min-w-0 flex-1 gap-0"
|
|
881
|
-
labelClassName="hidden"
|
|
896
|
+
labelClassName="h-7 w-6 justify-center gap-0 rounded-l-md text-muted-foreground [&>span]:hidden"
|
|
882
897
|
inputClassName="h-6 border-0 bg-transparent px-1 text-[11px] shadow-none focus-visible:ring-0"
|
|
883
898
|
/>
|
|
884
899
|
{onDistribute != null ? (
|
|
@@ -972,16 +987,15 @@ function PaddingField({
|
|
|
972
987
|
return (
|
|
973
988
|
<div
|
|
974
989
|
className={cn(
|
|
975
|
-
"flex h-7 min-w-0 items-center rounded-md bg-[var(--design-editor-control-bg)]
|
|
990
|
+
"flex h-7 min-w-0 items-center rounded-md bg-[var(--design-editor-control-bg)]",
|
|
976
991
|
disabled && "opacity-40",
|
|
977
992
|
)}
|
|
978
993
|
>
|
|
979
|
-
<span className="flex shrink-0 items-center text-muted-foreground">
|
|
980
|
-
<Icon className="size-3.5" />
|
|
981
|
-
</span>
|
|
982
994
|
<ScrubInput
|
|
983
995
|
label={ariaLabel}
|
|
984
996
|
ariaLabel={ariaLabel}
|
|
997
|
+
tooltipLabel={ariaLabel}
|
|
998
|
+
icon={Icon}
|
|
985
999
|
value={value}
|
|
986
1000
|
onChange={(next) => onChange(next)}
|
|
987
1001
|
unit="px"
|
|
@@ -990,7 +1004,7 @@ function PaddingField({
|
|
|
990
1004
|
precision={1}
|
|
991
1005
|
disabled={disabled}
|
|
992
1006
|
className="min-w-0 flex-1 gap-0"
|
|
993
|
-
labelClassName="hidden"
|
|
1007
|
+
labelClassName="h-7 w-6 justify-center gap-0 rounded-l-md text-muted-foreground [&>span]:hidden"
|
|
994
1008
|
inputClassName="h-6 border-0 bg-transparent px-1 text-[11px] shadow-none focus-visible:ring-0"
|
|
995
1009
|
/>
|
|
996
1010
|
</div>
|
|
@@ -1055,6 +1069,12 @@ export interface SizingFieldProps {
|
|
|
1055
1069
|
labels?: Partial<AutoLayoutMatrixLabels>;
|
|
1056
1070
|
disabled: boolean;
|
|
1057
1071
|
onChange: (value: AutoLayoutSizing) => void;
|
|
1072
|
+
/**
|
|
1073
|
+
* Invoked when the user directly scrubs or types a new pixel value while in
|
|
1074
|
+
* "fixed" sizing mode. When omitted the numeric display is read-only and the
|
|
1075
|
+
* entire trigger is a mode-picker dropdown (legacy behaviour).
|
|
1076
|
+
*/
|
|
1077
|
+
onSizeChange?: (px: number) => void;
|
|
1058
1078
|
onMinMaxChange?: (
|
|
1059
1079
|
axis: AutoLayoutSizingAxis,
|
|
1060
1080
|
kind: "min" | "max",
|
|
@@ -1085,6 +1105,7 @@ export function SizingField({
|
|
|
1085
1105
|
labels: labelOverrides,
|
|
1086
1106
|
disabled,
|
|
1087
1107
|
onChange,
|
|
1108
|
+
onSizeChange,
|
|
1088
1109
|
onMinMaxChange,
|
|
1089
1110
|
onApplyVariable,
|
|
1090
1111
|
}: SizingFieldProps) {
|
|
@@ -1108,6 +1129,9 @@ export function SizingField({
|
|
|
1108
1129
|
const minLabel = isWidth ? labels.minWidth : labels.minHeight;
|
|
1109
1130
|
const maxLabel = isWidth ? labels.maxWidth : labels.maxHeight;
|
|
1110
1131
|
|
|
1132
|
+
// Whether we're in editable fixed mode (ScrubInput shown for size).
|
|
1133
|
+
const isEditableFixed = value === "fixed" && onSizeChange != null;
|
|
1134
|
+
|
|
1111
1135
|
const openEditor = (kind: "min" | "max") => {
|
|
1112
1136
|
// Commit immediately so the shown row always reflects real state and
|
|
1113
1137
|
// persists across selection changes, remounts, and parent re-renders.
|
|
@@ -1116,105 +1140,173 @@ export function SizingField({
|
|
|
1116
1140
|
onMinMaxChange?.(sizingAxis, kind, seedValue);
|
|
1117
1141
|
};
|
|
1118
1142
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
{/* Caret */}
|
|
1150
|
-
<span className="flex shrink-0 items-center text-muted-foreground">
|
|
1151
|
-
<ChevronDownMini />
|
|
1152
|
-
</span>
|
|
1153
|
-
</button>
|
|
1154
|
-
</DropdownMenuTrigger>
|
|
1155
|
-
</TooltipTrigger>
|
|
1156
|
-
<TooltipContent>{`${axis} · ${labels[value]}`}</TooltipContent>
|
|
1157
|
-
</Tooltip>
|
|
1158
|
-
<DropdownMenuContent
|
|
1159
|
-
align="start"
|
|
1160
|
-
className="min-w-[180px] text-[12px]"
|
|
1161
|
-
sideOffset={4}
|
|
1162
|
-
>
|
|
1163
|
-
{/* ── Modes ── */}
|
|
1164
|
-
<SizingMenuItem
|
|
1165
|
-
icon={<IconSizingFixed />}
|
|
1166
|
-
label={labels.fixed}
|
|
1167
|
-
active={value === "fixed"}
|
|
1168
|
-
onSelect={() => onChange("fixed")}
|
|
1169
|
-
/>
|
|
1170
|
-
{canHug ? (
|
|
1171
|
-
<SizingMenuItem
|
|
1172
|
-
icon={<IconSizingHug />}
|
|
1173
|
-
label={labels.hugContents}
|
|
1174
|
-
active={value === "hug"}
|
|
1175
|
-
onSelect={() => onChange("hug")}
|
|
1176
|
-
/>
|
|
1177
|
-
) : null}
|
|
1178
|
-
{canFill ? (
|
|
1179
|
-
<SizingMenuItem
|
|
1180
|
-
icon={<IconSizingFill />}
|
|
1181
|
-
label={labels.fillContainer}
|
|
1182
|
-
active={value === "fill"}
|
|
1183
|
-
onSelect={() => onChange("fill")}
|
|
1184
|
-
/>
|
|
1185
|
-
) : null}
|
|
1186
|
-
|
|
1187
|
-
{/* ── Min / Max ── */}
|
|
1188
|
-
{onMinMaxChange ? (
|
|
1189
|
-
<>
|
|
1190
|
-
<DropdownMenuSeparator />
|
|
1191
|
-
<SizingMenuItem
|
|
1192
|
-
icon={<IconSizingMin />}
|
|
1193
|
-
label={addMinLabel}
|
|
1194
|
-
active={hasMin}
|
|
1195
|
-
disabled={hasMin}
|
|
1196
|
-
onSelect={() => openEditor("min")}
|
|
1197
|
-
/>
|
|
1198
|
-
<SizingMenuItem
|
|
1199
|
-
icon={<IconSizingMax />}
|
|
1200
|
-
label={addMaxLabel}
|
|
1201
|
-
active={hasMax}
|
|
1202
|
-
disabled={hasMax}
|
|
1203
|
-
onSelect={() => openEditor("max")}
|
|
1204
|
-
/>
|
|
1205
|
-
</>
|
|
1206
|
-
) : null}
|
|
1143
|
+
// Shared dropdown content (mode picker menu).
|
|
1144
|
+
const dropdownContent = (
|
|
1145
|
+
<DropdownMenuContent
|
|
1146
|
+
align="start"
|
|
1147
|
+
className="min-w-[180px] text-[12px]"
|
|
1148
|
+
sideOffset={4}
|
|
1149
|
+
>
|
|
1150
|
+
{/* ── Modes ── */}
|
|
1151
|
+
<SizingMenuItem
|
|
1152
|
+
icon={<IconSizingFixed />}
|
|
1153
|
+
label={labels.fixed}
|
|
1154
|
+
active={value === "fixed"}
|
|
1155
|
+
onSelect={() => onChange("fixed")}
|
|
1156
|
+
/>
|
|
1157
|
+
{canHug ? (
|
|
1158
|
+
<SizingMenuItem
|
|
1159
|
+
icon={<IconSizingHug />}
|
|
1160
|
+
label={labels.hugContents}
|
|
1161
|
+
active={value === "hug"}
|
|
1162
|
+
onSelect={() => onChange("hug")}
|
|
1163
|
+
/>
|
|
1164
|
+
) : null}
|
|
1165
|
+
{canFill ? (
|
|
1166
|
+
<SizingMenuItem
|
|
1167
|
+
icon={<IconSizingFill />}
|
|
1168
|
+
label={labels.fillContainer}
|
|
1169
|
+
active={value === "fill"}
|
|
1170
|
+
onSelect={() => onChange("fill")}
|
|
1171
|
+
/>
|
|
1172
|
+
) : null}
|
|
1207
1173
|
|
|
1208
|
-
|
|
1174
|
+
{/* ── Min / Max ── */}
|
|
1175
|
+
{onMinMaxChange ? (
|
|
1176
|
+
<>
|
|
1209
1177
|
<DropdownMenuSeparator />
|
|
1210
1178
|
<SizingMenuItem
|
|
1211
|
-
icon={<
|
|
1212
|
-
label={
|
|
1213
|
-
|
|
1214
|
-
|
|
1179
|
+
icon={<IconSizingMin />}
|
|
1180
|
+
label={addMinLabel}
|
|
1181
|
+
active={hasMin}
|
|
1182
|
+
disabled={hasMin}
|
|
1183
|
+
onSelect={() => openEditor("min")}
|
|
1215
1184
|
/>
|
|
1216
|
-
|
|
1217
|
-
|
|
1185
|
+
<SizingMenuItem
|
|
1186
|
+
icon={<IconSizingMax />}
|
|
1187
|
+
label={addMaxLabel}
|
|
1188
|
+
active={hasMax}
|
|
1189
|
+
disabled={hasMax}
|
|
1190
|
+
onSelect={() => openEditor("max")}
|
|
1191
|
+
/>
|
|
1192
|
+
</>
|
|
1193
|
+
) : null}
|
|
1194
|
+
|
|
1195
|
+
{/* ── Variable ── */}
|
|
1196
|
+
<DropdownMenuSeparator />
|
|
1197
|
+
<SizingMenuItem
|
|
1198
|
+
icon={<IconSizingVariable />}
|
|
1199
|
+
label={labels.applyVariable}
|
|
1200
|
+
disabled={!onApplyVariable}
|
|
1201
|
+
onSelect={() => onApplyVariable?.(sizingAxis)}
|
|
1202
|
+
/>
|
|
1203
|
+
</DropdownMenuContent>
|
|
1204
|
+
);
|
|
1205
|
+
|
|
1206
|
+
return (
|
|
1207
|
+
<div className="flex min-w-0 flex-col gap-1">
|
|
1208
|
+
{isEditableFixed ? (
|
|
1209
|
+
/*
|
|
1210
|
+
* Editable fixed mode: split the trigger into two zones —
|
|
1211
|
+
* [axis letter | ScrubInput (numeric) | ▾ mode-picker caret]
|
|
1212
|
+
* The ScrubInput occupies the center and lets the user type or
|
|
1213
|
+
* drag-to-scrub the size in px. The chevron opens the mode dropdown.
|
|
1214
|
+
*/
|
|
1215
|
+
<DropdownMenu>
|
|
1216
|
+
<div
|
|
1217
|
+
className={cn(
|
|
1218
|
+
"flex h-7 w-full items-center overflow-hidden rounded-md",
|
|
1219
|
+
"bg-[var(--design-editor-control-bg)] text-[11px]",
|
|
1220
|
+
disabled && "pointer-events-none opacity-40",
|
|
1221
|
+
)}
|
|
1222
|
+
>
|
|
1223
|
+
{/* Scrub-editable size value */}
|
|
1224
|
+
<ScrubInput
|
|
1225
|
+
label={axis}
|
|
1226
|
+
ariaLabel={`${axis} size in pixels`}
|
|
1227
|
+
tooltipLabel={`${axis} size`}
|
|
1228
|
+
icon={null}
|
|
1229
|
+
value={Math.round(resolvedSize ?? 0)}
|
|
1230
|
+
onChange={(next) => onSizeChange!(Math.max(1, Math.round(next)))}
|
|
1231
|
+
unit="px"
|
|
1232
|
+
min={1}
|
|
1233
|
+
step={1}
|
|
1234
|
+
precision={0}
|
|
1235
|
+
disabled={disabled}
|
|
1236
|
+
className="min-w-0 flex-1 gap-0"
|
|
1237
|
+
labelClassName="h-7 w-5 justify-center gap-0 rounded-l-md px-0 text-muted-foreground"
|
|
1238
|
+
inputClassName="h-6 border-0 bg-transparent px-1 text-[11px] shadow-none focus-visible:ring-0"
|
|
1239
|
+
/>
|
|
1240
|
+
{/* Caret opens the mode picker */}
|
|
1241
|
+
<Tooltip>
|
|
1242
|
+
<TooltipTrigger asChild>
|
|
1243
|
+
<DropdownMenuTrigger asChild>
|
|
1244
|
+
<button
|
|
1245
|
+
type="button"
|
|
1246
|
+
aria-label={`${axis} sizing mode — ${labels[value]}`}
|
|
1247
|
+
disabled={disabled}
|
|
1248
|
+
className={cn(
|
|
1249
|
+
"flex h-7 w-6 shrink-0 items-center justify-center rounded-r-md",
|
|
1250
|
+
"text-muted-foreground hover:text-foreground",
|
|
1251
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color,hsl(var(--primary)))]",
|
|
1252
|
+
)}
|
|
1253
|
+
>
|
|
1254
|
+
<ChevronDownMini />
|
|
1255
|
+
</button>
|
|
1256
|
+
</DropdownMenuTrigger>
|
|
1257
|
+
</TooltipTrigger>
|
|
1258
|
+
<TooltipContent>
|
|
1259
|
+
{`${axis} · ${labels[value]} — click to change sizing mode`}
|
|
1260
|
+
</TooltipContent>
|
|
1261
|
+
</Tooltip>
|
|
1262
|
+
</div>
|
|
1263
|
+
{dropdownContent}
|
|
1264
|
+
</DropdownMenu>
|
|
1265
|
+
) : (
|
|
1266
|
+
/*
|
|
1267
|
+
* Non-editable / hug / fill mode: keep the original single-button
|
|
1268
|
+
* dropdown trigger showing [axis | resolved size | mode word | ▾].
|
|
1269
|
+
*/
|
|
1270
|
+
<DropdownMenu>
|
|
1271
|
+
<Tooltip>
|
|
1272
|
+
<TooltipTrigger asChild>
|
|
1273
|
+
<DropdownMenuTrigger asChild>
|
|
1274
|
+
<button
|
|
1275
|
+
type="button"
|
|
1276
|
+
aria-label={`${axis} ${Math.round(resolvedSize ?? 0)} ${labels[value]}`}
|
|
1277
|
+
disabled={disabled}
|
|
1278
|
+
className={cn(
|
|
1279
|
+
"flex h-7 w-full items-center gap-1 overflow-hidden rounded-md px-1.5",
|
|
1280
|
+
"bg-[var(--design-editor-control-bg)] text-[11px]",
|
|
1281
|
+
"hover:bg-[var(--design-editor-panel-raised-bg)]",
|
|
1282
|
+
"disabled:pointer-events-none disabled:opacity-40",
|
|
1283
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color,hsl(var(--primary)))]",
|
|
1284
|
+
)}
|
|
1285
|
+
>
|
|
1286
|
+
{/* Axis letter */}
|
|
1287
|
+
<span className="shrink-0 text-muted-foreground">{axis}</span>
|
|
1288
|
+
{/* Resolved size */}
|
|
1289
|
+
<span className="min-w-0 flex-1 truncate text-left tabular-nums text-foreground">
|
|
1290
|
+
{Math.round(resolvedSize ?? 0)}
|
|
1291
|
+
</span>
|
|
1292
|
+
{/* Mode word (Hug/Fill only) */}
|
|
1293
|
+
{showWord ? (
|
|
1294
|
+
<span className="shrink-0 truncate text-muted-foreground">
|
|
1295
|
+
{labels[value]}
|
|
1296
|
+
</span>
|
|
1297
|
+
) : null}
|
|
1298
|
+
{/* Caret */}
|
|
1299
|
+
<span className="flex shrink-0 items-center text-muted-foreground">
|
|
1300
|
+
<ChevronDownMini />
|
|
1301
|
+
</span>
|
|
1302
|
+
</button>
|
|
1303
|
+
</DropdownMenuTrigger>
|
|
1304
|
+
</TooltipTrigger>
|
|
1305
|
+
<TooltipContent>{`${axis} · ${labels[value]} — click to change sizing mode`}</TooltipContent>
|
|
1306
|
+
</Tooltip>
|
|
1307
|
+
{dropdownContent}
|
|
1308
|
+
</DropdownMenu>
|
|
1309
|
+
)}
|
|
1218
1310
|
|
|
1219
1311
|
{/* ── Constraint sub-rows ── */}
|
|
1220
1312
|
{hasMin ? (
|
|
@@ -14,6 +14,7 @@ import type { ShaderDescriptor } from "@shared/shader-presets";
|
|
|
14
14
|
import { IconChevronDown, IconColorPicker } from "@tabler/icons-react";
|
|
15
15
|
import {
|
|
16
16
|
useEffect,
|
|
17
|
+
useMemo,
|
|
17
18
|
useRef,
|
|
18
19
|
useState,
|
|
19
20
|
type JSX,
|
|
@@ -130,6 +131,7 @@ export interface DesignColorPickerProps {
|
|
|
130
131
|
value: string;
|
|
131
132
|
onChange: (value: string) => void;
|
|
132
133
|
onPaintValueChange?: (value: string) => void;
|
|
134
|
+
onImageFillChange?: (value: ImageFillValue) => void;
|
|
133
135
|
label?: string;
|
|
134
136
|
opacity?: number;
|
|
135
137
|
onOpacityChange?: (opacity: number) => void;
|
|
@@ -191,6 +193,87 @@ interface HsvaColor {
|
|
|
191
193
|
|
|
192
194
|
const FALLBACK_COLOR: RgbaColor = { r: 0, g: 0, b: 0, a: 1 };
|
|
193
195
|
|
|
196
|
+
// ─── Extended CSS color parser ──────────────────────────────────────────────────
|
|
197
|
+
//
|
|
198
|
+
// `parseCssColor` from color-utils handles hex, comma-separated rgb/rgba, and
|
|
199
|
+
// hsl/hsla. Browsers increasingly emit modern CSS Level 4 formats from
|
|
200
|
+
// getComputedStyle: space-separated `rgb(R G B)`, `rgb(R G B / A)`, and
|
|
201
|
+
// opaque formats like `oklch(...)` or `color(display-p3 ...)`.
|
|
202
|
+
//
|
|
203
|
+
// This local wrapper extends the parser to cover those cases so that colors
|
|
204
|
+
// arriving from the canvas's computed-style bridge are always usable.
|
|
205
|
+
|
|
206
|
+
const MODERN_RGB_PATTERN =
|
|
207
|
+
/^rgba?\(\s*([0-9.]+)\s+([0-9.]+)\s+([0-9.]+)(?:\s*\/\s*([0-9.]+%?))?\s*\)$/i;
|
|
208
|
+
|
|
209
|
+
/** Canvas element reused across calls for DOM-based color resolution. */
|
|
210
|
+
let _resolverCanvas: HTMLCanvasElement | null = null;
|
|
211
|
+
let _resolverCtx: CanvasRenderingContext2D | null = null;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Parses a CSS color string into RgbaColor, extending the base parser with:
|
|
215
|
+
* - Modern space-separated `rgb(R G B)` / `rgb(R G B / A)` syntax
|
|
216
|
+
* - Opaque formats (oklch, color, etc.) resolved via a hidden canvas
|
|
217
|
+
*
|
|
218
|
+
* Falls back to null if the value is unparseable and the DOM is unavailable.
|
|
219
|
+
*/
|
|
220
|
+
function parseCssColorExtended(value: string): RgbaColor | null {
|
|
221
|
+
// 1. Try the standard parser first (handles hex, comma rgb/rgba, hsl/hsla).
|
|
222
|
+
const standard = parseCssColor(value);
|
|
223
|
+
if (standard) return standard;
|
|
224
|
+
|
|
225
|
+
const trimmed = value.trim();
|
|
226
|
+
if (!trimmed || trimmed === "transparent" || trimmed === "none") return null;
|
|
227
|
+
|
|
228
|
+
// 2. Modern space-separated rgb/rgba — CSS Level 4.
|
|
229
|
+
const modernRgb = trimmed.match(MODERN_RGB_PATTERN);
|
|
230
|
+
if (modernRgb) {
|
|
231
|
+
const parseAlphaLocal = (v: string | undefined): number => {
|
|
232
|
+
if (!v) return 1;
|
|
233
|
+
if (v.endsWith("%"))
|
|
234
|
+
return Math.max(0, Math.min(1, Number(v.slice(0, -1)) / 100));
|
|
235
|
+
return Math.max(0, Math.min(1, Number(v)));
|
|
236
|
+
};
|
|
237
|
+
return {
|
|
238
|
+
r: Math.round(Math.max(0, Math.min(255, Number(modernRgb[1])))),
|
|
239
|
+
g: Math.round(Math.max(0, Math.min(255, Number(modernRgb[2])))),
|
|
240
|
+
b: Math.round(Math.max(0, Math.min(255, Number(modernRgb[3])))),
|
|
241
|
+
a: parseAlphaLocal(modernRgb[4]),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 3. DOM-based resolver for oklch, color(display-p3 ...), hsl (modern), etc.
|
|
246
|
+
// Uses a hidden 1×1 canvas to resolve any valid CSS color to rgb().
|
|
247
|
+
if (typeof document === "undefined") return null;
|
|
248
|
+
try {
|
|
249
|
+
if (!_resolverCanvas) {
|
|
250
|
+
_resolverCanvas = document.createElement("canvas");
|
|
251
|
+
_resolverCanvas.width = 1;
|
|
252
|
+
_resolverCanvas.height = 1;
|
|
253
|
+
}
|
|
254
|
+
if (!_resolverCtx) {
|
|
255
|
+
_resolverCtx = _resolverCanvas.getContext("2d", {
|
|
256
|
+
willReadFrequently: true,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const ctx = _resolverCtx;
|
|
260
|
+
if (!ctx) return null;
|
|
261
|
+
// Detect invalid color values: save fillStyle before and after assignment.
|
|
262
|
+
// If the browser rejects the value, fillStyle won't change.
|
|
263
|
+
const prev = ctx.fillStyle;
|
|
264
|
+
ctx.fillStyle = trimmed;
|
|
265
|
+
const next = ctx.fillStyle; // browser normalises to rgb/hex on accept
|
|
266
|
+
// If the value was rejected, fillStyle stays at the previous value.
|
|
267
|
+
if (next === prev) return null;
|
|
268
|
+
ctx.clearRect(0, 0, 1, 1);
|
|
269
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
270
|
+
const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
|
|
271
|
+
return { r, g, b, a: a / 255 };
|
|
272
|
+
} catch {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
194
277
|
const DEFAULT_LABELS: DesignColorPickerLabels = {
|
|
195
278
|
trigger: "Open color picker", // i18n-ignore fallback component label
|
|
196
279
|
hex: "Hex", // i18n-ignore fallback component label
|
|
@@ -575,6 +658,7 @@ export function DesignColorPicker({
|
|
|
575
658
|
value,
|
|
576
659
|
onChange,
|
|
577
660
|
onPaintValueChange,
|
|
661
|
+
onImageFillChange,
|
|
578
662
|
label: _label,
|
|
579
663
|
opacity,
|
|
580
664
|
onOpacityChange,
|
|
@@ -605,7 +689,7 @@ export function DesignColorPicker({
|
|
|
605
689
|
className,
|
|
606
690
|
}: DesignColorPickerProps) {
|
|
607
691
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
608
|
-
const color =
|
|
692
|
+
const color = parseCssColorExtended(value) ?? FALLBACK_COLOR;
|
|
609
693
|
const hsv = rgbaToHsv(color);
|
|
610
694
|
const hsl = rgbaToHsl(color);
|
|
611
695
|
const effectiveOpacity = opacity ?? alphaToOpacity(color.a);
|
|
@@ -651,16 +735,24 @@ export function DesignColorPicker({
|
|
|
651
735
|
|
|
652
736
|
// Resolve the active gradient: prefer EditPanel-driven props; otherwise parse
|
|
653
737
|
// the live CSS value, falling back to local edit state.
|
|
654
|
-
const parsedGradient =
|
|
738
|
+
const parsedGradient = useMemo(
|
|
739
|
+
() => parseGradientCss(value, gradientType ?? "linear"),
|
|
740
|
+
[gradientType, value],
|
|
741
|
+
);
|
|
742
|
+
const fallbackGradient = useMemo(
|
|
743
|
+
() =>
|
|
744
|
+
GRADIENT_TYPES.has(effectivePaintType)
|
|
745
|
+
? defaultGradient(
|
|
746
|
+
effectivePaintType as GradientKind,
|
|
747
|
+
toCssColor(color) || "#000000",
|
|
748
|
+
)
|
|
749
|
+
: null,
|
|
750
|
+
[color.r, color.g, color.b, color.a, effectivePaintType],
|
|
751
|
+
);
|
|
655
752
|
const activeGradient: GradientValue | null = GRADIENT_TYPES.has(
|
|
656
753
|
effectivePaintType,
|
|
657
754
|
)
|
|
658
|
-
? (localGradient ??
|
|
659
|
-
parsedGradient ??
|
|
660
|
-
defaultGradient(
|
|
661
|
-
effectivePaintType as GradientKind,
|
|
662
|
-
toCssColor(color) || "#000000",
|
|
663
|
-
))
|
|
755
|
+
? (localGradient ?? parsedGradient ?? fallbackGradient)
|
|
664
756
|
: null;
|
|
665
757
|
|
|
666
758
|
useEffect(() => {
|
|
@@ -754,7 +846,8 @@ export function DesignColorPicker({
|
|
|
754
846
|
|
|
755
847
|
// The 2D field edits the selected gradient stop's color when in gradient mode.
|
|
756
848
|
const fieldColor: RgbaColor = activeGradient
|
|
757
|
-
? (
|
|
849
|
+
? (parseCssColorExtended(selectedStop?.color ?? "#000000") ??
|
|
850
|
+
FALLBACK_COLOR)
|
|
758
851
|
: color;
|
|
759
852
|
const rawFieldHsv = rgbaToHsv(fieldColor);
|
|
760
853
|
// Preserve the last non-zero hue so dragging through gray doesn't lose it.
|
|
@@ -771,7 +864,7 @@ export function DesignColorPicker({
|
|
|
771
864
|
const selectedStopColor = selectedStop?.color;
|
|
772
865
|
useEffect(() => {
|
|
773
866
|
if (!activeGradient || !selectedStopColor) return;
|
|
774
|
-
const parsed =
|
|
867
|
+
const parsed = parseCssColorExtended(selectedStopColor);
|
|
775
868
|
if (parsed) setHexDraft(toDisplayHex(parsed));
|
|
776
869
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
777
870
|
}, [selectedStopColor, selectedStopId]);
|
|
@@ -807,6 +900,10 @@ export function DesignColorPicker({
|
|
|
807
900
|
|
|
808
901
|
const emitImageFill = (next: ImageFillValue) => {
|
|
809
902
|
setImageFill(next);
|
|
903
|
+
if (onImageFillChange) {
|
|
904
|
+
onImageFillChange(next);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
810
907
|
emitPaintValue(imageFillToCss(next));
|
|
811
908
|
};
|
|
812
909
|
|
|
@@ -860,6 +957,10 @@ export function DesignColorPicker({
|
|
|
860
957
|
return;
|
|
861
958
|
}
|
|
862
959
|
if (nextType === "image") {
|
|
960
|
+
if (onImageFillChange && imageFill.url) {
|
|
961
|
+
onImageFillChange(imageFill);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
863
964
|
emitPaintValue(imageFill.url ? imageFillToCss(imageFill) : "transparent");
|
|
864
965
|
return;
|
|
865
966
|
}
|
|
@@ -1060,8 +1161,18 @@ export function DesignColorPicker({
|
|
|
1060
1161
|
align="start"
|
|
1061
1162
|
sideOffset={8}
|
|
1062
1163
|
className="z-[10000] w-[252px] p-0 shadow-xl"
|
|
1164
|
+
// Keep the picker open when the style change triggered by a paint-type
|
|
1165
|
+
// switch causes the canvas to re-project the element. Without this,
|
|
1166
|
+
// Radix treats the resulting focus shift as an "interact outside" event
|
|
1167
|
+
// and closes the popover before the type switch is visible.
|
|
1168
|
+
onInteractOutside={(e) => {
|
|
1169
|
+
// Allow closing only for genuine pointer clicks on the canvas area
|
|
1170
|
+
// (the user clicked somewhere else). Programmatic focus changes from
|
|
1171
|
+
// the canvas bridge (element re-projection) should not close the picker.
|
|
1172
|
+
if (e.type === "focusoutside") e.preventDefault();
|
|
1173
|
+
}}
|
|
1063
1174
|
>
|
|
1064
|
-
<div className="
|
|
1175
|
+
<div className="rounded-md bg-popover text-popover-foreground">
|
|
1065
1176
|
{view === "shader" ? (
|
|
1066
1177
|
<ShaderFillsPanel
|
|
1067
1178
|
descriptor={shaderDescriptor ?? undefined}
|