@agent-native/core 0.80.2 → 0.80.5
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 +66 -2
- package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -1
- package/corpus/core/docs/content/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/template-design.mdx +1 -1
- package/corpus/core/package.json +3 -7
- package/corpus/core/src/action.ts +32 -0
- package/corpus/core/src/agent/durable-background.ts +32 -11
- package/corpus/core/src/agent/production-agent.ts +14 -1
- package/corpus/core/src/agent/run-loop-with-resume.ts +3 -1
- package/corpus/core/src/brand-kit/fig/index.ts +17 -37
- package/corpus/core/src/cli/index.ts +1 -1
- package/corpus/core/src/cli/mcp-config-writers.ts +187 -51
- package/corpus/core/src/cli/plan-local.ts +445 -108
- package/corpus/core/src/cli/skills.ts +32 -8
- package/corpus/core/src/client/AssistantChat.tsx +11 -1
- package/corpus/core/src/client/agent-chat.ts +12 -1
- package/corpus/core/src/client/composer/PromptComposer.tsx +100 -41
- package/corpus/core/src/client/org/OrgSwitcher.tsx +1 -1
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -27
- package/corpus/core/src/collab/presence.ts +1 -1
- package/corpus/core/src/mcp/connect-route.ts +34 -5
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/action-routes.ts +14 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +20 -0
- package/corpus/core/src/vite/client.ts +15 -1
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +117 -33
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
- package/corpus/templates/analytics/changelog/2026-06-27-dashboards-can-now-nest-under-a-parent-in-the-sidebar-via-a-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-pages-now-sit-closer-under-the-header-with-tighter.md +6 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +3 -0
- package/corpus/templates/assets/actions/generate-image.ts +3 -0
- package/corpus/templates/assets/changelog/2026-06-29-longer-agent-generation-window.md +6 -0
- package/corpus/templates/assets/netlify.toml +3 -3
- package/corpus/templates/assets/server/plugins/agent-chat.ts +4 -1
- package/corpus/templates/clips/app/components/capture-install-options.tsx +7 -8
- package/corpus/templates/clips/app/components/player/timestamped-comment-button.tsx +162 -0
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +1 -1
- package/corpus/templates/clips/app/i18n/ar-SA.ts +4 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +4 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +4 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +4 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +4 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +4 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +4 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +42 -18
- package/corpus/templates/content/AGENTS.md +12 -0
- package/corpus/templates/content/app/blocks/BuilderDocsBlocks.tsx +10 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -0
- package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +185 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -0
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +19 -0
- package/corpus/templates/content/app/components/editor/extensions/ContentReferenceNode.tsx +82 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +12 -0
- package/corpus/templates/content/app/i18n-data.ts +140 -0
- package/corpus/templates/content/changelog/2026-06-29-reusable-mdx-references-now-preview-linked-local-documents-i.md +6 -0
- package/corpus/templates/content/shared/builder-docs-blocks.ts +8 -0
- package/corpus/templates/content/shared/builder-mdx.ts +141 -4
- package/corpus/templates/content/shared/nfm.ts +62 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +10 -11
- package/corpus/templates/design/AGENTS.md +5 -0
- package/corpus/templates/design/_gitignore +7 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +22 -12
- package/corpus/templates/design/actions/create-design-system.ts +15 -4
- package/corpus/templates/design/actions/create-file.ts +20 -1
- package/corpus/templates/design/actions/delete-design-system.ts +2 -2
- package/corpus/templates/design/actions/duplicate-design.ts +35 -9
- package/corpus/templates/design/actions/generate-design.ts +146 -69
- package/corpus/templates/design/actions/generate-screens.ts +51 -19
- package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
- package/corpus/templates/design/actions/get-design.ts +2 -0
- package/corpus/templates/design/actions/import-design-project.ts +28 -9
- package/corpus/templates/design/actions/import-document.ts +22 -8
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +6 -153
- package/corpus/templates/design/actions/navigate.ts +105 -54
- package/corpus/templates/design/actions/set-default-design-system.ts +17 -3
- package/corpus/templates/design/actions/show-design-questions.ts +2 -0
- package/corpus/templates/design/actions/update-design.ts +2 -1
- package/corpus/templates/design/actions/view-screen.ts +12 -6
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +4 -4
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +159 -41
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +8 -4
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +37 -42
- package/corpus/templates/design/app/components/design/EditPanel.tsx +336 -183
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +153 -34
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +135 -33
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +6 -0
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +4 -10
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +84 -43
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +4 -4
- package/corpus/templates/design/app/components/design/inspector/{FigmaColorPicker.tsx → DesignColorPicker.tsx} +89 -62
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +14 -26
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +40 -12
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +13 -13
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -9
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/inspector/{figma-icons.tsx → design-icons.tsx} +2 -2
- package/corpus/templates/design/app/components/design/inspector/index.ts +12 -12
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +5 -2
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -3
- package/corpus/templates/design/app/components/layout/Layout.tsx +5 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +167 -91
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +16 -3
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +15 -0
- package/corpus/templates/design/app/global.css +4 -0
- package/corpus/templates/design/app/hooks/use-design-system.ts +4 -1
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +38 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +9 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
- package/corpus/templates/design/app/i18n-data.ts +527 -0
- package/corpus/templates/design/app/lib/design-systems.ts +6 -4
- package/corpus/templates/design/app/lib/design-templates.ts +15 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +641 -158
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +42 -34
- package/corpus/templates/design/app/pages/VisualEdit.tsx +100 -0
- package/corpus/templates/design/app/root.tsx +42 -9
- package/corpus/templates/design/app/routes/visual-edit.tsx +7 -0
- package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +1 -1
- package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +1 -1
- package/corpus/templates/design/changelog/2026-06-28-visual-code-design-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-generation-prompts-now-show-connect-ai-before-sending.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +1 -1
- package/corpus/templates/design/changelog/2026-06-29-fixed-design-editor-exports-and-gradient-edits-so-multi-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-png-export-handles-modern-css-color-functions-more-reliably.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-png-export-now-ignores-duplicate-clicks-while-a-download-is-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-public-visual-edit-and-shared-design-links-now-invite-visito.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-visual-edit-actions-now-reject-missing-design-targets-before.md +6 -0
- package/corpus/templates/design/e2e/README.md +71 -0
- package/corpus/templates/design/e2e/global-setup.ts +157 -0
- package/corpus/templates/design/e2e/helpers.ts +148 -0
- package/corpus/templates/design/netlify.toml +3 -4
- package/corpus/templates/design/package.json +5 -1
- package/corpus/templates/design/playwright.config.ts +51 -0
- package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +121 -0
- package/corpus/templates/design/server/handlers/uploads.ts +89 -38
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +181 -0
- package/corpus/templates/design/server/lib/coding-handoff.ts +33 -4
- package/corpus/templates/design/server/lib/design-export.ts +35 -8
- package/corpus/templates/design/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/design/server/plugins/auth.ts +10 -1
- package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +1 -0
- package/corpus/templates/design/shared/api.ts +2 -0
- package/corpus/templates/design/shared/apply-edits.ts +12 -8
- package/corpus/templates/design/shared/canvas-math.ts +19 -1
- package/corpus/templates/design/shared/code-layer.ts +87 -28
- package/corpus/templates/design/shared/generation-session.ts +1 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +4 -3
- package/corpus/templates/design/shared/shader-presets.ts +10 -0
- package/corpus/templates/design/shared/source-mode.ts +9 -0
- package/corpus/templates/design/vitest.config.ts +2 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +1 -1
- package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +14 -5
- package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +14 -5
- package/corpus/templates/plan/AGENTS.md +1 -1
- package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
- package/corpus/templates/plan/actions/validate-local-plan-source.ts +110 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -0
- package/corpus/templates/plan/docs/commenting-ux-plan.md +2 -2
- package/corpus/templates/plan/server/lib/plan-connector-catalog.ts +1 -0
- package/corpus/templates/plan/server/lib/public-action-paths.ts +1 -0
- package/corpus/templates/plan/server/plan-content.ts +1 -1
- package/corpus/templates/plan/server/ui-plan-html.ts +2 -2
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +6 -153
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +53 -38
- package/corpus/templates/slides/app/components/design-system/{fig-import-response.ts → builder-index-response.ts} +9 -6
- package/corpus/templates/slides/app/components/visual-editor/MultiSelectChip.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-06-29-design-system-setup-now-indexes-uploaded-design-files-throug.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
- package/corpus/templates/slides/netlify.toml +3 -6
- package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +116 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +179 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -1
- package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +1 -0
- package/dist/action.d.ts +23 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +9 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/durable-background.d.ts +9 -7
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +27 -11
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +10 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +2 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +2 -2
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/brand-kit/fig/index.d.ts +4 -4
- package/dist/brand-kit/fig/index.d.ts.map +1 -1
- package/dist/brand-kit/fig/index.js +14 -27
- package/dist/brand-kit/fig/index.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts +15 -7
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +198 -45
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/cli/plan-local.d.ts +30 -0
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +281 -56
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/skills.d.ts +2 -2
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +32 -8
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +11 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +15 -2
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +35 -1
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/org/OrgSwitcher.js +1 -1
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +2 -15
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/presence.d.ts +1 -1
- package/dist/collab/presence.js +1 -1
- package/dist/collab/presence.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/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +34 -5
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/observability/routes.d.ts +2 -2
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +14 -0
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +6 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +12 -3
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +12 -1
- package/dist/vite/client.js.map +1 -1
- package/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ar-SA/template-design.mdx +1 -1
- package/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
- package/docs/content/locales/de-DE/template-design.mdx +1 -1
- package/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/es-ES/template-design.mdx +1 -1
- package/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/fr-FR/template-design.mdx +1 -1
- package/docs/content/locales/hi-IN/template-design.mdx +1 -1
- package/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ja-JP/template-design.mdx +1 -1
- package/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ko-KR/template-design.mdx +1 -1
- package/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/pt-BR/template-design.mdx +1 -1
- package/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/zh-CN/template-design.mdx +1 -1
- package/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/zh-TW/template-design.mdx +1 -1
- package/docs/content/real-time-collaboration.mdx +2 -2
- package/docs/content/template-design.mdx +1 -1
- package/package.json +3 -7
- package/corpus/core/src/brand-kit/fig/decode.ts +0 -410
- package/corpus/core/src/brand-kit/fig/extract-design-system.ts +0 -947
- package/corpus/core/src/brand-kit/fig/fig-to-html.ts +0 -1906
- package/corpus/templates/design/actions/import-figma.ts +0 -55
- package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +0 -6
- package/corpus/templates/design/server/handlers/import-figma-system.ts +0 -86
- package/corpus/templates/design/server/routes/api/import-figma-system.post.ts +0 -1
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +0 -101
- package/corpus/templates/slides/server/routes/api/import-figma-system.post.ts +0 -1
- package/dist/brand-kit/fig/decode.d.ts +0 -33
- package/dist/brand-kit/fig/decode.d.ts.map +0 -1
- package/dist/brand-kit/fig/decode.js +0 -358
- package/dist/brand-kit/fig/decode.js.map +0 -1
- package/dist/brand-kit/fig/extract-design-system.d.ts +0 -44
- package/dist/brand-kit/fig/extract-design-system.d.ts.map +0 -1
- package/dist/brand-kit/fig/extract-design-system.js +0 -752
- package/dist/brand-kit/fig/extract-design-system.js.map +0 -1
- package/dist/brand-kit/fig/fig-to-html.d.ts +0 -246
- package/dist/brand-kit/fig/fig-to-html.d.ts.map +0 -1
- package/dist/brand-kit/fig/fig-to-html.js +0 -1506
- package/dist/brand-kit/fig/fig-to-html.js.map +0 -1
|
@@ -91,7 +91,7 @@ import {
|
|
|
91
91
|
AutoLayoutMatrix,
|
|
92
92
|
ConstraintsWidget,
|
|
93
93
|
ExportSettingsPanel,
|
|
94
|
-
|
|
94
|
+
DesignColorPicker,
|
|
95
95
|
ScrubInput,
|
|
96
96
|
SizingField,
|
|
97
97
|
type AlignmentMatrixValue,
|
|
@@ -100,13 +100,13 @@ import {
|
|
|
100
100
|
type AutoLayoutSizingAxis,
|
|
101
101
|
type ConstraintsValue,
|
|
102
102
|
type ExportSettingsValue,
|
|
103
|
-
type
|
|
104
|
-
type
|
|
105
|
-
type
|
|
106
|
-
type
|
|
107
|
-
type
|
|
103
|
+
type DesignFillRow,
|
|
104
|
+
type DesignFillRowPatch,
|
|
105
|
+
type DesignGradientStop,
|
|
106
|
+
type DesignGradientStopPatch,
|
|
107
|
+
type DesignGradientType,
|
|
108
108
|
} from "./inspector";
|
|
109
|
-
import type {
|
|
109
|
+
import type { DesignPaintType } from "./inspector/DesignColorPicker";
|
|
110
110
|
import { TweaksPanelContent } from "./TweaksPanel";
|
|
111
111
|
import type { ElementInfo } from "./types";
|
|
112
112
|
|
|
@@ -127,7 +127,7 @@ interface EditPanelProps {
|
|
|
127
127
|
onRequestTweaks?: (anchor: HTMLElement) => void;
|
|
128
128
|
onStyleChange: (property: string, value: string) => void;
|
|
129
129
|
onStylesChange?: (styles: Record<string, string>) => void;
|
|
130
|
-
onExport?: (settings: ExportSettingsValue) => void;
|
|
130
|
+
onExport?: (settings: ExportSettingsValue[]) => void;
|
|
131
131
|
exporting?: boolean;
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -175,7 +175,10 @@ function PropInput({
|
|
|
175
175
|
}, [value]);
|
|
176
176
|
|
|
177
177
|
const commit = () => {
|
|
178
|
-
if (defaultUnit === undefined)
|
|
178
|
+
if (defaultUnit === undefined) {
|
|
179
|
+
if (draft !== value) onChange(draft);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
179
182
|
const next = normalizeLengthValue(draft, defaultUnit);
|
|
180
183
|
if (next !== draft) setDraft(next);
|
|
181
184
|
if (next !== value) onChange(next);
|
|
@@ -210,7 +213,7 @@ function PropInput({
|
|
|
210
213
|
);
|
|
211
214
|
}
|
|
212
215
|
|
|
213
|
-
/** Compact color input: label +
|
|
216
|
+
/** Compact color input: label + design-editor picker popover. */
|
|
214
217
|
function ColorInput({
|
|
215
218
|
label,
|
|
216
219
|
value,
|
|
@@ -221,6 +224,7 @@ function ColorInput({
|
|
|
221
224
|
onBlendModeChange,
|
|
222
225
|
supportsLayeredFills = false,
|
|
223
226
|
documentColors,
|
|
227
|
+
pickerKey,
|
|
224
228
|
}: {
|
|
225
229
|
label: string;
|
|
226
230
|
value: string;
|
|
@@ -232,6 +236,7 @@ function ColorInput({
|
|
|
232
236
|
supportsLayeredFills?: boolean;
|
|
233
237
|
/** Hex strings already in use on the page — forwarded to the color picker swatch grid. */
|
|
234
238
|
documentColors?: string[];
|
|
239
|
+
pickerKey?: string;
|
|
235
240
|
}) {
|
|
236
241
|
const [draft, setDraft] = useState(value);
|
|
237
242
|
const [selectedFillId, setSelectedFillId] = useState(SOLID_FILL_ID);
|
|
@@ -333,7 +338,7 @@ function ColorInput({
|
|
|
333
338
|
)
|
|
334
339
|
: undefined;
|
|
335
340
|
|
|
336
|
-
const handleFillChange = (id: string, patch:
|
|
341
|
+
const handleFillChange = (id: string, patch: DesignFillRowPatch) => {
|
|
337
342
|
if (id === SOLID_FILL_ID) {
|
|
338
343
|
if (patch.value !== undefined) setNext(patch.value);
|
|
339
344
|
if (patch.opacity !== undefined) {
|
|
@@ -361,6 +366,7 @@ function ColorInput({
|
|
|
361
366
|
...stop,
|
|
362
367
|
opacity: patch.opacity,
|
|
363
368
|
})),
|
|
369
|
+
gradient.prefix,
|
|
364
370
|
),
|
|
365
371
|
);
|
|
366
372
|
};
|
|
@@ -387,7 +393,7 @@ function ColorInput({
|
|
|
387
393
|
|
|
388
394
|
const handleGradientTypeChange =
|
|
389
395
|
activeGradient && activeGradientIndex !== null
|
|
390
|
-
? (type:
|
|
396
|
+
? (type: DesignGradientType) => {
|
|
391
397
|
replaceBackgroundLayer(
|
|
392
398
|
activeGradientIndex,
|
|
393
399
|
buildGradientLayer(type, activeGradient.stops),
|
|
@@ -397,13 +403,17 @@ function ColorInput({
|
|
|
397
403
|
|
|
398
404
|
const handleGradientStopChange =
|
|
399
405
|
activeGradient && activeGradientIndex !== null
|
|
400
|
-
? (id: string, patch:
|
|
406
|
+
? (id: string, patch: DesignGradientStopPatch) => {
|
|
401
407
|
const nextStops = activeGradient.stops.map((stop) =>
|
|
402
408
|
stop.id === id ? { ...stop, ...patch } : stop,
|
|
403
409
|
);
|
|
404
410
|
replaceBackgroundLayer(
|
|
405
411
|
activeGradientIndex,
|
|
406
|
-
buildGradientLayer(
|
|
412
|
+
buildGradientLayer(
|
|
413
|
+
activeGradient.type,
|
|
414
|
+
nextStops,
|
|
415
|
+
activeGradient.prefix,
|
|
416
|
+
),
|
|
407
417
|
);
|
|
408
418
|
}
|
|
409
419
|
: undefined;
|
|
@@ -411,7 +421,7 @@ function ColorInput({
|
|
|
411
421
|
const handleAddGradientStop = onBackgroundImageChange
|
|
412
422
|
? () => {
|
|
413
423
|
if (activeGradient && activeGradientIndex !== null) {
|
|
414
|
-
const nextStop:
|
|
424
|
+
const nextStop: DesignGradientStop = {
|
|
415
425
|
id: `stop-${activeGradient.stops.length}`,
|
|
416
426
|
color: draft || "#000000",
|
|
417
427
|
position: 50,
|
|
@@ -419,10 +429,11 @@ function ColorInput({
|
|
|
419
429
|
};
|
|
420
430
|
replaceBackgroundLayer(
|
|
421
431
|
activeGradientIndex,
|
|
422
|
-
buildGradientLayer(
|
|
423
|
-
|
|
424
|
-
nextStop,
|
|
425
|
-
|
|
432
|
+
buildGradientLayer(
|
|
433
|
+
activeGradient.type,
|
|
434
|
+
[...activeGradient.stops, nextStop],
|
|
435
|
+
activeGradient.prefix,
|
|
436
|
+
),
|
|
426
437
|
);
|
|
427
438
|
setSelectedStopId(nextStop.id);
|
|
428
439
|
return;
|
|
@@ -448,13 +459,17 @@ function ColorInput({
|
|
|
448
459
|
);
|
|
449
460
|
replaceBackgroundLayer(
|
|
450
461
|
activeGradientIndex,
|
|
451
|
-
buildGradientLayer(
|
|
462
|
+
buildGradientLayer(
|
|
463
|
+
activeGradient.type,
|
|
464
|
+
nextStops,
|
|
465
|
+
activeGradient.prefix,
|
|
466
|
+
),
|
|
452
467
|
);
|
|
453
468
|
setSelectedStopId(nextStops[0]?.id);
|
|
454
469
|
}
|
|
455
470
|
: undefined;
|
|
456
471
|
|
|
457
|
-
const selectedPaintType:
|
|
472
|
+
const selectedPaintType: DesignPaintType =
|
|
458
473
|
selectedFillId !== SOLID_FILL_ID
|
|
459
474
|
? selectedGradient
|
|
460
475
|
? selectedGradient.type
|
|
@@ -466,8 +481,7 @@ function ColorInput({
|
|
|
466
481
|
selectedLayerIndex !== null
|
|
467
482
|
? (backgroundLayers[selectedLayerIndex] ?? draft ?? value ?? "#000000")
|
|
468
483
|
: draft || "#000000";
|
|
469
|
-
|
|
470
|
-
const handlePaintTypeChange = (type: FigmaPaintType) => {
|
|
484
|
+
const handlePaintTypeChange = (type: DesignPaintType) => {
|
|
471
485
|
const selectedLayer = fillLayerIndex(selectedFillId);
|
|
472
486
|
if (type === "solid") {
|
|
473
487
|
if (selectedLayer !== null) removeBackgroundLayer(selectedLayer);
|
|
@@ -493,7 +507,7 @@ function ColorInput({
|
|
|
493
507
|
) {
|
|
494
508
|
return;
|
|
495
509
|
}
|
|
496
|
-
const nextType:
|
|
510
|
+
const nextType: DesignGradientType = type;
|
|
497
511
|
const layerIndex = selectedLayer ?? activeGradientIndex;
|
|
498
512
|
if (layerIndex !== null) {
|
|
499
513
|
const currentGradient = parseGradientLayer(
|
|
@@ -518,7 +532,8 @@ function ColorInput({
|
|
|
518
532
|
};
|
|
519
533
|
|
|
520
534
|
return (
|
|
521
|
-
<
|
|
535
|
+
<DesignColorPicker
|
|
536
|
+
key={pickerKey}
|
|
522
537
|
label={label}
|
|
523
538
|
value={pickerValue}
|
|
524
539
|
onChange={setNext}
|
|
@@ -555,8 +570,25 @@ const SOLID_FILL_ID = "solid";
|
|
|
555
570
|
const FILL_LAYER_PREFIX = "layer:";
|
|
556
571
|
|
|
557
572
|
interface ParsedGradientLayer {
|
|
558
|
-
type:
|
|
559
|
-
|
|
573
|
+
type: DesignGradientType;
|
|
574
|
+
prefix?: string;
|
|
575
|
+
stops: DesignGradientStop[];
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const DEFAULT_EXPORT_SETTINGS: ExportSettingsValue = {
|
|
579
|
+
scale: 1,
|
|
580
|
+
format: "png",
|
|
581
|
+
suffix: "",
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
function elementIdentityKey(element: ElementInfo): string {
|
|
585
|
+
return [
|
|
586
|
+
element.sourceId ?? element.id ?? element.selector ?? element.tagName,
|
|
587
|
+
Math.round(element.boundingRect.x),
|
|
588
|
+
Math.round(element.boundingRect.y),
|
|
589
|
+
Math.round(element.boundingRect.width),
|
|
590
|
+
Math.round(element.boundingRect.height),
|
|
591
|
+
].join(":");
|
|
560
592
|
}
|
|
561
593
|
|
|
562
594
|
function fillLayerId(index: number): string {
|
|
@@ -573,9 +605,9 @@ function buildFillRows(
|
|
|
573
605
|
colorValue: string,
|
|
574
606
|
backgroundLayers: string[],
|
|
575
607
|
selectedFillId: string,
|
|
576
|
-
):
|
|
608
|
+
): DesignFillRow[] {
|
|
577
609
|
const solid = parseCssColor(colorValue);
|
|
578
|
-
const rows:
|
|
610
|
+
const rows: DesignFillRow[] = [
|
|
579
611
|
{
|
|
580
612
|
id: SOLID_FILL_ID,
|
|
581
613
|
label: "Solid", // i18n-ignore inspector fallback label
|
|
@@ -605,7 +637,7 @@ function buildFillRows(
|
|
|
605
637
|
return rows;
|
|
606
638
|
}
|
|
607
639
|
|
|
608
|
-
function averageGradientOpacity(stops:
|
|
640
|
+
function averageGradientOpacity(stops: DesignGradientStop[]): number {
|
|
609
641
|
if (!stops.length) return 100;
|
|
610
642
|
const total = stops.reduce((sum, stop) => {
|
|
611
643
|
const parsed = parseCssColor(stop.color);
|
|
@@ -642,27 +674,28 @@ function joinCssLayers(layers: string[]): string {
|
|
|
642
674
|
return cleaned.length ? cleaned.join(", ") : "none";
|
|
643
675
|
}
|
|
644
676
|
|
|
645
|
-
function parseGradientLayer(layer: string): ParsedGradientLayer | null {
|
|
677
|
+
export function parseGradientLayer(layer: string): ParsedGradientLayer | null {
|
|
646
678
|
const match = layer.trim().match(/^(linear|radial|conic)-gradient\((.*)\)$/i);
|
|
647
679
|
if (!match) return null;
|
|
648
680
|
|
|
649
681
|
const parts = splitCssLayers(match[2] || "");
|
|
650
682
|
const type = gradientTypeFromCss(match[1] || "", layer);
|
|
651
683
|
const firstStop = parseGradientStop(parts[0] || "", 0, parts.length);
|
|
684
|
+
const prefix = firstStop ? undefined : parts[0]?.trim();
|
|
652
685
|
const stopParts = firstStop ? parts : parts.slice(1);
|
|
653
686
|
const stops = stopParts
|
|
654
687
|
.map((part, index) => parseGradientStop(part, index, stopParts.length))
|
|
655
|
-
.filter((stop): stop is
|
|
688
|
+
.filter((stop): stop is DesignGradientStop => Boolean(stop));
|
|
656
689
|
|
|
657
690
|
if (!stops.length) return null;
|
|
658
|
-
return { type, stops };
|
|
691
|
+
return { type, prefix, stops };
|
|
659
692
|
}
|
|
660
693
|
|
|
661
694
|
function parseGradientStop(
|
|
662
695
|
part: string,
|
|
663
696
|
index: number,
|
|
664
697
|
total: number,
|
|
665
|
-
):
|
|
698
|
+
): DesignGradientStop | null {
|
|
666
699
|
const color = readLeadingColor(part);
|
|
667
700
|
if (!color) return null;
|
|
668
701
|
const parsed = parseCssColor(color.value);
|
|
@@ -690,7 +723,7 @@ function readLeadingColor(part: string): { raw: string; value: string } | null {
|
|
|
690
723
|
if (transparent) {
|
|
691
724
|
return { raw: transparent[0], value: "rgba(0, 0, 0, 0)" };
|
|
692
725
|
}
|
|
693
|
-
const functionName = trimmed.match(/^(
|
|
726
|
+
const functionName = trimmed.match(/^[a-z][a-z0-9-]*\(/i);
|
|
694
727
|
if (!functionName) return null;
|
|
695
728
|
let depth = 0;
|
|
696
729
|
for (let index = 0; index < trimmed.length; index += 1) {
|
|
@@ -710,29 +743,37 @@ function readLeadingColor(part: string): { raw: string; value: string } | null {
|
|
|
710
743
|
function gradientTypeFromCss(
|
|
711
744
|
functionName: string,
|
|
712
745
|
layer: string,
|
|
713
|
-
):
|
|
746
|
+
): DesignGradientType {
|
|
714
747
|
if (functionName.toLowerCase() === "conic") return "angular";
|
|
715
748
|
if (/closest-corner/i.test(layer)) return "diamond";
|
|
716
749
|
if (functionName.toLowerCase() === "radial") return "radial";
|
|
717
750
|
return "linear";
|
|
718
751
|
}
|
|
719
752
|
|
|
720
|
-
function gradientLabel(type:
|
|
753
|
+
function gradientLabel(type: DesignGradientType): string {
|
|
721
754
|
if (type === "radial") {
|
|
722
|
-
return "Radial gradient"; // i18n-ignore
|
|
755
|
+
return "Radial gradient"; // i18n-ignore design inspector paint row
|
|
723
756
|
}
|
|
724
757
|
if (type === "angular") {
|
|
725
|
-
return "Angular gradient"; // i18n-ignore
|
|
758
|
+
return "Angular gradient"; // i18n-ignore design inspector paint row
|
|
726
759
|
}
|
|
727
760
|
if (type === "diamond") {
|
|
728
|
-
return "Diamond gradient"; // i18n-ignore
|
|
761
|
+
return "Diamond gradient"; // i18n-ignore design inspector paint row
|
|
729
762
|
}
|
|
730
|
-
return "Linear gradient"; // i18n-ignore
|
|
763
|
+
return "Linear gradient"; // i18n-ignore design inspector paint row
|
|
731
764
|
}
|
|
732
765
|
|
|
733
|
-
function
|
|
734
|
-
type
|
|
735
|
-
|
|
766
|
+
function defaultGradientPrefix(type: DesignGradientType): string {
|
|
767
|
+
if (type === "radial") return "circle at 50% 50%";
|
|
768
|
+
if (type === "angular") return "from 0deg at 50% 50%";
|
|
769
|
+
if (type === "diamond") return "closest-corner at 50% 50%";
|
|
770
|
+
return "90deg";
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export function buildGradientLayer(
|
|
774
|
+
type: DesignGradientType,
|
|
775
|
+
stops: DesignGradientStop[],
|
|
776
|
+
prefix = defaultGradientPrefix(type),
|
|
736
777
|
): string {
|
|
737
778
|
const stopList = [...stops]
|
|
738
779
|
.sort((a, b) => a.position - b.position)
|
|
@@ -746,19 +787,14 @@ function buildGradientLayer(
|
|
|
746
787
|
})
|
|
747
788
|
.join(", ");
|
|
748
789
|
|
|
749
|
-
if (type === "radial") {
|
|
750
|
-
return `radial-gradient(
|
|
751
|
-
}
|
|
752
|
-
if (type === "angular") {
|
|
753
|
-
return `conic-gradient(from 0deg at 50% 50%, ${stopList})`;
|
|
790
|
+
if (type === "radial" || type === "diamond") {
|
|
791
|
+
return `radial-gradient(${prefix}, ${stopList})`;
|
|
754
792
|
}
|
|
755
|
-
if (type === "
|
|
756
|
-
|
|
757
|
-
}
|
|
758
|
-
return `linear-gradient(90deg, ${stopList})`;
|
|
793
|
+
if (type === "angular") return `conic-gradient(${prefix}, ${stopList})`;
|
|
794
|
+
return `linear-gradient(${prefix}, ${stopList})`;
|
|
759
795
|
}
|
|
760
796
|
|
|
761
|
-
function defaultGradientStops(colorValue: string):
|
|
797
|
+
function defaultGradientStops(colorValue: string): DesignGradientStop[] {
|
|
762
798
|
const parsed =
|
|
763
799
|
parseCssColor(cssColorOrFallback(colorValue, "#000000")) ??
|
|
764
800
|
parseCssColor("#000000");
|
|
@@ -773,7 +809,7 @@ function defaultGradientStops(colorValue: string): FigmaGradientStop[] {
|
|
|
773
809
|
];
|
|
774
810
|
}
|
|
775
811
|
|
|
776
|
-
function defaultGradientLayer(type:
|
|
812
|
+
function defaultGradientLayer(type: DesignGradientType, colorValue: string) {
|
|
777
813
|
return buildGradientLayer(type, defaultGradientStops(colorValue));
|
|
778
814
|
}
|
|
779
815
|
|
|
@@ -857,14 +893,14 @@ function PropSlider({
|
|
|
857
893
|
const EmptyScrubIcon = () => null;
|
|
858
894
|
|
|
859
895
|
/**
|
|
860
|
-
*
|
|
861
|
-
* - NO left collapse chevron (
|
|
896
|
+
* design-editor inspector section. Matches the design editor "Design" panel chrome:
|
|
897
|
+
* - NO left collapse chevron (the design editor uses none).
|
|
862
898
|
* - A thin divider line above each section.
|
|
863
899
|
* - A bold left-aligned title.
|
|
864
900
|
* - Right-aligned action icons (add layer, toggles, styles, etc.).
|
|
865
901
|
*
|
|
866
|
-
* The title is still clickable to collapse the body (
|
|
867
|
-
* on title click) but renders no chevron glyph, just
|
|
902
|
+
* The title is still clickable to collapse the body (design sections collapse
|
|
903
|
+
* on title click) but renders no chevron glyph, just the same way.
|
|
868
904
|
*/
|
|
869
905
|
function PanelSection({
|
|
870
906
|
title,
|
|
@@ -919,7 +955,7 @@ function SubsectionLabel({ children }: { children: ReactNode }) {
|
|
|
919
955
|
);
|
|
920
956
|
}
|
|
921
957
|
|
|
922
|
-
function
|
|
958
|
+
function DesignSpacingControl({
|
|
923
959
|
label,
|
|
924
960
|
values,
|
|
925
961
|
onChange,
|
|
@@ -987,7 +1023,6 @@ function FigmaSpacingControl({
|
|
|
987
1023
|
onChange={setAll}
|
|
988
1024
|
unit="px"
|
|
989
1025
|
min={0}
|
|
990
|
-
precision={1}
|
|
991
1026
|
labelClassName="w-16"
|
|
992
1027
|
inputClassName="h-6"
|
|
993
1028
|
/>
|
|
@@ -1082,9 +1117,11 @@ const ALIGN_SELF_OPTIONS = [
|
|
|
1082
1117
|
{ value: "stretch", key: "stretch" },
|
|
1083
1118
|
{ value: "baseline", key: "baseline" },
|
|
1084
1119
|
] as const;
|
|
1120
|
+
// "center" stroke position is omitted: CSS has no native single-property
|
|
1121
|
+
// centered stroke; choosing it in the UI caused a confusing revert to "inside"
|
|
1122
|
+
// on next render. Inside (border) and outside (outline) are fully supported.
|
|
1085
1123
|
const STROKE_POSITION_OPTIONS = [
|
|
1086
1124
|
{ value: "inside", key: "inside" },
|
|
1087
|
-
{ value: "center", key: "center" },
|
|
1088
1125
|
{ value: "outside", key: "outside" },
|
|
1089
1126
|
] as const;
|
|
1090
1127
|
const BLEND_MODE_OPTIONS = [
|
|
@@ -1094,10 +1131,10 @@ const BLEND_MODE_OPTIONS = [
|
|
|
1094
1131
|
{ value: "overlay", label: "Overlay" },
|
|
1095
1132
|
{ value: "darken", label: "Darken" },
|
|
1096
1133
|
{ value: "lighten", label: "Lighten" },
|
|
1097
|
-
{ value: "color-dodge", label: "Color dodge" }, // i18n-ignore
|
|
1098
|
-
{ value: "color-burn", label: "Color burn" }, // i18n-ignore
|
|
1099
|
-
{ value: "hard-light", label: "Hard light" }, // i18n-ignore
|
|
1100
|
-
{ value: "soft-light", label: "Soft light" }, // i18n-ignore
|
|
1134
|
+
{ value: "color-dodge", label: "Color dodge" }, // i18n-ignore design blend mode label
|
|
1135
|
+
{ value: "color-burn", label: "Color burn" }, // i18n-ignore design blend mode label
|
|
1136
|
+
{ value: "hard-light", label: "Hard light" }, // i18n-ignore design blend mode label
|
|
1137
|
+
{ value: "soft-light", label: "Soft light" }, // i18n-ignore design blend mode label
|
|
1101
1138
|
{ value: "difference", label: "Difference" },
|
|
1102
1139
|
{ value: "exclusion", label: "Exclusion" },
|
|
1103
1140
|
{ value: "hue", label: "Hue" },
|
|
@@ -1110,11 +1147,47 @@ function parseNumericValue(value: string): number {
|
|
|
1110
1147
|
return parseFloat(value) || 0;
|
|
1111
1148
|
}
|
|
1112
1149
|
|
|
1150
|
+
/**
|
|
1151
|
+
* Resolve a CSS line-height value to a unitless ratio for display/editing.
|
|
1152
|
+
* When the browser returns a px-computed value (e.g. "19.2px" for line-height
|
|
1153
|
+
* 1.2 on a 16px font), divide by the font-size to recover the unitless ratio.
|
|
1154
|
+
* Falls back to 1.2 when the value cannot be parsed.
|
|
1155
|
+
*/
|
|
1156
|
+
function resolveLineHeight(
|
|
1157
|
+
lineHeight: string | undefined,
|
|
1158
|
+
fontSize: string | undefined,
|
|
1159
|
+
): number {
|
|
1160
|
+
const lh = lineHeight?.trim() || "";
|
|
1161
|
+
if (!lh || lh === "normal") return 1.2;
|
|
1162
|
+
if (lh.endsWith("px")) {
|
|
1163
|
+
const lhPx = parseFloat(lh);
|
|
1164
|
+
const fsPx = parseFloat(fontSize || "");
|
|
1165
|
+
if (Number.isFinite(lhPx) && Number.isFinite(fsPx) && fsPx > 0) {
|
|
1166
|
+
return Math.round((lhPx / fsPx) * 100) / 100;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
const numeric = parseFloat(lh);
|
|
1170
|
+
return Number.isFinite(numeric) && numeric > 0 ? numeric : 1.2;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1113
1173
|
function parseRotationValue(transform: string | undefined): number {
|
|
1114
1174
|
const match = transform?.match(/rotate\((-?\d+(?:\.\d+)?)deg\)/);
|
|
1115
1175
|
return match ? Number(match[1]) : 0;
|
|
1116
1176
|
}
|
|
1117
1177
|
|
|
1178
|
+
/**
|
|
1179
|
+
* Parse a CSS `scale` property value (e.g. "-1 1", "1", "none") into two
|
|
1180
|
+
* numeric components [scaleX, scaleY]. Defaults both axes to 1 when absent
|
|
1181
|
+
* or unparseable, matching the CSS initial value.
|
|
1182
|
+
*/
|
|
1183
|
+
function parseScaleValue(value: string | undefined): [number, number] {
|
|
1184
|
+
if (!value || value === "none") return [1, 1];
|
|
1185
|
+
const parts = value.trim().split(/\s+/);
|
|
1186
|
+
const x = Number(parts[0]);
|
|
1187
|
+
const y = parts.length > 1 ? Number(parts[1]) : x;
|
|
1188
|
+
return [Number.isFinite(x) ? x : 1, Number.isFinite(y) ? y : 1];
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1118
1191
|
function mergeRotationValue(transform: string | undefined, degrees: number) {
|
|
1119
1192
|
const nextRotate = `rotate(${Math.round(degrees * 10) / 10}deg)`;
|
|
1120
1193
|
if (!transform || transform === "none") return nextRotate;
|
|
@@ -1124,6 +1197,26 @@ function mergeRotationValue(transform: string | undefined, degrees: number) {
|
|
|
1124
1197
|
return `${transform} ${nextRotate}`;
|
|
1125
1198
|
}
|
|
1126
1199
|
|
|
1200
|
+
/**
|
|
1201
|
+
* Replace or remove a translateX/translateY function within an existing
|
|
1202
|
+
* transform string while preserving all other transform functions (rotate,
|
|
1203
|
+
* scale, skew, etc.). Pass `null` as `value` to strip the function.
|
|
1204
|
+
*/
|
|
1205
|
+
function mergeTranslateFunction(
|
|
1206
|
+
transform: string | undefined,
|
|
1207
|
+
axis: "X" | "Y",
|
|
1208
|
+
value: string | null,
|
|
1209
|
+
): string {
|
|
1210
|
+
const pattern =
|
|
1211
|
+
axis === "X" ? /translateX\([^)]*\)/g : /translateY\([^)]*\)/g;
|
|
1212
|
+
const base = (!transform || transform === "none" ? "" : transform)
|
|
1213
|
+
.replace(pattern, "")
|
|
1214
|
+
.trim();
|
|
1215
|
+
if (value === null) return base || "none";
|
|
1216
|
+
const fn = `translate${axis}(${value})`;
|
|
1217
|
+
return base ? `${fn} ${base}` : fn;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1127
1220
|
function ScrubStyleInput({
|
|
1128
1221
|
label,
|
|
1129
1222
|
value,
|
|
@@ -1307,9 +1400,9 @@ function autoLayoutAlignmentFromStyles(
|
|
|
1307
1400
|
}
|
|
1308
1401
|
|
|
1309
1402
|
/**
|
|
1310
|
-
* Block-level container tags that act
|
|
1403
|
+
* Block-level container tags that act the same way frames. Selecting any of
|
|
1311
1404
|
* these shows the Auto layout section (in an "add" state when not yet flex),
|
|
1312
|
-
* mirroring
|
|
1405
|
+
* mirroring the editor pattern where any frame/container exposes auto-layout controls.
|
|
1313
1406
|
*/
|
|
1314
1407
|
const CONTAINER_TAGS = new Set([
|
|
1315
1408
|
"div",
|
|
@@ -1355,7 +1448,7 @@ const LEAF_TAGS = new Set([
|
|
|
1355
1448
|
* Whether the element should expose the Auto layout section. True for anything
|
|
1356
1449
|
* already laid out with flexbox, or any block-level container tag that isn't a
|
|
1357
1450
|
* known leaf/text element. This is what makes a plain frame/container with
|
|
1358
|
-
* children show the full Auto layout section
|
|
1451
|
+
* children show the full Auto layout section the same way does.
|
|
1359
1452
|
*/
|
|
1360
1453
|
function isContainerElement(element: ElementInfo): boolean {
|
|
1361
1454
|
if (element.isFlexContainer) return true;
|
|
@@ -1386,7 +1479,7 @@ function isTextElement(element: ElementInfo): boolean {
|
|
|
1386
1479
|
}
|
|
1387
1480
|
|
|
1388
1481
|
/**
|
|
1389
|
-
* Per-axis sizing availability following
|
|
1482
|
+
* Per-axis sizing availability following the design editor's contextual rules:
|
|
1390
1483
|
* - Fixed: always.
|
|
1391
1484
|
* - Hug contents: only CONTAINERS (flex/container frames) and TEXT can hug
|
|
1392
1485
|
* their content. Leaves like img/svg/input cannot.
|
|
@@ -1597,12 +1690,12 @@ function SelectionHeader({ element }: { element: ElementInfo | null }) {
|
|
|
1597
1690
|
{/* M3 · Right-aligned quick actions: create-component + dev inspect (</>) */}
|
|
1598
1691
|
<div className="flex shrink-0 items-center gap-0.5">
|
|
1599
1692
|
<SectionIconButton
|
|
1600
|
-
label={"Create component" /* i18n-ignore
|
|
1693
|
+
label={"Create component" /* i18n-ignore design inspector action */}
|
|
1601
1694
|
>
|
|
1602
1695
|
<IconComponents className="size-3.5" />
|
|
1603
1696
|
</SectionIconButton>
|
|
1604
1697
|
<SectionIconButton
|
|
1605
|
-
label={"Inspect code" /* i18n-ignore
|
|
1698
|
+
label={"Inspect code" /* i18n-ignore design inspector action */}
|
|
1606
1699
|
>
|
|
1607
1700
|
<IconCode className="size-3.5" />
|
|
1608
1701
|
</SectionIconButton>
|
|
@@ -1633,7 +1726,7 @@ function InspectorTabsHeader({
|
|
|
1633
1726
|
value="design"
|
|
1634
1727
|
className="h-6 rounded-md px-1.5 text-[11px] font-semibold text-muted-foreground shadow-none transition-colors hover:text-foreground data-[state=active]:bg-[var(--design-editor-panel-raised-bg)] data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
|
1635
1728
|
>
|
|
1636
|
-
{"Design" /* i18n-ignore
|
|
1729
|
+
{"Design" /* i18n-ignore design inspector tab */}
|
|
1637
1730
|
</TabsTrigger>
|
|
1638
1731
|
<TabsTrigger
|
|
1639
1732
|
value="tweaks"
|
|
@@ -1691,7 +1784,7 @@ function SectionIconButton({
|
|
|
1691
1784
|
}
|
|
1692
1785
|
|
|
1693
1786
|
/**
|
|
1694
|
-
* Section-header toggle icon (
|
|
1787
|
+
* Section-header toggle icon (the design editor's right-aligned section actions, e.g. the
|
|
1695
1788
|
* auto-layout ⊞ toggle). Highlights with the accent color when active.
|
|
1696
1789
|
*/
|
|
1697
1790
|
function SectionIconToggle({
|
|
@@ -1922,10 +2015,7 @@ function StrokeLayerControl({
|
|
|
1922
2015
|
const t = useT();
|
|
1923
2016
|
const strokePositionOptions = STROKE_POSITION_OPTIONS.map((option) => ({
|
|
1924
2017
|
value: option.value,
|
|
1925
|
-
label:
|
|
1926
|
-
option.key === "center"
|
|
1927
|
-
? t("editPanel.textAligns.center")
|
|
1928
|
-
: t(`editPanel.labels.${option.key}`),
|
|
2018
|
+
label: t(`editPanel.labels.${option.key}`),
|
|
1929
2019
|
}));
|
|
1930
2020
|
const prefix = kind === "border" ? "border" : "outline";
|
|
1931
2021
|
const position = kind === "border" ? "inside" : "outside";
|
|
@@ -1944,7 +2034,7 @@ function StrokeLayerControl({
|
|
|
1944
2034
|
|
|
1945
2035
|
return (
|
|
1946
2036
|
<div className="space-y-1.5">
|
|
1947
|
-
{/*
|
|
2037
|
+
{/* design stroke row: [swatch+hex trigger (flex-1)] [eye] [remove] */}
|
|
1948
2038
|
<div className="group flex items-center gap-1.5">
|
|
1949
2039
|
<div className="min-w-0 flex-1">
|
|
1950
2040
|
<ColorInput
|
|
@@ -1986,7 +2076,7 @@ function StrokeLayerControl({
|
|
|
1986
2076
|
<IconMinus className="size-3.5" />
|
|
1987
2077
|
</SectionIconButton>
|
|
1988
2078
|
</div>
|
|
1989
|
-
{/*
|
|
2079
|
+
{/* design stroke geometry: position + weight side by side */}
|
|
1990
2080
|
<div className="grid grid-cols-2 gap-1.5">
|
|
1991
2081
|
<Select value={position} onValueChange={movePosition}>
|
|
1992
2082
|
<SelectTrigger className="h-6 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)]">
|
|
@@ -2105,7 +2195,7 @@ function serializeShadowLayers(layers: ShadowLayer[]) {
|
|
|
2105
2195
|
`${Math.round(layer.x)}px`,
|
|
2106
2196
|
`${Math.round(layer.y)}px`,
|
|
2107
2197
|
`${Math.max(0, Math.round(layer.blur))}px`,
|
|
2108
|
-
`${Math.max(0, Math.round(layer.spread))}px`,
|
|
2198
|
+
`${layer.inset ? Math.round(layer.spread) : Math.max(0, Math.round(layer.spread))}px`,
|
|
2109
2199
|
layer.color,
|
|
2110
2200
|
]
|
|
2111
2201
|
.filter(Boolean)
|
|
@@ -2133,7 +2223,7 @@ function ShadowEffectRow({
|
|
|
2133
2223
|
const t = useT();
|
|
2134
2224
|
return (
|
|
2135
2225
|
<Popover>
|
|
2136
|
-
{/*
|
|
2226
|
+
{/* design effect row: [swatch+label+x,y,blur trigger (flex-1)] [remove] */}
|
|
2137
2227
|
<div className="group flex items-center gap-1.5">
|
|
2138
2228
|
<PopoverTrigger asChild>
|
|
2139
2229
|
<button
|
|
@@ -2216,9 +2306,11 @@ function ShadowEffectRow({
|
|
|
2216
2306
|
<ScrubInput
|
|
2217
2307
|
label={t("editPanel.labels.spread")}
|
|
2218
2308
|
value={layer.spread}
|
|
2219
|
-
onChange={(value) =>
|
|
2309
|
+
onChange={(value) =>
|
|
2310
|
+
onChange({ spread: layer.inset ? value : Math.max(0, value) })
|
|
2311
|
+
}
|
|
2220
2312
|
unit="px"
|
|
2221
|
-
min={0}
|
|
2313
|
+
min={layer.inset ? undefined : 0}
|
|
2222
2314
|
precision={1}
|
|
2223
2315
|
inputClassName="h-6"
|
|
2224
2316
|
/>
|
|
@@ -2304,7 +2396,7 @@ function TypographyProperties({
|
|
|
2304
2396
|
}));
|
|
2305
2397
|
const textAlign = styles.textAlign || "left";
|
|
2306
2398
|
|
|
2307
|
-
// M1 · Text resizing mode (auto-width / auto-height / fixed).
|
|
2399
|
+
// M1 · Text resizing mode (auto-width / auto-height / fixed). the design editor's text
|
|
2308
2400
|
// nodes always expose this segment. Infer the current mode from the live CSS:
|
|
2309
2401
|
// auto-width hugs both axes (width:auto + no wrapping), auto-height hugs the
|
|
2310
2402
|
// height only (fixed width, content wraps), fixed pins both width and height.
|
|
@@ -2337,7 +2429,7 @@ function TypographyProperties({
|
|
|
2337
2429
|
};
|
|
2338
2430
|
|
|
2339
2431
|
// M2 · Vertical text alignment (top / middle / bottom). For auto-layout text
|
|
2340
|
-
// containers (display:flex)
|
|
2432
|
+
// containers (display:flex) the design editor maps this to `justifyContent`; for normal
|
|
2341
2433
|
// text we fall back to `verticalAlign`, which is what an inline/grid text box
|
|
2342
2434
|
// honors. Read whichever the element currently expresses.
|
|
2343
2435
|
const display = (styles.display || "").toLowerCase();
|
|
@@ -2373,21 +2465,21 @@ function TypographyProperties({
|
|
|
2373
2465
|
{/* Row 0: text resizing (auto-width / auto-height / fixed) */}
|
|
2374
2466
|
<InspectorSegment>
|
|
2375
2467
|
<InspectorIconButton
|
|
2376
|
-
label={"Auto width" /* i18n-ignore
|
|
2468
|
+
label={"Auto width" /* i18n-ignore design text-resize mode */}
|
|
2377
2469
|
active={resizeMode === "auto-width"}
|
|
2378
2470
|
onClick={() => setResizeMode("auto-width")}
|
|
2379
2471
|
>
|
|
2380
2472
|
<IconArrowAutofitWidth className="size-3.5" />
|
|
2381
2473
|
</InspectorIconButton>
|
|
2382
2474
|
<InspectorIconButton
|
|
2383
|
-
label={"Auto height" /* i18n-ignore
|
|
2475
|
+
label={"Auto height" /* i18n-ignore design text-resize mode */}
|
|
2384
2476
|
active={resizeMode === "auto-height"}
|
|
2385
2477
|
onClick={() => setResizeMode("auto-height")}
|
|
2386
2478
|
>
|
|
2387
2479
|
<IconArrowAutofitHeight className="size-3.5" />
|
|
2388
2480
|
</InspectorIconButton>
|
|
2389
2481
|
<InspectorIconButton
|
|
2390
|
-
label={"Fixed size" /* i18n-ignore
|
|
2482
|
+
label={"Fixed size" /* i18n-ignore design text-resize mode */}
|
|
2391
2483
|
active={resizeMode === "fixed"}
|
|
2392
2484
|
onClick={() => setResizeMode("fixed")}
|
|
2393
2485
|
>
|
|
@@ -2454,13 +2546,13 @@ function TypographyProperties({
|
|
|
2454
2546
|
/>
|
|
2455
2547
|
</div>
|
|
2456
2548
|
|
|
2457
|
-
{/* Row 3: line-height + letter-spacing with
|
|
2549
|
+
{/* Row 3: line-height + letter-spacing with design-editor leading icons */}
|
|
2458
2550
|
<div className="grid grid-cols-2 gap-1.5">
|
|
2459
2551
|
<ScrubInput
|
|
2460
2552
|
label={t("editPanel.labels.lineHeight")}
|
|
2461
2553
|
ariaLabel={t("editPanel.labels.lineHeight")}
|
|
2462
2554
|
icon={IconLineHeight}
|
|
2463
|
-
value={
|
|
2555
|
+
value={resolveLineHeight(styles.lineHeight, styles.fontSize)}
|
|
2464
2556
|
onChange={(value) =>
|
|
2465
2557
|
onStyleChange("lineHeight", String(Math.max(0.1, value)))
|
|
2466
2558
|
}
|
|
@@ -2521,21 +2613,21 @@ function TypographyProperties({
|
|
|
2521
2613
|
</InspectorSegment>
|
|
2522
2614
|
<InspectorSegment>
|
|
2523
2615
|
<InspectorIconButton
|
|
2524
|
-
label={"Align top" /* i18n-ignore
|
|
2616
|
+
label={"Align top" /* i18n-ignore design vertical text align */}
|
|
2525
2617
|
active={verticalAlign === "top"}
|
|
2526
2618
|
onClick={() => setVerticalAlign("top")}
|
|
2527
2619
|
>
|
|
2528
2620
|
<IconLayoutAlignTop className="size-3.5" />
|
|
2529
2621
|
</InspectorIconButton>
|
|
2530
2622
|
<InspectorIconButton
|
|
2531
|
-
label={"Align middle" /* i18n-ignore
|
|
2623
|
+
label={"Align middle" /* i18n-ignore design vertical text align */}
|
|
2532
2624
|
active={verticalAlign === "middle"}
|
|
2533
2625
|
onClick={() => setVerticalAlign("middle")}
|
|
2534
2626
|
>
|
|
2535
2627
|
<IconLayoutAlignMiddle className="size-3.5" />
|
|
2536
2628
|
</InspectorIconButton>
|
|
2537
2629
|
<InspectorIconButton
|
|
2538
|
-
label={"Align bottom" /* i18n-ignore
|
|
2630
|
+
label={"Align bottom" /* i18n-ignore design vertical text align */}
|
|
2539
2631
|
active={verticalAlign === "bottom"}
|
|
2540
2632
|
onClick={() => setVerticalAlign("bottom")}
|
|
2541
2633
|
>
|
|
@@ -2660,10 +2752,13 @@ function FlexContainerControls({
|
|
|
2660
2752
|
onPaddingLinkedChange={(linked) => {
|
|
2661
2753
|
setPaddingLinked(linked);
|
|
2662
2754
|
if (!linked) return;
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
onStyleChange("
|
|
2755
|
+
const avg = Math.round(
|
|
2756
|
+
(padding.top + padding.right + padding.bottom + padding.left) / 4,
|
|
2757
|
+
);
|
|
2758
|
+
onStyleChange("paddingTop", `${avg}px`);
|
|
2759
|
+
onStyleChange("paddingRight", `${avg}px`);
|
|
2760
|
+
onStyleChange("paddingBottom", `${avg}px`);
|
|
2761
|
+
onStyleChange("paddingLeft", `${avg}px`);
|
|
2667
2762
|
}}
|
|
2668
2763
|
onClipContentChange={(clipContent) =>
|
|
2669
2764
|
onStyleChange("overflow", clipContent ? "hidden" : "visible")
|
|
@@ -2821,11 +2916,11 @@ function LayoutContextProperties({
|
|
|
2821
2916
|
);
|
|
2822
2917
|
|
|
2823
2918
|
// Leaf elements (text, img, svg, etc.) never get auto layout — show the plain
|
|
2824
|
-
//
|
|
2919
|
+
// design W/H sizing block instead.
|
|
2825
2920
|
if (!isContainer) {
|
|
2826
2921
|
return (
|
|
2827
2922
|
<PanelSection title={t("editPanel.sections.layout")}>
|
|
2828
|
-
{/*
|
|
2923
|
+
{/* design-editor single-row-per-axis: [W | value | Fixed/Hug/Fill ▾] with
|
|
2829
2924
|
the full sizing menu (modes + min/max + variable) per axis. */}
|
|
2830
2925
|
<div className="grid grid-cols-2 items-start gap-1.5">
|
|
2831
2926
|
<SizingField
|
|
@@ -2877,7 +2972,7 @@ function LayoutContextProperties({
|
|
|
2877
2972
|
}
|
|
2878
2973
|
|
|
2879
2974
|
// Any container element ALREADY has a layout in code — normal flow (block) by
|
|
2880
|
-
// default, or flex when it uses flexbox.
|
|
2975
|
+
// default, or flex when it uses flexbox. the design editor never makes you "add" auto
|
|
2881
2976
|
// layout for a frame, so we always render the full layout controls and let
|
|
2882
2977
|
// the Flow control reflect/switch the element's current `display`. Choosing a
|
|
2883
2978
|
// horizontal/vertical/wrap/grid flow applies `display:flex`; choosing the
|
|
@@ -2895,7 +2990,7 @@ function LayoutContextProperties({
|
|
|
2895
2990
|
}
|
|
2896
2991
|
|
|
2897
2992
|
/**
|
|
2898
|
-
*
|
|
2993
|
+
* design layout-guide section. Shown for frame/container
|
|
2899
2994
|
* elements. Renders an overlay column/row guide by applying a non-destructive
|
|
2900
2995
|
* `backgroundImage` repeating gradient layer tagged so it can be toggled off
|
|
2901
2996
|
* without disturbing real fills.
|
|
@@ -2903,7 +2998,7 @@ function LayoutContextProperties({
|
|
|
2903
2998
|
const LAYOUT_GUIDE_MARKER = "/* an-layout-guide */";
|
|
2904
2999
|
|
|
2905
3000
|
function hasLayoutGuide(styles: Record<string, string>): boolean {
|
|
2906
|
-
return Boolean(styles.backgroundImage?.includes(
|
|
3001
|
+
return Boolean(styles.backgroundImage?.includes(LAYOUT_GUIDE_MARKER));
|
|
2907
3002
|
}
|
|
2908
3003
|
|
|
2909
3004
|
function LayoutGuideProperties({
|
|
@@ -2917,9 +3012,10 @@ function LayoutGuideProperties({
|
|
|
2917
3012
|
const active = hasLayoutGuide(styles);
|
|
2918
3013
|
|
|
2919
3014
|
const addGuide = () => {
|
|
2920
|
-
// 12-column overlay guide —
|
|
2921
|
-
|
|
2922
|
-
|
|
3015
|
+
// 12-column overlay guide — the design editor's default columns layout grid.
|
|
3016
|
+
// The LAYOUT_GUIDE_MARKER comment is embedded so hasLayoutGuide and removeGuide
|
|
3017
|
+
// can detect/remove it without touching unrelated repeating-linear-gradient fills.
|
|
3018
|
+
const guide = `repeating-linear-gradient(to right, color-mix(in srgb, var(--design-editor-accent-color) 22%, transparent) 0 1px, transparent 1px calc(100% / 12)) ${LAYOUT_GUIDE_MARKER}`;
|
|
2923
3019
|
const existing = compactCssValue(styles.backgroundImage, "");
|
|
2924
3020
|
onStyleChange(
|
|
2925
3021
|
"backgroundImage",
|
|
@@ -2929,7 +3025,7 @@ function LayoutGuideProperties({
|
|
|
2929
3025
|
|
|
2930
3026
|
const removeGuide = () => {
|
|
2931
3027
|
const layers = splitCssLayers(styles.backgroundImage || "").filter(
|
|
2932
|
-
(layer) => !layer.includes(
|
|
3028
|
+
(layer) => !layer.includes(LAYOUT_GUIDE_MARKER),
|
|
2933
3029
|
);
|
|
2934
3030
|
onStyleChange(
|
|
2935
3031
|
"backgroundImage",
|
|
@@ -2939,14 +3035,14 @@ function LayoutGuideProperties({
|
|
|
2939
3035
|
|
|
2940
3036
|
return (
|
|
2941
3037
|
<PanelSection
|
|
2942
|
-
title={"Layout guide" /* i18n-ignore
|
|
3038
|
+
title={"Layout guide" /* i18n-ignore design inspector label */}
|
|
2943
3039
|
defaultCollapsed
|
|
2944
3040
|
actions={
|
|
2945
3041
|
<SectionIconButton
|
|
2946
3042
|
label={
|
|
2947
3043
|
active
|
|
2948
|
-
? "Remove layout guide" /* i18n-ignore
|
|
2949
|
-
: "Add layout guide" /* i18n-ignore
|
|
3044
|
+
? "Remove layout guide" /* i18n-ignore design inspector action */
|
|
3045
|
+
: "Add layout guide" /* i18n-ignore design inspector action */
|
|
2950
3046
|
}
|
|
2951
3047
|
onClick={active ? removeGuide : addGuide}
|
|
2952
3048
|
>
|
|
@@ -2962,13 +3058,13 @@ function LayoutGuideProperties({
|
|
|
2962
3058
|
<div className="flex items-center gap-2 rounded-md bg-[var(--design-editor-control-bg)] px-2 py-1.5 text-[11px] text-muted-foreground">
|
|
2963
3059
|
<IconLayoutGrid className="size-3.5 shrink-0" />
|
|
2964
3060
|
<span className="min-w-0 flex-1 truncate text-foreground">
|
|
2965
|
-
{"Columns" /* i18n-ignore
|
|
3061
|
+
{"Columns" /* i18n-ignore design inspector label */}
|
|
2966
3062
|
</span>
|
|
2967
3063
|
<span className="shrink-0 tabular-nums">12</span>
|
|
2968
3064
|
</div>
|
|
2969
3065
|
) : (
|
|
2970
3066
|
<p className="text-[11px] text-muted-foreground">
|
|
2971
|
-
{"No layout guides" /* i18n-ignore
|
|
3067
|
+
{"No layout guides" /* i18n-ignore design inspector empty state */}
|
|
2972
3068
|
</p>
|
|
2973
3069
|
)}
|
|
2974
3070
|
</PanelSection>
|
|
@@ -2976,7 +3072,7 @@ function LayoutGuideProperties({
|
|
|
2976
3072
|
}
|
|
2977
3073
|
|
|
2978
3074
|
/**
|
|
2979
|
-
* Togglable export preview thumbnail (
|
|
3075
|
+
* Togglable export preview thumbnail (the design editor shows a small preview of the export
|
|
2980
3076
|
* frame above the export rows). Renders a proportional placeholder reflecting
|
|
2981
3077
|
* the selected element's aspect ratio, fill, radius and dimensions.
|
|
2982
3078
|
*/
|
|
@@ -3026,36 +3122,56 @@ function PositionLayoutProperties({
|
|
|
3026
3122
|
const styles = element.computedStyles;
|
|
3027
3123
|
const constrainedPosition =
|
|
3028
3124
|
styles.position === "absolute" || styles.position === "fixed";
|
|
3029
|
-
// Reflect the active packing in the alignment segments (
|
|
3125
|
+
// Reflect the active packing in the alignment segments (the design editor highlights the
|
|
3030
3126
|
// current alignment). For a flex container the main axis is justifyContent.
|
|
3031
3127
|
const alignH = justifyToHorizontal(styles.justifyContent);
|
|
3032
3128
|
const alignV = alignToVertical(styles.alignItems);
|
|
3033
3129
|
const constraintsValue: ConstraintsValue = {
|
|
3034
3130
|
horizontal:
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3131
|
+
// Check scale before left+right: "scale" writes width:100% and clears
|
|
3132
|
+
// left/right to auto, but legacy data may have 0px values that are truthy.
|
|
3133
|
+
styles.width === "100%"
|
|
3134
|
+
? "scale"
|
|
3135
|
+
: styles.left && styles.right
|
|
3136
|
+
? "left-right"
|
|
3137
|
+
: styles.right
|
|
3138
|
+
? "right"
|
|
3139
|
+
: styles.transform?.includes("translateX(-50%)")
|
|
3140
|
+
? "center"
|
|
3043
3141
|
: "left",
|
|
3044
3142
|
vertical:
|
|
3045
|
-
styles.
|
|
3046
|
-
? "
|
|
3047
|
-
: styles.bottom
|
|
3048
|
-
? "bottom"
|
|
3049
|
-
: styles.
|
|
3050
|
-
? "
|
|
3051
|
-
: styles.
|
|
3052
|
-
? "
|
|
3143
|
+
styles.height === "100%"
|
|
3144
|
+
? "scale"
|
|
3145
|
+
: styles.top && styles.bottom
|
|
3146
|
+
? "top-bottom"
|
|
3147
|
+
: styles.bottom
|
|
3148
|
+
? "bottom"
|
|
3149
|
+
: styles.transform?.includes("translateY(-50%)")
|
|
3150
|
+
? "center"
|
|
3053
3151
|
: "top",
|
|
3054
3152
|
};
|
|
3055
3153
|
|
|
3056
3154
|
const handleConstraintsChange = useCallback(
|
|
3057
3155
|
(value: ConstraintsValue) => {
|
|
3058
3156
|
onStyleChange("position", "absolute");
|
|
3157
|
+
|
|
3158
|
+
// Compute the desired translateX/Y for each axis independently, then
|
|
3159
|
+
// compose both into a single transform write so the two axes don't
|
|
3160
|
+
// overwrite each other when both change simultaneously.
|
|
3161
|
+
const txValue = value.horizontal === "center" ? "-50%" : null;
|
|
3162
|
+
const tyValue = value.vertical === "center" ? "-50%" : null;
|
|
3163
|
+
// Start from the current transform, apply X, then apply Y on top.
|
|
3164
|
+
const transformAfterX = mergeTranslateFunction(
|
|
3165
|
+
styles.transform,
|
|
3166
|
+
"X",
|
|
3167
|
+
txValue,
|
|
3168
|
+
);
|
|
3169
|
+
const transformAfterXY = mergeTranslateFunction(
|
|
3170
|
+
transformAfterX,
|
|
3171
|
+
"Y",
|
|
3172
|
+
tyValue,
|
|
3173
|
+
);
|
|
3174
|
+
|
|
3059
3175
|
if (value.horizontal === "left") {
|
|
3060
3176
|
onStyleChange(
|
|
3061
3177
|
"left",
|
|
@@ -3074,10 +3190,11 @@ function PositionLayoutProperties({
|
|
|
3074
3190
|
} else if (value.horizontal === "center") {
|
|
3075
3191
|
onStyleChange("left", "50%");
|
|
3076
3192
|
onStyleChange("right", "auto");
|
|
3077
|
-
onStyleChange("transform", "translateX(-50%)");
|
|
3078
3193
|
} else {
|
|
3079
|
-
|
|
3080
|
-
|
|
3194
|
+
// scale: use auto (not 0px) so the left && right truthiness check
|
|
3195
|
+
// in the reader does not misidentify this as "left-right".
|
|
3196
|
+
onStyleChange("left", "auto");
|
|
3197
|
+
onStyleChange("right", "auto");
|
|
3081
3198
|
onStyleChange("width", "100%");
|
|
3082
3199
|
}
|
|
3083
3200
|
|
|
@@ -3099,12 +3216,15 @@ function PositionLayoutProperties({
|
|
|
3099
3216
|
} else if (value.vertical === "center") {
|
|
3100
3217
|
onStyleChange("top", "50%");
|
|
3101
3218
|
onStyleChange("bottom", "auto");
|
|
3102
|
-
onStyleChange("transform", "translateY(-50%)");
|
|
3103
3219
|
} else {
|
|
3104
|
-
|
|
3105
|
-
onStyleChange("
|
|
3220
|
+
// scale
|
|
3221
|
+
onStyleChange("top", "auto");
|
|
3222
|
+
onStyleChange("bottom", "auto");
|
|
3106
3223
|
onStyleChange("height", "100%");
|
|
3107
3224
|
}
|
|
3225
|
+
|
|
3226
|
+
// Write the composed transform once, after both axes are resolved.
|
|
3227
|
+
onStyleChange("transform", transformAfterXY);
|
|
3108
3228
|
},
|
|
3109
3229
|
[
|
|
3110
3230
|
element.boundingRect.x,
|
|
@@ -3112,6 +3232,7 @@ function PositionLayoutProperties({
|
|
|
3112
3232
|
onStyleChange,
|
|
3113
3233
|
styles.left,
|
|
3114
3234
|
styles.top,
|
|
3235
|
+
styles.transform,
|
|
3115
3236
|
],
|
|
3116
3237
|
);
|
|
3117
3238
|
|
|
@@ -3120,7 +3241,7 @@ function PositionLayoutProperties({
|
|
|
3120
3241
|
title={t("editPanel.sections.positionLayout")}
|
|
3121
3242
|
actions={
|
|
3122
3243
|
<SectionIconToggle
|
|
3123
|
-
label={"Absolute position" /* i18n-ignore
|
|
3244
|
+
label={"Absolute position" /* i18n-ignore design inspector action */}
|
|
3124
3245
|
active={constrainedPosition}
|
|
3125
3246
|
onClick={() =>
|
|
3126
3247
|
onStyleChange(
|
|
@@ -3135,7 +3256,7 @@ function PositionLayoutProperties({
|
|
|
3135
3256
|
>
|
|
3136
3257
|
<div className="space-y-1.5">
|
|
3137
3258
|
<SubsectionLabel>
|
|
3138
|
-
{"Alignment" /* i18n-ignore
|
|
3259
|
+
{"Alignment" /* i18n-ignore design inspector label */}
|
|
3139
3260
|
</SubsectionLabel>
|
|
3140
3261
|
<div className="flex items-center gap-3">
|
|
3141
3262
|
<InspectorSegment>
|
|
@@ -3227,13 +3348,19 @@ function PositionLayoutProperties({
|
|
|
3227
3348
|
<InspectorSegment>
|
|
3228
3349
|
<InspectorIconButton
|
|
3229
3350
|
label={t("editPanel.labels.flipHorizontal")}
|
|
3230
|
-
onClick={() =>
|
|
3351
|
+
onClick={() => {
|
|
3352
|
+
const [sx, sy] = parseScaleValue(styles.scale);
|
|
3353
|
+
onStyleChange("scale", `${sx === -1 ? 1 : -1} ${sy}`);
|
|
3354
|
+
}}
|
|
3231
3355
|
>
|
|
3232
3356
|
<IconFlipHorizontal className="size-4" />
|
|
3233
3357
|
</InspectorIconButton>
|
|
3234
3358
|
<InspectorIconButton
|
|
3235
3359
|
label={t("editPanel.labels.flipVertical")}
|
|
3236
|
-
onClick={() =>
|
|
3360
|
+
onClick={() => {
|
|
3361
|
+
const [sx, sy] = parseScaleValue(styles.scale);
|
|
3362
|
+
onStyleChange("scale", `${sx} ${sy === -1 ? 1 : -1}`);
|
|
3363
|
+
}}
|
|
3237
3364
|
>
|
|
3238
3365
|
<IconFlipVertical className="size-4" />
|
|
3239
3366
|
</InspectorIconButton>
|
|
@@ -3275,13 +3402,13 @@ function FillProperties({
|
|
|
3275
3402
|
isTextElement || colorHasVisibleAlpha(fillValue) || hasBackgroundLayer;
|
|
3276
3403
|
|
|
3277
3404
|
// Non-destructive fill hide: stash the color before hiding so toggling
|
|
3278
|
-
// visible again restores the exact original value (
|
|
3279
|
-
// Keyed by
|
|
3405
|
+
// visible again restores the exact original value (the design editor never loses color).
|
|
3406
|
+
// Keyed by a stable selected-element identity so anonymous same-tag elements
|
|
3280
3407
|
// don't share stash slots.
|
|
3281
3408
|
const [hiddenFillStash, setHiddenFillStash] = useState<
|
|
3282
3409
|
Record<string, string>
|
|
3283
3410
|
>({});
|
|
3284
|
-
const stashKey = `${element
|
|
3411
|
+
const stashKey = `${elementIdentityKey(element)}:${fillProperty}`;
|
|
3285
3412
|
const isHidden = fillValue === "transparent" || fillValue === "";
|
|
3286
3413
|
const handleFillVisibilityToggle = () => {
|
|
3287
3414
|
if (isHidden) {
|
|
@@ -3324,9 +3451,9 @@ function FillProperties({
|
|
|
3324
3451
|
title={t("editPanel.sections.fill")}
|
|
3325
3452
|
actions={
|
|
3326
3453
|
<>
|
|
3327
|
-
{/*
|
|
3454
|
+
{/* design color-styles affordance (grid icon) to the left of "+". */}
|
|
3328
3455
|
<SectionIconButton
|
|
3329
|
-
label={"Styles" /* i18n-ignore
|
|
3456
|
+
label={"Styles" /* i18n-ignore design inspector action */}
|
|
3330
3457
|
>
|
|
3331
3458
|
<IconLayoutGrid className="size-3.5" />
|
|
3332
3459
|
</SectionIconButton>
|
|
@@ -3366,7 +3493,7 @@ function FillProperties({
|
|
|
3366
3493
|
{hasVisibleFill ? (
|
|
3367
3494
|
<div className="space-y-1.5">
|
|
3368
3495
|
{isTextElement || colorHasVisibleAlpha(fillValue) ? (
|
|
3369
|
-
/*
|
|
3496
|
+
/* design row: [swatch+hex trigger (flex-1)] [eye] [remove] */
|
|
3370
3497
|
<div className="group flex items-center gap-1.5">
|
|
3371
3498
|
<div className="min-w-0 flex-1">
|
|
3372
3499
|
<ColorInput
|
|
@@ -3385,6 +3512,13 @@ function FillProperties({
|
|
|
3385
3512
|
: (v) => onStyleChange("backgroundBlendMode", v)
|
|
3386
3513
|
}
|
|
3387
3514
|
documentColors={documentColors}
|
|
3515
|
+
pickerKey={[
|
|
3516
|
+
element.sourceId ??
|
|
3517
|
+
element.id ??
|
|
3518
|
+
element.selector ??
|
|
3519
|
+
element.tagName,
|
|
3520
|
+
fillProperty,
|
|
3521
|
+
].join(":")}
|
|
3388
3522
|
/>
|
|
3389
3523
|
</div>
|
|
3390
3524
|
<SectionIconButton
|
|
@@ -3432,7 +3566,7 @@ function FillProperties({
|
|
|
3432
3566
|
: 100;
|
|
3433
3567
|
const label = gradient
|
|
3434
3568
|
? `${gradientLabel(gradient.type)} ${index + 1}`
|
|
3435
|
-
: `${"Image" /* i18n-ignore
|
|
3569
|
+
: `${"Image" /* i18n-ignore design inspector paint row */} ${
|
|
3436
3570
|
index + 1
|
|
3437
3571
|
}`;
|
|
3438
3572
|
const replaceLayer = (nextLayer: string) => {
|
|
@@ -3452,7 +3586,7 @@ function FillProperties({
|
|
|
3452
3586
|
};
|
|
3453
3587
|
|
|
3454
3588
|
return (
|
|
3455
|
-
/*
|
|
3589
|
+
/* design row: [swatch+label+opacity% trigger (flex-1)] [eye] [remove] */
|
|
3456
3590
|
<div
|
|
3457
3591
|
key={`${layer}-${index}`}
|
|
3458
3592
|
className="group flex items-center gap-1.5"
|
|
@@ -3481,7 +3615,7 @@ function FillProperties({
|
|
|
3481
3615
|
sideOffset={8}
|
|
3482
3616
|
className="w-80 p-0"
|
|
3483
3617
|
>
|
|
3484
|
-
<
|
|
3618
|
+
<DesignColorPicker
|
|
3485
3619
|
value={gradient?.stops[0]?.color ?? layer}
|
|
3486
3620
|
onPaintValueChange={replaceLayer}
|
|
3487
3621
|
onChange={(nextColor) => {
|
|
@@ -3489,10 +3623,14 @@ function FillProperties({
|
|
|
3489
3623
|
const firstStop = gradient.stops[0];
|
|
3490
3624
|
if (!firstStop) return;
|
|
3491
3625
|
replaceLayer(
|
|
3492
|
-
buildGradientLayer(
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3626
|
+
buildGradientLayer(
|
|
3627
|
+
gradient.type,
|
|
3628
|
+
[
|
|
3629
|
+
{ ...firstStop, color: nextColor },
|
|
3630
|
+
...gradient.stops.slice(1),
|
|
3631
|
+
],
|
|
3632
|
+
gradient.prefix,
|
|
3633
|
+
),
|
|
3496
3634
|
);
|
|
3497
3635
|
}}
|
|
3498
3636
|
paintType={gradient?.type ?? "image"}
|
|
@@ -3533,6 +3671,7 @@ function FillProperties({
|
|
|
3533
3671
|
...stop,
|
|
3534
3672
|
opacity: opacity <= 0 ? 100 : 0,
|
|
3535
3673
|
})),
|
|
3674
|
+
gradient.prefix,
|
|
3536
3675
|
),
|
|
3537
3676
|
);
|
|
3538
3677
|
}}
|
|
@@ -3576,6 +3715,11 @@ function StrokeProperties({
|
|
|
3576
3715
|
styles.outlineWidth,
|
|
3577
3716
|
styles.outlineStyle,
|
|
3578
3717
|
);
|
|
3718
|
+
// Render the row whenever a stroke has been configured (non-zero width),
|
|
3719
|
+
// even when its style is "none" (hidden). This mirrors Figma's behavior where
|
|
3720
|
+
// hidden stroke rows remain present so the user can re-show them via the eye icon.
|
|
3721
|
+
const borderExists = cssLengthNumber(styles.borderWidth) > 0;
|
|
3722
|
+
const outlineExists = cssLengthNumber(styles.outlineWidth) > 0;
|
|
3579
3723
|
|
|
3580
3724
|
return (
|
|
3581
3725
|
<PanelSection
|
|
@@ -3640,7 +3784,7 @@ function StrokeProperties({
|
|
|
3640
3784
|
</SectionIconButton>
|
|
3641
3785
|
}
|
|
3642
3786
|
>
|
|
3643
|
-
{
|
|
3787
|
+
{borderExists ? (
|
|
3644
3788
|
<StrokeLayerControl
|
|
3645
3789
|
kind="border"
|
|
3646
3790
|
visible={borderVisible}
|
|
@@ -3657,7 +3801,7 @@ function StrokeProperties({
|
|
|
3657
3801
|
}}
|
|
3658
3802
|
/>
|
|
3659
3803
|
) : null}
|
|
3660
|
-
{
|
|
3804
|
+
{outlineExists ? (
|
|
3661
3805
|
<StrokeLayerControl
|
|
3662
3806
|
kind="outline"
|
|
3663
3807
|
visible={outlineVisible}
|
|
@@ -3696,10 +3840,10 @@ function AppearanceProperties({
|
|
|
3696
3840
|
title={t("root.commandAppearance")}
|
|
3697
3841
|
actions={
|
|
3698
3842
|
<>
|
|
3699
|
-
{/* Opacity / blend-mode affordance — matches
|
|
3843
|
+
{/* Opacity / blend-mode affordance — matches the design editor's pill icon */}
|
|
3700
3844
|
<SectionIconButton
|
|
3701
3845
|
label={
|
|
3702
|
-
"Opacity & blend mode" /* i18n-ignore
|
|
3846
|
+
"Opacity & blend mode" /* i18n-ignore design inspector action */
|
|
3703
3847
|
}
|
|
3704
3848
|
>
|
|
3705
3849
|
<IconSlice className="size-3.5" />
|
|
@@ -3708,8 +3852,8 @@ function AppearanceProperties({
|
|
|
3708
3852
|
<SectionIconToggle
|
|
3709
3853
|
label={
|
|
3710
3854
|
hidden
|
|
3711
|
-
? "Show" /* i18n-ignore
|
|
3712
|
-
: "Hide" /* i18n-ignore
|
|
3855
|
+
? "Show" /* i18n-ignore design inspector action */
|
|
3856
|
+
: "Hide" /* i18n-ignore design inspector action */
|
|
3713
3857
|
}
|
|
3714
3858
|
active={hidden}
|
|
3715
3859
|
onClick={() =>
|
|
@@ -3722,9 +3866,9 @@ function AppearanceProperties({
|
|
|
3722
3866
|
<IconEye className="size-3.5" />
|
|
3723
3867
|
)}
|
|
3724
3868
|
</SectionIconToggle>
|
|
3725
|
-
{/* Styles / fill library affordance — matches
|
|
3869
|
+
{/* Styles / fill library affordance — matches the design editor's droplet icon */}
|
|
3726
3870
|
<SectionIconButton
|
|
3727
|
-
label={"Styles" /* i18n-ignore
|
|
3871
|
+
label={"Styles" /* i18n-ignore design inspector action */}
|
|
3728
3872
|
>
|
|
3729
3873
|
<IconDroplet className="size-3.5" />
|
|
3730
3874
|
</SectionIconButton>
|
|
@@ -3787,7 +3931,7 @@ function EffectsProperties({
|
|
|
3787
3931
|
const t = useT();
|
|
3788
3932
|
const styles = element.computedStyles;
|
|
3789
3933
|
const blurValue = readBlurFilter(styles.filter);
|
|
3790
|
-
// M5 · Background (backdrop) blur is a distinct
|
|
3934
|
+
// M5 · Background (backdrop) blur is a distinct design effect type, backed by
|
|
3791
3935
|
// CSS `backdrop-filter: blur()` (vs layer blur's `filter: blur()`).
|
|
3792
3936
|
const backdropBlurValue = readBlurFilter(
|
|
3793
3937
|
styles.backdropFilter || styles.webkitBackdropFilter,
|
|
@@ -3842,7 +3986,7 @@ function EffectsProperties({
|
|
|
3842
3986
|
onSelect={addBackgroundBlur}
|
|
3843
3987
|
>
|
|
3844
3988
|
<IconBackground className="size-3.5" />
|
|
3845
|
-
{"Background blur" /* i18n-ignore
|
|
3989
|
+
{"Background blur" /* i18n-ignore design effect type */}
|
|
3846
3990
|
</DropdownMenuItem>
|
|
3847
3991
|
</DropdownMenuContent>
|
|
3848
3992
|
</DropdownMenu>
|
|
@@ -3873,7 +4017,7 @@ function EffectsProperties({
|
|
|
3873
4017
|
</div>
|
|
3874
4018
|
) : null}
|
|
3875
4019
|
{blurValue > 0 ? (
|
|
3876
|
-
/*
|
|
4020
|
+
/* design effect row for layer blur: flat row matching shadow rows */
|
|
3877
4021
|
<Popover>
|
|
3878
4022
|
<div className="group flex items-center gap-1.5">
|
|
3879
4023
|
<PopoverTrigger asChild>
|
|
@@ -3907,12 +4051,14 @@ function EffectsProperties({
|
|
|
3907
4051
|
<ScrubInput
|
|
3908
4052
|
label={t("editPanel.labels.blur")}
|
|
3909
4053
|
value={blurValue}
|
|
3910
|
-
onChange={(value) =>
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
4054
|
+
onChange={(value) => {
|
|
4055
|
+
const blurFn = `blur(${Math.max(0, Math.round(value))}px)`;
|
|
4056
|
+
const existing = styles.filter || "";
|
|
4057
|
+
const next = existing.includes("blur(")
|
|
4058
|
+
? existing.replace(/blur\([^)]*\)/, blurFn)
|
|
4059
|
+
: blurFn;
|
|
4060
|
+
onStyleChange("filter", next);
|
|
4061
|
+
}}
|
|
3916
4062
|
unit="px"
|
|
3917
4063
|
min={0}
|
|
3918
4064
|
precision={1}
|
|
@@ -3932,7 +4078,7 @@ function EffectsProperties({
|
|
|
3932
4078
|
className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
|
|
3933
4079
|
>
|
|
3934
4080
|
<span className="min-w-0 flex-1 truncate font-medium text-foreground">
|
|
3935
|
-
{"Background blur" /* i18n-ignore
|
|
4081
|
+
{"Background blur" /* i18n-ignore design effect type */}
|
|
3936
4082
|
</span>
|
|
3937
4083
|
<span className="shrink-0 tabular-nums text-muted-foreground">
|
|
3938
4084
|
{Math.round(backdropBlurValue)}px
|
|
@@ -4007,7 +4153,7 @@ function selectionColorValues(element: ElementInfo): SelectionColorValue[] {
|
|
|
4007
4153
|
});
|
|
4008
4154
|
}
|
|
4009
4155
|
|
|
4010
|
-
/** Uppercase 6-char hex (no #) for a CSS color, matching
|
|
4156
|
+
/** Uppercase 6-char hex (no #) for a CSS color, matching the design editor's row readout. */
|
|
4011
4157
|
function selectionDisplayHex(value: string): string {
|
|
4012
4158
|
const parsed = parseCssColor(value);
|
|
4013
4159
|
if (!parsed) return value.replace(/^#/, "").toUpperCase();
|
|
@@ -4021,7 +4167,7 @@ function SelectionColorsProperties({
|
|
|
4021
4167
|
element: ElementInfo;
|
|
4022
4168
|
onStyleChange: (property: string, value: string) => void;
|
|
4023
4169
|
}) {
|
|
4024
|
-
// M6 ·
|
|
4170
|
+
// M6 · the design editor's Selection colors collapses to a single "Show selection colors"
|
|
4025
4171
|
// affordance, expanding to one editable [swatch · hex · opacity] row per
|
|
4026
4172
|
// unique color — matching the Fill row grammar instead of a swatch strip.
|
|
4027
4173
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -4030,7 +4176,7 @@ function SelectionColorsProperties({
|
|
|
4030
4176
|
|
|
4031
4177
|
return (
|
|
4032
4178
|
<PanelSection
|
|
4033
|
-
title={"Selection colors" /* i18n-ignore
|
|
4179
|
+
title={"Selection colors" /* i18n-ignore design inspector label */}
|
|
4034
4180
|
>
|
|
4035
4181
|
{expanded ? (
|
|
4036
4182
|
<div className="space-y-1.5">
|
|
@@ -4063,7 +4209,7 @@ function SelectionColorsProperties({
|
|
|
4063
4209
|
sideOffset={8}
|
|
4064
4210
|
className="w-80 p-0"
|
|
4065
4211
|
>
|
|
4066
|
-
<
|
|
4212
|
+
<DesignColorPicker
|
|
4067
4213
|
value={cssColorOrFallback(color.value, "#000000")}
|
|
4068
4214
|
onChange={(value) => onStyleChange(color.property, value)}
|
|
4069
4215
|
/>
|
|
@@ -4079,7 +4225,7 @@ function SelectionColorsProperties({
|
|
|
4079
4225
|
onClick={() => setExpanded(true)}
|
|
4080
4226
|
>
|
|
4081
4227
|
<span className="truncate">
|
|
4082
|
-
{"Show selection colors" /* i18n-ignore
|
|
4228
|
+
{"Show selection colors" /* i18n-ignore design inspector label */}
|
|
4083
4229
|
</span>
|
|
4084
4230
|
<div className="flex shrink-0 items-center -space-x-1">
|
|
4085
4231
|
{colors.slice(0, 3).map((color, index) => (
|
|
@@ -4130,12 +4276,13 @@ export function EditPanel({
|
|
|
4130
4276
|
exporting = false,
|
|
4131
4277
|
}: EditPanelProps) {
|
|
4132
4278
|
const t = useT();
|
|
4133
|
-
const [exportSettings, setExportSettings] = useState<ExportSettingsValue>(
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
suffix: "",
|
|
4137
|
-
});
|
|
4279
|
+
const [exportSettings, setExportSettings] = useState<ExportSettingsValue>(
|
|
4280
|
+
DEFAULT_EXPORT_SETTINGS,
|
|
4281
|
+
);
|
|
4138
4282
|
const [showExportPreview, setShowExportPreview] = useState(false);
|
|
4283
|
+
const selectedElementKey = selectedElement
|
|
4284
|
+
? elementIdentityKey(selectedElement)
|
|
4285
|
+
: "none";
|
|
4139
4286
|
const isTextElement = selectedElement
|
|
4140
4287
|
? TEXT_TAGS.has(selectedElement.tagName)
|
|
4141
4288
|
: false;
|
|
@@ -4150,6 +4297,11 @@ export function EditPanel({
|
|
|
4150
4297
|
[onTweakChange],
|
|
4151
4298
|
);
|
|
4152
4299
|
|
|
4300
|
+
useEffect(() => {
|
|
4301
|
+
setExportSettings(DEFAULT_EXPORT_SETTINGS);
|
|
4302
|
+
setShowExportPreview(false);
|
|
4303
|
+
}, [selectedElementKey]);
|
|
4304
|
+
|
|
4153
4305
|
return (
|
|
4154
4306
|
<div
|
|
4155
4307
|
className={cn(
|
|
@@ -4231,8 +4383,8 @@ export function EditPanel({
|
|
|
4231
4383
|
<SectionIconToggle
|
|
4232
4384
|
label={
|
|
4233
4385
|
showExportPreview
|
|
4234
|
-
? "Hide preview" /* i18n-ignore
|
|
4235
|
-
: "Show preview" /* i18n-ignore
|
|
4386
|
+
? "Hide preview" /* i18n-ignore design inspector action */
|
|
4387
|
+
: "Show preview" /* i18n-ignore design inspector action */
|
|
4236
4388
|
}
|
|
4237
4389
|
active={showExportPreview}
|
|
4238
4390
|
onClick={() => setShowExportPreview((shown) => !shown)}
|
|
@@ -4242,6 +4394,7 @@ export function EditPanel({
|
|
|
4242
4394
|
}
|
|
4243
4395
|
>
|
|
4244
4396
|
<ExportSettingsPanel
|
|
4397
|
+
key={selectedElementKey}
|
|
4245
4398
|
value={exportSettings}
|
|
4246
4399
|
formats={["png", "svg"]}
|
|
4247
4400
|
exporting={exporting}
|