@agent-native/core 0.80.8 → 0.80.10
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 +19 -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/design-connect.ts +28 -2
- package/corpus/core/src/cli/mcp.ts +10 -0
- package/corpus/core/src/cli/skills.ts +103 -36
- 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/DESIGN-STUDIO-PLAN.md +717 -0
- package/corpus/templates/design/actions/add-breakpoint.ts +143 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +5 -0
- package/corpus/templates/design/actions/apply-a11y-fix.ts +317 -0
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +433 -0
- package/corpus/templates/design/actions/apply-design-state.ts +200 -0
- package/corpus/templates/design/actions/apply-design-token-edit.ts +202 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +450 -0
- package/corpus/templates/design/actions/apply-shader-fill.ts +404 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +214 -8
- package/corpus/templates/design/actions/capture-design-state.ts +224 -0
- package/corpus/templates/design/actions/connect-builder-app.ts +162 -0
- package/corpus/templates/design/actions/create-component.ts +447 -0
- package/corpus/templates/design/actions/create-design-branch.ts +263 -0
- package/corpus/templates/design/actions/create-design-state.ts +162 -0
- package/corpus/templates/design/actions/delete-design-state.ts +55 -0
- package/corpus/templates/design/actions/deploy-design-preview.ts +275 -0
- 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-component-details.ts +242 -0
- package/corpus/templates/design/actions/get-design-branch-diff.ts +362 -0
- package/corpus/templates/design/actions/get-design-review.ts +314 -0
- package/corpus/templates/design/actions/get-design-surface-index.ts +582 -0
- package/corpus/templates/design/actions/get-design.ts +1 -0
- package/corpus/templates/design/actions/get-motion-timeline.ts +99 -0
- package/corpus/templates/design/actions/index-components.ts +258 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +273 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +309 -0
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +153 -0
- package/corpus/templates/design/actions/list-design-states.ts +72 -0
- package/corpus/templates/design/actions/migrate-inline-design-to-app.ts +298 -0
- package/corpus/templates/design/actions/open-component-source.ts +237 -0
- package/corpus/templates/design/actions/present-design-variants.ts +355 -30
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +254 -0
- package/corpus/templates/design/actions/preview-design-token-edit.ts +113 -0
- package/corpus/templates/design/actions/preview-shader-fill.ts +189 -0
- package/corpus/templates/design/actions/remove-breakpoint.ts +103 -0
- package/corpus/templates/design/actions/remove-motion-timeline.ts +196 -0
- package/corpus/templates/design/actions/run-design-audit.ts +421 -0
- package/corpus/templates/design/actions/run-design-extension-action.ts +284 -0
- package/corpus/templates/design/actions/set-active-breakpoint.ts +39 -0
- package/corpus/templates/design/actions/view-screen.ts +75 -31
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1738 -135
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +1 -1
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +632 -43
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1873 -132
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +384 -219
- package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +157 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1062 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2103 -295
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +83 -206
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +759 -0
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +579 -0
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +573 -0
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +1 -1
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +241 -0
- package/corpus/templates/design/app/components/design/index.ts +16 -1
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +238 -137
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +188 -14
- 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/InspectorAiActions.tsx +145 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +42 -12
- 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/components/visual-editor/DrawOverlay.tsx +1 -1
- 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/useAgentEditRequest.ts +131 -0
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +11 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +34 -0
- package/corpus/templates/design/app/i18n-data.ts +314 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +4929 -892
- package/corpus/templates/design/app/root.tsx +1 -1
- package/corpus/templates/design/changelog/2026-06-29-added-a-review-panel-in-the-design-editor-s-inspector-with-a.md +6 -0
- 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-copying-and-pasting-layers-no-longer-shows-success-notificat.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-device-presets-in-all-screens-view-resize-the-selected-previ.md +6 -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-and-canvas-drags-keep-the-layer-list-stable-while-chan.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-moves-can-be-undone-and-redone-without-flashing-the-ca.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-screen-previews-use-a-single-blue-hover-border-in-all-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-selected-containers-show-draggable-padding-and-gap-guides-on.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-adds-a-studio-layer-with-tokens-respon.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/changelog/2026-06-30-accessibility-findings-now-offer-a-one-click-fix.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-instances-now-have-editable-props-in-the-inspe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-inspect-code-now-shows-the-elements-opening-tag-at-a-gla.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-shader-fill-presets-can-now-be-applied-to-an-element.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-can-now-add-a-track-to-any-element-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-editor-selection-layer-paint-and-drawing-controls-are-more-reliable.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 +128 -11
- package/corpus/templates/design/playwright.config.ts +9 -3
- package/corpus/templates/design/server/db/schema.ts +123 -0
- 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/plugins/db.ts +90 -0
- package/corpus/templates/design/server/routes/api/design-handoff/[id].zip.get.ts +86 -0
- package/corpus/templates/design/shared/builder-app.ts +297 -0
- package/corpus/templates/design/shared/capability-resolver.ts +123 -0
- package/corpus/templates/design/shared/capture-sanitize.ts +70 -0
- package/corpus/templates/design/shared/code-layer.ts +1141 -93
- package/corpus/templates/design/shared/component-model.ts +239 -0
- package/corpus/templates/design/shared/design-review.ts +275 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +286 -0
- package/corpus/templates/design/shared/design-state.ts +112 -0
- package/corpus/templates/design/shared/design-surface-index.ts +258 -0
- package/corpus/templates/design/shared/generation-session.ts +41 -0
- package/corpus/templates/design/shared/motion-compiler.ts +278 -0
- package/corpus/templates/design/shared/motion-timeline.ts +193 -0
- package/corpus/templates/design/shared/responsive-classes.ts +452 -0
- package/corpus/templates/design/shared/shader-fill.ts +323 -0
- package/corpus/templates/design/shared/source-mode.ts +245 -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/CanvasArea.tsx +6 -2
- 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-canvases-open-without-an-initial-pan-and-zoom-flicker.md +6 -0
- 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/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +28 -2
- package/dist/cli/design-connect.js.map +1 -1
- 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 +95 -33
- 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/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/observability/routes.d.ts +3 -3
- 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/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/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
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
IconLock,
|
|
8
8
|
IconLockOpen,
|
|
9
9
|
IconLayoutGrid,
|
|
10
|
+
IconPencil,
|
|
10
11
|
IconPlus,
|
|
11
12
|
IconSearch,
|
|
12
13
|
} from "@tabler/icons-react";
|
|
13
14
|
import {
|
|
14
15
|
useCallback,
|
|
15
16
|
useEffect,
|
|
17
|
+
useLayoutEffect,
|
|
16
18
|
useMemo,
|
|
17
19
|
useRef,
|
|
18
20
|
useState,
|
|
@@ -23,6 +25,13 @@ import {
|
|
|
23
25
|
} from "react";
|
|
24
26
|
|
|
25
27
|
import { Button } from "@/components/ui/button";
|
|
28
|
+
import {
|
|
29
|
+
ContextMenu,
|
|
30
|
+
ContextMenuContent,
|
|
31
|
+
ContextMenuItem,
|
|
32
|
+
ContextMenuSeparator,
|
|
33
|
+
ContextMenuTrigger,
|
|
34
|
+
} from "@/components/ui/context-menu";
|
|
26
35
|
import { Input } from "@/components/ui/input";
|
|
27
36
|
import {
|
|
28
37
|
Tooltip,
|
|
@@ -31,6 +40,9 @@ import {
|
|
|
31
40
|
} from "@/components/ui/tooltip";
|
|
32
41
|
import { cn } from "@/lib/utils";
|
|
33
42
|
|
|
43
|
+
const LAYER_ROW_BASE_INDENT = 4;
|
|
44
|
+
const LAYER_ROW_DEPTH_INDENT = 18;
|
|
45
|
+
|
|
34
46
|
export type LayersPanelNodeType =
|
|
35
47
|
| "file"
|
|
36
48
|
| "screen"
|
|
@@ -94,6 +106,7 @@ export interface LayersPanelSelectionIntent {
|
|
|
94
106
|
id: string;
|
|
95
107
|
selectedIds: string[];
|
|
96
108
|
additive: boolean;
|
|
109
|
+
currentSelectedIds?: string[];
|
|
97
110
|
range: boolean;
|
|
98
111
|
source: "keyboard" | "pointer";
|
|
99
112
|
}
|
|
@@ -107,6 +120,7 @@ export interface LayersPanelMoveIntent {
|
|
|
107
120
|
export interface LayersPanelLabels {
|
|
108
121
|
title: string;
|
|
109
122
|
screens: string;
|
|
123
|
+
allScreens: string;
|
|
110
124
|
screenOverview: string;
|
|
111
125
|
addScreen: string;
|
|
112
126
|
searchPlaceholder: string;
|
|
@@ -128,6 +142,7 @@ export interface LayersPanelLabels {
|
|
|
128
142
|
export interface LayersPanelProps {
|
|
129
143
|
screens?: LayersPanelFile[];
|
|
130
144
|
activeScreenId?: string;
|
|
145
|
+
screenOverviewActive?: boolean;
|
|
131
146
|
files?: LayersPanelFile[];
|
|
132
147
|
layers?: LayersPanelNode[];
|
|
133
148
|
codeLayers?: LayersPanelNode[];
|
|
@@ -185,6 +200,7 @@ const SECTION_ELEMENT_ID = "__design_layers_elements__";
|
|
|
185
200
|
// Module-level drag state: dataTransfer.getData() returns "" during dragover
|
|
186
201
|
// per spec; the source row stores the drag payload here on dragstart instead.
|
|
187
202
|
let activeDragState: { sourceId: string; draggedIds: string[] } | null = null;
|
|
203
|
+
let activeDropIntent: LayersPanelMoveIntent | null = null;
|
|
188
204
|
|
|
189
205
|
const ROW_BASE_INDENT = 4;
|
|
190
206
|
const ROW_INDENT_STEP = 28;
|
|
@@ -198,6 +214,7 @@ function defaultLabels(t: ReturnType<typeof useT>): LayersPanelLabels {
|
|
|
198
214
|
return {
|
|
199
215
|
title: t("layersPanel.title"),
|
|
200
216
|
screens: t("layersPanel.screens"),
|
|
217
|
+
allScreens: t("layersPanel.allScreens"),
|
|
201
218
|
screenOverview: t("designEditor.screenOverview"),
|
|
202
219
|
addScreen: t("layersPanel.addScreen"),
|
|
203
220
|
searchPlaceholder: t("layersPanel.searchPlaceholder"),
|
|
@@ -406,6 +423,7 @@ function layerCanShowBadge(node: LayersPanelNode) {
|
|
|
406
423
|
export function LayersPanel({
|
|
407
424
|
screens,
|
|
408
425
|
activeScreenId,
|
|
426
|
+
screenOverviewActive = false,
|
|
409
427
|
files,
|
|
410
428
|
layers,
|
|
411
429
|
codeLayers,
|
|
@@ -432,6 +450,7 @@ export function LayersPanel({
|
|
|
432
450
|
const t = useT();
|
|
433
451
|
const labels = useMemo(() => mergeLabels(labelsProp, t), [labelsProp, t]);
|
|
434
452
|
const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
|
|
453
|
+
const selectedIdsRef = useRef<readonly string[]>(selectedIds);
|
|
435
454
|
const expandedIdSet = useMemo(() => new Set(expandedIds), [expandedIds]);
|
|
436
455
|
const lastSelectionAnchorRef = useRef<string | null>(selectedIds[0] ?? null);
|
|
437
456
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
@@ -467,6 +486,10 @@ export function LayersPanel({
|
|
|
467
486
|
[roots, selectedIdSet],
|
|
468
487
|
);
|
|
469
488
|
|
|
489
|
+
useLayoutEffect(() => {
|
|
490
|
+
selectedIdsRef.current = selectedIds;
|
|
491
|
+
}, [selectedIds]);
|
|
492
|
+
|
|
470
493
|
useEffect(() => {
|
|
471
494
|
if (selectedAncestorIds.length === 0) return;
|
|
472
495
|
const next = new Set(expandedIds);
|
|
@@ -513,10 +536,14 @@ export function LayersPanel({
|
|
|
513
536
|
id: string,
|
|
514
537
|
options: {
|
|
515
538
|
additive: boolean;
|
|
539
|
+
currentSelectedIds?: string[];
|
|
516
540
|
range: boolean;
|
|
517
541
|
source: "keyboard" | "pointer";
|
|
518
542
|
},
|
|
519
543
|
) => {
|
|
544
|
+
const currentSelectedIds =
|
|
545
|
+
options.currentSelectedIds ?? selectedIdsRef.current;
|
|
546
|
+
const currentSelectedIdSet = new Set(currentSelectedIds);
|
|
520
547
|
let nextIds: string[];
|
|
521
548
|
if (options.range && lastSelectionAnchorRef.current) {
|
|
522
549
|
let anchor = lastSelectionAnchorRef.current;
|
|
@@ -538,19 +565,18 @@ export function LayersPanel({
|
|
|
538
565
|
const [start, end] = from < to ? [from, to] : [to, from];
|
|
539
566
|
const rangeIds = selectableVisibleIds.slice(start, end + 1);
|
|
540
567
|
nextIds = options.additive
|
|
541
|
-
? Array.from(new Set([...
|
|
568
|
+
? Array.from(new Set([...currentSelectedIds, ...rangeIds]))
|
|
542
569
|
: rangeIds;
|
|
543
570
|
} else {
|
|
544
571
|
nextIds = [id];
|
|
545
572
|
}
|
|
546
573
|
} else if (options.additive) {
|
|
547
|
-
nextIds =
|
|
548
|
-
?
|
|
549
|
-
: [...
|
|
574
|
+
nextIds = currentSelectedIdSet.has(id)
|
|
575
|
+
? currentSelectedIds.filter((selectedId) => selectedId !== id)
|
|
576
|
+
: [...currentSelectedIds, id];
|
|
550
577
|
} else {
|
|
551
578
|
nextIds = [id];
|
|
552
579
|
}
|
|
553
|
-
|
|
554
580
|
// Only advance the anchor on plain clicks; Shift+clicks extend from the
|
|
555
581
|
// existing anchor so the pivot stays fixed across consecutive range clicks.
|
|
556
582
|
if (!options.range) {
|
|
@@ -558,7 +584,7 @@ export function LayersPanel({
|
|
|
558
584
|
}
|
|
559
585
|
onSelectionChange(nextIds, { id, selectedIds: nextIds, ...options });
|
|
560
586
|
},
|
|
561
|
-
[onSelectionChange, selectableVisibleIds
|
|
587
|
+
[onSelectionChange, selectableVisibleIds],
|
|
562
588
|
);
|
|
563
589
|
|
|
564
590
|
const commitRename = useCallback(
|
|
@@ -642,12 +668,6 @@ export function LayersPanel({
|
|
|
642
668
|
{labels.screens}
|
|
643
669
|
</h2>
|
|
644
670
|
<div className="flex items-center gap-0.5 text-muted-foreground">
|
|
645
|
-
<IconTooltipButton
|
|
646
|
-
label={labels.screenOverview}
|
|
647
|
-
onClick={onScreenOverview}
|
|
648
|
-
>
|
|
649
|
-
<IconLayoutGrid className="size-3.5" />
|
|
650
|
-
</IconTooltipButton>
|
|
651
671
|
<IconTooltipButton
|
|
652
672
|
label={labels.searchPlaceholder}
|
|
653
673
|
onClick={openSearch}
|
|
@@ -663,9 +683,30 @@ export function LayersPanel({
|
|
|
663
683
|
</IconTooltipButton>
|
|
664
684
|
</div>
|
|
665
685
|
</div>
|
|
686
|
+
<div className="px-2">
|
|
687
|
+
<button
|
|
688
|
+
type="button"
|
|
689
|
+
className={cn(
|
|
690
|
+
"flex h-8 w-full cursor-default items-center gap-2 rounded-[5px] px-2 text-left text-[12px] font-semibold outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
691
|
+
screenOverviewActive
|
|
692
|
+
? "bg-[var(--design-editor-active-row-color)] text-foreground"
|
|
693
|
+
: "text-foreground/85 hover:bg-[var(--design-editor-active-row-color)] hover:text-foreground",
|
|
694
|
+
)}
|
|
695
|
+
aria-current={screenOverviewActive ? "page" : undefined}
|
|
696
|
+
onClick={() => onScreenOverview?.()}
|
|
697
|
+
title={labels.allScreens}
|
|
698
|
+
>
|
|
699
|
+
<IconLayoutGrid className="size-4 shrink-0" />
|
|
700
|
+
<span className="min-w-0 flex-1 truncate">
|
|
701
|
+
{labels.allScreens}
|
|
702
|
+
</span>
|
|
703
|
+
</button>
|
|
704
|
+
</div>
|
|
705
|
+
<div className="mx-3 my-2 border-t border-[var(--design-editor-panel-divider-color)]" />
|
|
666
706
|
<div className="space-y-0.5 px-2">
|
|
667
707
|
{screenRows.map((screen) => {
|
|
668
|
-
const isActive =
|
|
708
|
+
const isActive =
|
|
709
|
+
!screenOverviewActive && screen.id === activeScreenId;
|
|
669
710
|
return (
|
|
670
711
|
<button
|
|
671
712
|
key={screen.id}
|
|
@@ -676,6 +717,7 @@ export function LayersPanel({
|
|
|
676
717
|
? "bg-[var(--design-editor-active-row-color)] text-foreground"
|
|
677
718
|
: "text-foreground/85 hover:bg-[var(--design-editor-active-row-color)] hover:text-foreground",
|
|
678
719
|
)}
|
|
720
|
+
aria-current={isActive ? "page" : undefined}
|
|
679
721
|
onClick={() => onScreenSelect?.(screen.id)}
|
|
680
722
|
title={screen.filename ?? screen.name}
|
|
681
723
|
>
|
|
@@ -740,9 +782,13 @@ export function LayersPanel({
|
|
|
740
782
|
</div>
|
|
741
783
|
) : null}
|
|
742
784
|
|
|
743
|
-
<div className="min-h-0 flex-1 overflow-
|
|
785
|
+
<div className="min-h-0 flex-1 overflow-auto overscroll-contain py-2">
|
|
744
786
|
{visibleRows.length ? (
|
|
745
|
-
<div
|
|
787
|
+
<div
|
|
788
|
+
className="w-max min-w-full px-2"
|
|
789
|
+
role="tree"
|
|
790
|
+
aria-label={labels.title}
|
|
791
|
+
>
|
|
746
792
|
{visibleRows.map((row) => (
|
|
747
793
|
<LayerRow
|
|
748
794
|
key={row.rowKey}
|
|
@@ -766,6 +812,7 @@ export function LayersPanel({
|
|
|
766
812
|
onCommitRename={commitRename}
|
|
767
813
|
onCancelRename={() => setRenamingId(null)}
|
|
768
814
|
onStartRename={startRename}
|
|
815
|
+
onRename={onRename}
|
|
769
816
|
onSelect={selectNode}
|
|
770
817
|
onToggleExpanded={(expanded) =>
|
|
771
818
|
onExpandedIdsChange(
|
|
@@ -809,6 +856,7 @@ function LayerRow({
|
|
|
809
856
|
onCommitRename,
|
|
810
857
|
onCancelRename,
|
|
811
858
|
onStartRename,
|
|
859
|
+
onRename,
|
|
812
860
|
onSelect,
|
|
813
861
|
onToggleExpanded,
|
|
814
862
|
onToggleLocked,
|
|
@@ -835,10 +883,12 @@ function LayerRow({
|
|
|
835
883
|
onCommitRename: (id: string) => void;
|
|
836
884
|
onCancelRename: () => void;
|
|
837
885
|
onStartRename: (node: LayersPanelNode) => void;
|
|
886
|
+
onRename?: (id: string, name: string) => void;
|
|
838
887
|
onSelect: (
|
|
839
888
|
id: string,
|
|
840
889
|
options: {
|
|
841
890
|
additive: boolean;
|
|
891
|
+
currentSelectedIds?: string[];
|
|
842
892
|
range: boolean;
|
|
843
893
|
source: "keyboard" | "pointer";
|
|
844
894
|
},
|
|
@@ -859,17 +909,39 @@ function LayerRow({
|
|
|
859
909
|
const selectable = node.selectable !== false;
|
|
860
910
|
const lockable = node.lockable !== false && Boolean(onToggleLocked);
|
|
861
911
|
const hideable = node.hideable !== false && Boolean(onToggleHidden);
|
|
862
|
-
const
|
|
912
|
+
const dragEligible = selectable && !node.locked && !node.hidden;
|
|
913
|
+
const draggable = dragEligible && Boolean(onMoveLayer);
|
|
914
|
+
const canDropInside = layerCanDropInside(node, hasChildren);
|
|
863
915
|
const activeDrop =
|
|
864
916
|
dropIndicator?.targetId === node.id ? dropIndicator.placement : null;
|
|
865
917
|
// Tracks whether the user pressed Escape to cancel rename so that the
|
|
866
918
|
// subsequent blur event does not commit the edit.
|
|
867
919
|
const renameCancelledRef = useRef(false);
|
|
868
920
|
|
|
921
|
+
const readSelectedIdsFromTree = (target: HTMLElement): string[] => {
|
|
922
|
+
const tree = target.closest('[role="tree"]');
|
|
923
|
+
if (!tree) return selectedIds.filter((id) => !id.startsWith("__"));
|
|
924
|
+
return Array.from(
|
|
925
|
+
tree.querySelectorAll<HTMLElement>(
|
|
926
|
+
'[role="treeitem"][aria-selected="true"] [data-layer-row-button][data-layer-node-id]',
|
|
927
|
+
),
|
|
928
|
+
)
|
|
929
|
+
.map((button) => button.dataset.layerNodeId)
|
|
930
|
+
.filter((id): id is string => Boolean(id && !id.startsWith("__")));
|
|
931
|
+
};
|
|
932
|
+
|
|
869
933
|
const handlePointerSelect = (event: MouseEvent<HTMLButtonElement>) => {
|
|
870
934
|
if (!selectable) return;
|
|
935
|
+
if (event.detail === 0) return;
|
|
936
|
+
const nativeEvent = event.nativeEvent;
|
|
937
|
+
const additive =
|
|
938
|
+
event.metaKey ||
|
|
939
|
+
event.ctrlKey ||
|
|
940
|
+
nativeEvent.metaKey ||
|
|
941
|
+
nativeEvent.ctrlKey;
|
|
871
942
|
onSelect(node.id, {
|
|
872
|
-
additive
|
|
943
|
+
additive,
|
|
944
|
+
currentSelectedIds: readSelectedIdsFromTree(event.currentTarget),
|
|
873
945
|
range: event.shiftKey,
|
|
874
946
|
source: "pointer",
|
|
875
947
|
});
|
|
@@ -900,11 +972,12 @@ function LayerRow({
|
|
|
900
972
|
(visibleRow) => visibleRow.rowKey === row.rowKey,
|
|
901
973
|
);
|
|
902
974
|
|
|
903
|
-
if (event.key === "Enter" || event.key === " ") {
|
|
975
|
+
if (event.key === "Enter" || event.key === " " || event.key === "Space") {
|
|
904
976
|
event.preventDefault();
|
|
905
977
|
if (!selectable) return;
|
|
906
978
|
onSelect(node.id, {
|
|
907
979
|
additive: event.metaKey || event.ctrlKey,
|
|
980
|
+
currentSelectedIds: readSelectedIdsFromTree(event.currentTarget),
|
|
908
981
|
range: event.shiftKey,
|
|
909
982
|
source: "keyboard",
|
|
910
983
|
});
|
|
@@ -989,7 +1062,7 @@ function LayerRow({
|
|
|
989
1062
|
};
|
|
990
1063
|
|
|
991
1064
|
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
|
|
992
|
-
if (!onMoveLayer || !
|
|
1065
|
+
if (!onMoveLayer || !dragEligible) return;
|
|
993
1066
|
// dataTransfer.getData() always returns "" during dragover per spec.
|
|
994
1067
|
// Read from the module-level activeDragState set in handleDragStart instead.
|
|
995
1068
|
if (!activeDragState) return;
|
|
@@ -1002,16 +1075,17 @@ function LayerRow({
|
|
|
1002
1075
|
const intent = {
|
|
1003
1076
|
draggedIds: cleanedIds,
|
|
1004
1077
|
targetId: node.id,
|
|
1005
|
-
placement: dropPlacementForEvent(event,
|
|
1078
|
+
placement: dropPlacementForEvent(event, canDropInside),
|
|
1006
1079
|
} satisfies LayersPanelMoveIntent;
|
|
1007
1080
|
if (canMoveLayer && !canMoveLayer(intent)) return;
|
|
1008
1081
|
event.preventDefault();
|
|
1009
1082
|
event.dataTransfer.dropEffect = "move";
|
|
1083
|
+
activeDropIntent = intent;
|
|
1010
1084
|
onDropIndicatorChange(intent);
|
|
1011
1085
|
};
|
|
1012
1086
|
|
|
1013
1087
|
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
|
1014
|
-
if (!onMoveLayer || !
|
|
1088
|
+
if (!onMoveLayer || !dragEligible) {
|
|
1015
1089
|
onDropIndicatorChange(null);
|
|
1016
1090
|
return;
|
|
1017
1091
|
}
|
|
@@ -1035,233 +1109,312 @@ function LayerRow({
|
|
|
1035
1109
|
(id) => id && id !== node.id && !id.startsWith("__"),
|
|
1036
1110
|
);
|
|
1037
1111
|
if (cleanedIds.length > 0) {
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1112
|
+
const storedIntent =
|
|
1113
|
+
activeDropIntent?.targetId === node.id
|
|
1114
|
+
? {
|
|
1115
|
+
...activeDropIntent,
|
|
1116
|
+
draggedIds: activeDropIntent.draggedIds.filter((id) =>
|
|
1117
|
+
cleanedIds.includes(id),
|
|
1118
|
+
),
|
|
1119
|
+
}
|
|
1120
|
+
: null;
|
|
1121
|
+
const intent =
|
|
1122
|
+
storedIntent && storedIntent.draggedIds.length > 0
|
|
1123
|
+
? storedIntent
|
|
1124
|
+
: ({
|
|
1125
|
+
draggedIds: cleanedIds,
|
|
1126
|
+
targetId: node.id,
|
|
1127
|
+
placement: dropPlacementForEvent(event, canDropInside),
|
|
1128
|
+
} satisfies LayersPanelMoveIntent);
|
|
1045
1129
|
if (!canMoveLayer || canMoveLayer(intent)) {
|
|
1046
1130
|
onMoveLayer(intent);
|
|
1047
1131
|
}
|
|
1048
1132
|
}
|
|
1133
|
+
activeDropIntent = null;
|
|
1049
1134
|
onDropIndicatorChange(null);
|
|
1050
1135
|
};
|
|
1051
1136
|
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
role="treeitem"
|
|
1056
|
-
aria-expanded={hasChildren ? isExpanded : undefined}
|
|
1057
|
-
aria-level={depth + 1}
|
|
1058
|
-
aria-selected={selectable ? isSelected : undefined}
|
|
1059
|
-
className="relative"
|
|
1060
|
-
draggable={draggable}
|
|
1061
|
-
onDragStart={handleDragStart}
|
|
1062
|
-
onDragOver={handleDragOver}
|
|
1063
|
-
onDragLeave={(event) => {
|
|
1064
|
-
// Only clear the indicator when the pointer truly leaves this row.
|
|
1065
|
-
// Moving into a child element fires dragleave on the outer div too, so
|
|
1066
|
-
// we suppress the clear when relatedTarget is still within this row.
|
|
1067
|
-
if (event.currentTarget.contains(event.relatedTarget as Node | null))
|
|
1068
|
-
return;
|
|
1069
|
-
onDropIndicatorChange(null);
|
|
1070
|
-
}}
|
|
1071
|
-
onDrop={handleDrop}
|
|
1072
|
-
onDragEnd={() => {
|
|
1073
|
-
activeDragState = null;
|
|
1074
|
-
onDropIndicatorChange(null);
|
|
1075
|
-
}}
|
|
1076
|
-
onMouseEnter={() => onHoverLayer?.(node.id)}
|
|
1077
|
-
onMouseLeave={() => onLeaveLayer?.(node.id)}
|
|
1078
|
-
>
|
|
1079
|
-
{activeDrop === "before" ? (
|
|
1080
|
-
<span className="pointer-events-none absolute left-2 right-2 top-0 z-10 h-px bg-[var(--design-editor-accent-color)]" />
|
|
1081
|
-
) : null}
|
|
1082
|
-
{activeDrop === "after" ? (
|
|
1083
|
-
<span className="pointer-events-none absolute bottom-0 left-2 right-2 z-10 h-px bg-[var(--design-editor-accent-color)]" />
|
|
1084
|
-
) : null}
|
|
1085
|
-
<div
|
|
1086
|
-
className={cn(
|
|
1087
|
-
"group flex h-8 items-center gap-1 rounded-[5px] pr-1 text-[12px]",
|
|
1088
|
-
activeDrop === "inside" &&
|
|
1089
|
-
"ring-1 ring-inset ring-[var(--design-editor-accent-color)]",
|
|
1090
|
-
isSelected &&
|
|
1091
|
-
"bg-[var(--design-editor-selection-color)] text-foreground",
|
|
1092
|
-
!isSelected &&
|
|
1093
|
-
isInSelectedSubtree &&
|
|
1094
|
-
"bg-[var(--design-editor-selected-subtree-color)] text-foreground/95",
|
|
1095
|
-
!isSelected &&
|
|
1096
|
-
isActiveScreen &&
|
|
1097
|
-
"bg-[var(--design-editor-active-row-color)] text-foreground hover:bg-[var(--design-editor-active-row-color)]",
|
|
1098
|
-
!isSelected &&
|
|
1099
|
-
!isInSelectedSubtree &&
|
|
1100
|
-
!isActiveScreen &&
|
|
1101
|
-
"text-foreground/90 hover:bg-[var(--design-editor-layer-hover-color)] hover:text-foreground",
|
|
1102
|
-
node.hidden && "text-muted-foreground",
|
|
1103
|
-
)}
|
|
1104
|
-
style={{ paddingLeft: rowIndent(depth) }}
|
|
1105
|
-
>
|
|
1106
|
-
{hasChildren ? (
|
|
1107
|
-
<Button
|
|
1108
|
-
type="button"
|
|
1109
|
-
variant="ghost"
|
|
1110
|
-
size="icon"
|
|
1111
|
-
className="size-4 shrink-0 rounded-sm p-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
1112
|
-
aria-label={isExpanded ? labels.collapse : labels.expand}
|
|
1113
|
-
onClick={(event) => {
|
|
1114
|
-
event.stopPropagation();
|
|
1115
|
-
onToggleExpanded(!isExpanded);
|
|
1116
|
-
}}
|
|
1117
|
-
>
|
|
1118
|
-
{isExpanded ? (
|
|
1119
|
-
<IconChevronDown className="size-4" />
|
|
1120
|
-
) : (
|
|
1121
|
-
<IconChevronRight className="size-4 rtl:-scale-x-100" />
|
|
1122
|
-
)}
|
|
1123
|
-
</Button>
|
|
1124
|
-
) : (
|
|
1125
|
-
<span className="size-4 shrink-0" />
|
|
1126
|
-
)}
|
|
1137
|
+
const showContextMenu =
|
|
1138
|
+
selectable &&
|
|
1139
|
+
(Boolean(onRename && node.renamable !== false) || lockable || hideable);
|
|
1127
1140
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1141
|
+
return (
|
|
1142
|
+
<ContextMenu>
|
|
1143
|
+
<ContextMenuTrigger asChild disabled={!showContextMenu}>
|
|
1144
|
+
<div
|
|
1145
|
+
ref={rowRef}
|
|
1146
|
+
role="treeitem"
|
|
1147
|
+
aria-expanded={hasChildren ? isExpanded : undefined}
|
|
1148
|
+
aria-level={depth + 1}
|
|
1149
|
+
aria-selected={selectable ? isSelected : undefined}
|
|
1150
|
+
className="relative min-w-full"
|
|
1151
|
+
draggable={draggable}
|
|
1152
|
+
onDragStart={handleDragStart}
|
|
1153
|
+
onDragOver={handleDragOver}
|
|
1154
|
+
onDragLeave={(event) => {
|
|
1155
|
+
// Only clear the indicator when the pointer truly leaves this row.
|
|
1156
|
+
// Moving into a child element fires dragleave on the outer div too, so
|
|
1157
|
+
// we suppress the clear when relatedTarget is still within this row.
|
|
1158
|
+
if (
|
|
1159
|
+
event.currentTarget.contains(event.relatedTarget as Node | null)
|
|
1160
|
+
)
|
|
1161
|
+
return;
|
|
1162
|
+
if (activeDropIntent?.targetId === node.id) activeDropIntent = null;
|
|
1163
|
+
onDropIndicatorChange(null);
|
|
1164
|
+
}}
|
|
1165
|
+
onDrop={handleDrop}
|
|
1166
|
+
onDragEnd={() => {
|
|
1167
|
+
activeDragState = null;
|
|
1168
|
+
activeDropIntent = null;
|
|
1169
|
+
onDropIndicatorChange(null);
|
|
1170
|
+
}}
|
|
1171
|
+
onMouseEnter={() => onHoverLayer?.(node.id)}
|
|
1172
|
+
onMouseLeave={() => onLeaveLayer?.(node.id)}
|
|
1140
1173
|
>
|
|
1141
|
-
|
|
1174
|
+
{activeDrop === "before" ? (
|
|
1175
|
+
<span className="pointer-events-none absolute left-2 right-2 top-0 z-10 h-px bg-[var(--design-editor-accent-color)]" />
|
|
1176
|
+
) : null}
|
|
1177
|
+
{activeDrop === "after" ? (
|
|
1178
|
+
<span className="pointer-events-none absolute bottom-0 left-2 right-2 z-10 h-px bg-[var(--design-editor-accent-color)]" />
|
|
1179
|
+
) : null}
|
|
1180
|
+
<div
|
|
1142
1181
|
className={cn(
|
|
1143
|
-
"
|
|
1144
|
-
|
|
1182
|
+
"group flex h-8 min-w-full items-center gap-1 rounded-[5px] pr-1 text-[12px]",
|
|
1183
|
+
activeDrop === "inside" &&
|
|
1184
|
+
"ring-1 ring-inset ring-[var(--design-editor-accent-color)]",
|
|
1185
|
+
isSelected &&
|
|
1186
|
+
"bg-[var(--design-editor-selection-color)] text-foreground",
|
|
1187
|
+
!isSelected &&
|
|
1188
|
+
isInSelectedSubtree &&
|
|
1189
|
+
"bg-[var(--design-editor-selected-subtree-color)] text-foreground/95",
|
|
1190
|
+
!isSelected &&
|
|
1191
|
+
isActiveScreen &&
|
|
1192
|
+
"bg-[var(--design-editor-active-row-color)] text-foreground hover:bg-[var(--design-editor-active-row-color)]",
|
|
1193
|
+
!isSelected &&
|
|
1194
|
+
!isInSelectedSubtree &&
|
|
1195
|
+
!isActiveScreen &&
|
|
1196
|
+
"text-foreground/90 hover:bg-[var(--design-editor-layer-hover-color)] hover:text-foreground",
|
|
1197
|
+
node.hidden && "text-muted-foreground",
|
|
1145
1198
|
)}
|
|
1199
|
+
style={{ paddingLeft: rowIndent(depth) }}
|
|
1146
1200
|
>
|
|
1147
|
-
{
|
|
1148
|
-
<LayerGlyph
|
|
1149
|
-
node={node}
|
|
1150
|
-
selected={isSelected}
|
|
1151
|
-
inSelectedSubtree={isInSelectedSubtree}
|
|
1152
|
-
/>
|
|
1153
|
-
)}
|
|
1154
|
-
</span>
|
|
1155
|
-
{isRenaming ? (
|
|
1156
|
-
<input
|
|
1157
|
-
autoFocus
|
|
1158
|
-
value={renameDraft}
|
|
1159
|
-
onClick={(event) => event.stopPropagation()}
|
|
1160
|
-
onChange={(event) => onRenameDraftChange(event.target.value)}
|
|
1161
|
-
onBlur={() => {
|
|
1162
|
-
// Escape sets renameCancelledRef before blur fires; skip commit.
|
|
1163
|
-
if (renameCancelledRef.current) {
|
|
1164
|
-
renameCancelledRef.current = false;
|
|
1165
|
-
return;
|
|
1166
|
-
}
|
|
1167
|
-
onCommitRename(node.id);
|
|
1168
|
-
}}
|
|
1169
|
-
onKeyDown={(event) => {
|
|
1170
|
-
if (event.key === "Enter") {
|
|
1171
|
-
event.preventDefault();
|
|
1172
|
-
onCommitRename(node.id);
|
|
1173
|
-
} else if (event.key === "Escape") {
|
|
1174
|
-
event.preventDefault();
|
|
1175
|
-
renameCancelledRef.current = true;
|
|
1176
|
-
onCancelRename();
|
|
1177
|
-
}
|
|
1178
|
-
}}
|
|
1179
|
-
className="h-6 min-w-0 flex-1 rounded-[4px] border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1.5 text-[12px] text-foreground outline-none"
|
|
1180
|
-
aria-label={labels.rename}
|
|
1181
|
-
/>
|
|
1182
|
-
) : (
|
|
1183
|
-
<span
|
|
1184
|
-
className={cn(
|
|
1185
|
-
"min-w-0 flex-1 truncate font-medium leading-none",
|
|
1186
|
-
node.hidden && "line-through",
|
|
1187
|
-
)}
|
|
1188
|
-
>
|
|
1189
|
-
{node.name}
|
|
1190
|
-
</span>
|
|
1191
|
-
)}
|
|
1192
|
-
{!isRenaming &&
|
|
1193
|
-
layerCanShowBadge(node) &&
|
|
1194
|
-
node.badge !== null &&
|
|
1195
|
-
node.badge !== undefined ? (
|
|
1196
|
-
<span className="shrink-0 rounded-sm bg-muted px-1 text-[10px] text-muted-foreground">
|
|
1197
|
-
{node.badge}
|
|
1198
|
-
</span>
|
|
1199
|
-
) : null}
|
|
1200
|
-
</button>
|
|
1201
|
-
|
|
1202
|
-
{lockable ? (
|
|
1203
|
-
<Tooltip>
|
|
1204
|
-
<TooltipTrigger asChild>
|
|
1201
|
+
{hasChildren ? (
|
|
1205
1202
|
<Button
|
|
1206
1203
|
type="button"
|
|
1207
1204
|
variant="ghost"
|
|
1208
1205
|
size="icon"
|
|
1209
|
-
className=
|
|
1210
|
-
|
|
1211
|
-
node.locked && "opacity-100",
|
|
1212
|
-
isSelected && "text-foreground",
|
|
1213
|
-
)}
|
|
1214
|
-
aria-label={node.locked ? labels.unlock : labels.lock}
|
|
1206
|
+
className="size-4 shrink-0 rounded-sm p-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
1207
|
+
aria-label={isExpanded ? labels.collapse : labels.expand}
|
|
1215
1208
|
onClick={(event) => {
|
|
1216
1209
|
event.stopPropagation();
|
|
1217
|
-
|
|
1210
|
+
onToggleExpanded(!isExpanded);
|
|
1218
1211
|
}}
|
|
1219
1212
|
>
|
|
1220
|
-
{
|
|
1221
|
-
<
|
|
1213
|
+
{isExpanded ? (
|
|
1214
|
+
<IconChevronDown className="size-4" />
|
|
1222
1215
|
) : (
|
|
1223
|
-
<
|
|
1216
|
+
<IconChevronRight className="size-4 rtl:-scale-x-100" />
|
|
1224
1217
|
)}
|
|
1225
1218
|
</Button>
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
</TooltipContent>
|
|
1230
|
-
</Tooltip>
|
|
1231
|
-
) : null}
|
|
1219
|
+
) : (
|
|
1220
|
+
<span className="size-4 shrink-0" />
|
|
1221
|
+
)}
|
|
1232
1222
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1223
|
+
<button
|
|
1224
|
+
type="button"
|
|
1225
|
+
disabled={!selectable}
|
|
1226
|
+
data-layer-row-button
|
|
1227
|
+
data-layer-node-id={node.id}
|
|
1228
|
+
className={cn(
|
|
1229
|
+
"flex min-w-0 flex-1 items-center gap-2 rounded-sm px-0.5 py-0 text-left outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
1230
|
+
selectable ? "cursor-default" : "cursor-default opacity-80",
|
|
1231
|
+
)}
|
|
1232
|
+
onClick={handlePointerSelect}
|
|
1233
|
+
onDoubleClick={() => onStartRename(node)}
|
|
1234
|
+
onKeyDown={handleKeyDown}
|
|
1235
|
+
>
|
|
1236
|
+
<span
|
|
1240
1237
|
className={cn(
|
|
1241
|
-
"
|
|
1242
|
-
|
|
1243
|
-
isSelected && "text-foreground",
|
|
1238
|
+
"shrink-0 text-muted-foreground",
|
|
1239
|
+
(isSelected || isInSelectedSubtree) && "text-foreground",
|
|
1244
1240
|
)}
|
|
1245
|
-
aria-label={node.hidden ? labels.show : labels.hide}
|
|
1246
|
-
onClick={(event) => {
|
|
1247
|
-
event.stopPropagation();
|
|
1248
|
-
onToggleHidden?.(node.id, !node.hidden);
|
|
1249
|
-
}}
|
|
1250
1241
|
>
|
|
1251
|
-
{node.
|
|
1252
|
-
<
|
|
1253
|
-
|
|
1254
|
-
|
|
1242
|
+
{node.icon ?? (
|
|
1243
|
+
<LayerGlyph
|
|
1244
|
+
node={node}
|
|
1245
|
+
selected={isSelected}
|
|
1246
|
+
inSelectedSubtree={isInSelectedSubtree}
|
|
1247
|
+
/>
|
|
1255
1248
|
)}
|
|
1256
|
-
</
|
|
1257
|
-
|
|
1258
|
-
|
|
1249
|
+
</span>
|
|
1250
|
+
{isRenaming ? (
|
|
1251
|
+
<input
|
|
1252
|
+
autoFocus
|
|
1253
|
+
value={renameDraft}
|
|
1254
|
+
onClick={(event) => event.stopPropagation()}
|
|
1255
|
+
onChange={(event) => onRenameDraftChange(event.target.value)}
|
|
1256
|
+
onBlur={() => {
|
|
1257
|
+
// Escape sets renameCancelledRef before blur fires; skip commit.
|
|
1258
|
+
if (renameCancelledRef.current) {
|
|
1259
|
+
renameCancelledRef.current = false;
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
onCommitRename(node.id);
|
|
1263
|
+
}}
|
|
1264
|
+
onKeyDown={(event) => {
|
|
1265
|
+
if (event.key === "Enter") {
|
|
1266
|
+
event.preventDefault();
|
|
1267
|
+
// Stop propagation so the keydown does not bubble up to the
|
|
1268
|
+
// parent row <button>'s handleKeyDown, which would fire
|
|
1269
|
+
// onSelect and potentially trigger canvas-level side effects
|
|
1270
|
+
// (e.g. switching to overview mode or selecting a wrong layer).
|
|
1271
|
+
event.stopPropagation();
|
|
1272
|
+
onCommitRename(node.id);
|
|
1273
|
+
} else if (event.key === "Tab") {
|
|
1274
|
+
// Commit the rename on Tab (Figma behavior) and prevent the
|
|
1275
|
+
// keydown from reaching the global design hotkeys handler which
|
|
1276
|
+
// would cycle the active file when Tab fires outside an input.
|
|
1277
|
+
event.preventDefault();
|
|
1278
|
+
event.stopPropagation();
|
|
1279
|
+
onCommitRename(node.id);
|
|
1280
|
+
} else if (event.key === "Escape") {
|
|
1281
|
+
event.preventDefault();
|
|
1282
|
+
event.stopPropagation();
|
|
1283
|
+
renameCancelledRef.current = true;
|
|
1284
|
+
onCancelRename();
|
|
1285
|
+
}
|
|
1286
|
+
}}
|
|
1287
|
+
className="h-6 min-w-0 flex-1 rounded-[4px] border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1.5 text-[12px] text-foreground outline-none"
|
|
1288
|
+
aria-label={labels.rename}
|
|
1289
|
+
/>
|
|
1290
|
+
) : (
|
|
1291
|
+
<span
|
|
1292
|
+
className={cn(
|
|
1293
|
+
"min-w-0 flex-1 truncate font-medium leading-none",
|
|
1294
|
+
node.hidden && "line-through",
|
|
1295
|
+
)}
|
|
1296
|
+
title={node.name}
|
|
1297
|
+
>
|
|
1298
|
+
{node.name}
|
|
1299
|
+
</span>
|
|
1300
|
+
)}
|
|
1301
|
+
{!isRenaming &&
|
|
1302
|
+
layerCanShowBadge(node) &&
|
|
1303
|
+
node.badge !== null &&
|
|
1304
|
+
node.badge !== undefined ? (
|
|
1305
|
+
<span className="shrink-0 rounded-sm bg-muted px-1 text-[10px] text-muted-foreground">
|
|
1306
|
+
{node.badge}
|
|
1307
|
+
</span>
|
|
1308
|
+
) : null}
|
|
1309
|
+
</button>
|
|
1310
|
+
|
|
1311
|
+
{lockable ? (
|
|
1312
|
+
<Tooltip>
|
|
1313
|
+
<TooltipTrigger asChild>
|
|
1314
|
+
<Button
|
|
1315
|
+
type="button"
|
|
1316
|
+
variant="ghost"
|
|
1317
|
+
size="icon"
|
|
1318
|
+
className={cn(
|
|
1319
|
+
"size-5 shrink-0 rounded-sm p-0 text-muted-foreground opacity-0 hover:bg-transparent hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100",
|
|
1320
|
+
node.locked && "opacity-100",
|
|
1321
|
+
isSelected && "text-foreground",
|
|
1322
|
+
)}
|
|
1323
|
+
aria-label={node.locked ? labels.unlock : labels.lock}
|
|
1324
|
+
onClick={(event) => {
|
|
1325
|
+
event.stopPropagation();
|
|
1326
|
+
onToggleLocked?.(node.id, !node.locked);
|
|
1327
|
+
}}
|
|
1328
|
+
>
|
|
1329
|
+
{node.locked ? (
|
|
1330
|
+
<IconLock className="size-3" />
|
|
1331
|
+
) : (
|
|
1332
|
+
<IconLockOpen className="size-3" />
|
|
1333
|
+
)}
|
|
1334
|
+
</Button>
|
|
1335
|
+
</TooltipTrigger>
|
|
1336
|
+
<TooltipContent>
|
|
1337
|
+
{node.locked ? labels.unlock : labels.lock}
|
|
1338
|
+
</TooltipContent>
|
|
1339
|
+
</Tooltip>
|
|
1340
|
+
) : null}
|
|
1341
|
+
|
|
1342
|
+
{hideable ? (
|
|
1343
|
+
<Tooltip>
|
|
1344
|
+
<TooltipTrigger asChild>
|
|
1345
|
+
<Button
|
|
1346
|
+
type="button"
|
|
1347
|
+
variant="ghost"
|
|
1348
|
+
size="icon"
|
|
1349
|
+
className={cn(
|
|
1350
|
+
"size-5 shrink-0 rounded-sm p-0 text-muted-foreground opacity-0 hover:bg-transparent hover:text-foreground group-hover:opacity-100 focus-visible:opacity-100",
|
|
1351
|
+
node.hidden && "opacity-100",
|
|
1352
|
+
isSelected && "text-foreground",
|
|
1353
|
+
)}
|
|
1354
|
+
aria-label={node.hidden ? labels.show : labels.hide}
|
|
1355
|
+
onClick={(event) => {
|
|
1356
|
+
event.stopPropagation();
|
|
1357
|
+
onToggleHidden?.(node.id, !node.hidden);
|
|
1358
|
+
}}
|
|
1359
|
+
>
|
|
1360
|
+
{node.hidden ? (
|
|
1361
|
+
<IconEyeOff className="size-3" />
|
|
1362
|
+
) : (
|
|
1363
|
+
<IconEye className="size-3" />
|
|
1364
|
+
)}
|
|
1365
|
+
</Button>
|
|
1366
|
+
</TooltipTrigger>
|
|
1367
|
+
<TooltipContent>
|
|
1368
|
+
{node.hidden ? labels.show : labels.hide}
|
|
1369
|
+
</TooltipContent>
|
|
1370
|
+
</Tooltip>
|
|
1371
|
+
) : null}
|
|
1372
|
+
</div>
|
|
1373
|
+
</div>
|
|
1374
|
+
</ContextMenuTrigger>
|
|
1375
|
+
{showContextMenu ? (
|
|
1376
|
+
<ContextMenuContent className="z-[300] min-w-[160px] text-[12px]">
|
|
1377
|
+
{onRename && node.renamable !== false ? (
|
|
1378
|
+
<ContextMenuItem
|
|
1379
|
+
className="gap-2 text-[12px]"
|
|
1380
|
+
onSelect={() => onStartRename(node)}
|
|
1381
|
+
>
|
|
1382
|
+
<IconPencil className="size-3.5 text-muted-foreground" />
|
|
1383
|
+
{labels.rename}
|
|
1384
|
+
</ContextMenuItem>
|
|
1385
|
+
) : null}
|
|
1386
|
+
{onRename && node.renamable !== false && (lockable || hideable) ? (
|
|
1387
|
+
<ContextMenuSeparator />
|
|
1388
|
+
) : null}
|
|
1389
|
+
{lockable ? (
|
|
1390
|
+
<ContextMenuItem
|
|
1391
|
+
className="gap-2 text-[12px]"
|
|
1392
|
+
onSelect={() => onToggleLocked?.(node.id, !node.locked)}
|
|
1393
|
+
>
|
|
1394
|
+
{node.locked ? (
|
|
1395
|
+
<IconLockOpen className="size-3.5 text-muted-foreground" />
|
|
1396
|
+
) : (
|
|
1397
|
+
<IconLock className="size-3.5 text-muted-foreground" />
|
|
1398
|
+
)}
|
|
1399
|
+
{node.locked ? labels.unlock : labels.lock}
|
|
1400
|
+
</ContextMenuItem>
|
|
1401
|
+
) : null}
|
|
1402
|
+
{hideable ? (
|
|
1403
|
+
<ContextMenuItem
|
|
1404
|
+
className="gap-2 text-[12px]"
|
|
1405
|
+
onSelect={() => onToggleHidden?.(node.id, !node.hidden)}
|
|
1406
|
+
>
|
|
1407
|
+
{node.hidden ? (
|
|
1408
|
+
<IconEye className="size-3.5 text-muted-foreground" />
|
|
1409
|
+
) : (
|
|
1410
|
+
<IconEyeOff className="size-3.5 text-muted-foreground" />
|
|
1411
|
+
)}
|
|
1259
1412
|
{node.hidden ? labels.show : labels.hide}
|
|
1260
|
-
</
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
</
|
|
1413
|
+
</ContextMenuItem>
|
|
1414
|
+
) : null}
|
|
1415
|
+
</ContextMenuContent>
|
|
1416
|
+
) : null}
|
|
1417
|
+
</ContextMenu>
|
|
1265
1418
|
);
|
|
1266
1419
|
}
|
|
1267
1420
|
|
|
@@ -1623,6 +1776,18 @@ function ElementLayerGlyph({ className }: { className?: string }) {
|
|
|
1623
1776
|
);
|
|
1624
1777
|
}
|
|
1625
1778
|
|
|
1779
|
+
function layerCanDropInside(node: LayersPanelNode, hasChildren: boolean) {
|
|
1780
|
+
return (
|
|
1781
|
+
hasChildren ||
|
|
1782
|
+
Boolean(node.layout?.isFlexContainer || node.layout?.isGridContainer) ||
|
|
1783
|
+
node.type === "file" ||
|
|
1784
|
+
node.type === "screen" ||
|
|
1785
|
+
node.type === "frame" ||
|
|
1786
|
+
node.type === "group" ||
|
|
1787
|
+
node.type === "section"
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1626
1791
|
function dropPlacementForEvent(
|
|
1627
1792
|
event: DragEvent<HTMLDivElement>,
|
|
1628
1793
|
canDropInside: boolean,
|