@agent-native/core 0.163.4 → 0.164.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
- package/corpus/templates/analytics/netlify.toml +2 -0
- package/corpus/templates/assets/netlify.toml +2 -0
- package/corpus/templates/brain/netlify.toml +2 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
- package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
- package/corpus/templates/calendar/netlify.toml +2 -0
- package/corpus/templates/chat/netlify.toml +2 -0
- package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
- package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
- package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
- package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
- package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
- package/corpus/templates/clips/app/routes/_index.tsx +10 -2
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
- package/corpus/templates/clips/netlify.toml +2 -0
- package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
- package/corpus/templates/content/netlify.toml +2 -0
- package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
- package/corpus/templates/design/AGENTS.md +3 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
- package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
- package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
- package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
- package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
- package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
- package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
- package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
- package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
- package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
- package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
- package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
- package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
- package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
- package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
- package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
- package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
- package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
- package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
- package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
- package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
- package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
- package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
- package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
- package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
- package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
- package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
- package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
- package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
- package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
- package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
- package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
- package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
- package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
- package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
- package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
- package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
- package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
- package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
- package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
- package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
- package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
- package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
- package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
- package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
- package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
- package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
- package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
- package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
- package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
- package/corpus/templates/design/netlify.toml +2 -0
- package/corpus/templates/design/shared/board-file.ts +2 -1
- package/corpus/templates/dispatch/netlify.toml +2 -0
- package/corpus/templates/forms/netlify.toml +2 -0
- package/corpus/templates/mail/netlify.toml +2 -0
- package/corpus/templates/plan/netlify.toml +2 -0
- package/corpus/templates/slides/netlify.toml +2 -0
- package/dist/agent/engine/builder-engine.d.ts +6 -4
- package/dist/agent/engine/builder-engine.js +91 -26
- package/dist/agent/engine/registry.js +16 -0
- package/dist/cli/code-agent-agent-tools.d.ts +2 -0
- package/dist/cli/code-agent-agent-tools.js +188 -0
- package/dist/cli/code-agent-collaboration.d.ts +34 -0
- package/dist/cli/code-agent-collaboration.js +104 -0
- package/dist/cli/code-agent-executor.js +6 -0
- package/dist/cli/code-agent-schedules.d.ts +54 -0
- package/dist/cli/code-agent-schedules.js +198 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -2
- package/dist/client/EnvironmentBadge.js +6 -5
- package/dist/client/chat/run-recovery.js +3 -2
- package/dist/client/chat/tool-call-display.js +2 -3
- package/dist/client/components/CodeRequiredDialog.js +4 -2
- package/dist/client/i18n.js +5 -4
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +6 -1
- package/dist/client/settings/useBuilderStatus.js +22 -33
- package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
- package/dist/code-agents/index.d.ts +2 -0
- package/dist/code-agents/index.js +2 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/shared.d.ts +2 -0
- package/dist/localization/shared.js +5 -0
- package/dist/mcp-client/oauth-client.d.ts +1 -0
- package/dist/mcp-client/oauth-client.js +50 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/auth-marketing-locales.d.ts +7 -0
- package/dist/server/auth-marketing-locales.js +1322 -0
- package/dist/server/auth-marketing.d.ts +1 -0
- package/dist/server/auth-marketing.js +35 -6
- package/dist/server/auth.js +4 -17
- package/dist/server/builder-browser.d.ts +22 -0
- package/dist/server/builder-browser.js +77 -1
- package/dist/server/builder-oauth.d.ts +50 -0
- package/dist/server/builder-oauth.js +263 -0
- package/dist/server/core-routes-plugin.d.ts +11 -0
- package/dist/server/core-routes-plugin.js +291 -415
- package/dist/server/credential-provider.d.ts +1 -1
- package/dist/server/credential-provider.js +1 -1
- package/dist/server/csrf.js +2 -2
- package/dist/server/onboarding-html.js +52 -120
- package/dist/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/templates/chat/netlify.toml +2 -0
- package/docs/content/deployment.mdx +2 -0
- package/docs/content/multi-app-workspace.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/chat/netlify.toml +2 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { CanvasFrameGeometryById } from "@shared/canvas-frames";
|
|
2
|
+
import type { MoveNodeEditIntent } from "@shared/code-layer";
|
|
3
|
+
import { applyVisualEdit } from "@shared/code-layer";
|
|
4
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
5
|
+
|
|
6
|
+
import { trace } from "@/components/design/design-trace";
|
|
7
|
+
import { getInitialFrameGeometry } from "@/components/design/multi-screen/frame-geometry";
|
|
8
|
+
import type { FrameGeometry } from "@/components/design/multi-screen/types";
|
|
9
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
10
|
+
import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
|
|
11
|
+
import type { SelectedLayerTarget } from "@/pages/design-editor/code-layer-state";
|
|
12
|
+
import {
|
|
13
|
+
elementInfoFromCodeLayerNode,
|
|
14
|
+
resolveCodeLayerNodeFromElementInfo,
|
|
15
|
+
} from "@/pages/design-editor/code-layer-state";
|
|
16
|
+
import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
|
|
17
|
+
import {
|
|
18
|
+
cloneCanvasFrameGeometry,
|
|
19
|
+
getCanvasFrameGeometry,
|
|
20
|
+
} from "@/pages/design-editor/design-data-geometry-utils";
|
|
21
|
+
import type { DesignEditorPreferences } from "@/pages/design-editor/editor-preferences";
|
|
22
|
+
import {
|
|
23
|
+
resolveElementNudgeIntent,
|
|
24
|
+
resolveNudgeIntent,
|
|
25
|
+
} from "@/pages/design-editor/nudge-intent";
|
|
26
|
+
import { overviewSelectionTargetsElement } from "@/pages/design-editor/selection-state";
|
|
27
|
+
import type { DesignFile } from "@/pages/design-editor/types";
|
|
28
|
+
|
|
29
|
+
export interface NudgeSelectionArgs {
|
|
30
|
+
activeFile: DesignFile;
|
|
31
|
+
applyLocalContentUpdate: (
|
|
32
|
+
nextContent: string,
|
|
33
|
+
options?: {
|
|
34
|
+
refreshPreview?: boolean;
|
|
35
|
+
skipPreview?: boolean;
|
|
36
|
+
forcePreviewFullDocument?: boolean;
|
|
37
|
+
immediateSave?: boolean;
|
|
38
|
+
persist?: boolean;
|
|
39
|
+
recordHistory?: boolean;
|
|
40
|
+
historyBeforeContent?: string;
|
|
41
|
+
updatedAt?: string;
|
|
42
|
+
clipboardMutation?: ClipboardContentMutationPublication;
|
|
43
|
+
},
|
|
44
|
+
) => void;
|
|
45
|
+
boardFileId: string | undefined;
|
|
46
|
+
boardFrameGeometry: FrameGeometry | undefined;
|
|
47
|
+
canEditDesign: boolean;
|
|
48
|
+
commitVisualStyles: (
|
|
49
|
+
selector: string,
|
|
50
|
+
styles: Record<string, string>,
|
|
51
|
+
options?: {
|
|
52
|
+
runtimeApplied?: boolean;
|
|
53
|
+
elementInfo?: ElementInfo;
|
|
54
|
+
originalStyles?: Record<string, string>;
|
|
55
|
+
},
|
|
56
|
+
) => void;
|
|
57
|
+
designDataJsonRef: RefObject<Record<string, unknown>>;
|
|
58
|
+
editorPreferences: DesignEditorPreferences;
|
|
59
|
+
files: DesignFile[];
|
|
60
|
+
getFreshActiveContent: () => string;
|
|
61
|
+
handleGeometryCommit: (
|
|
62
|
+
before: CanvasFrameGeometryById,
|
|
63
|
+
after: CanvasFrameGeometryById,
|
|
64
|
+
options?: { source?: "pointer" | "keyboard" },
|
|
65
|
+
) => void;
|
|
66
|
+
hideSelectionChromeForNudge: () => void;
|
|
67
|
+
overviewScreens: OverviewScreen[];
|
|
68
|
+
overviewSelectedScreenIds: string[];
|
|
69
|
+
selectedElement: ElementInfo | null;
|
|
70
|
+
selectedLayerIdsState: string[];
|
|
71
|
+
selectedLayerTargetsRef: RefObject<SelectedLayerTarget[]>;
|
|
72
|
+
setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
|
|
73
|
+
setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
|
|
74
|
+
viewModeRef: RefObject<"single" | "overview">;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function runNudgeSelection(
|
|
78
|
+
{
|
|
79
|
+
activeFile,
|
|
80
|
+
applyLocalContentUpdate,
|
|
81
|
+
boardFileId,
|
|
82
|
+
boardFrameGeometry,
|
|
83
|
+
canEditDesign,
|
|
84
|
+
commitVisualStyles,
|
|
85
|
+
designDataJsonRef,
|
|
86
|
+
editorPreferences,
|
|
87
|
+
files,
|
|
88
|
+
getFreshActiveContent,
|
|
89
|
+
handleGeometryCommit,
|
|
90
|
+
hideSelectionChromeForNudge,
|
|
91
|
+
overviewScreens,
|
|
92
|
+
overviewSelectedScreenIds,
|
|
93
|
+
selectedElement,
|
|
94
|
+
selectedLayerIdsState,
|
|
95
|
+
selectedLayerTargetsRef,
|
|
96
|
+
setSelectedElement,
|
|
97
|
+
setSelectedLayerIdsState,
|
|
98
|
+
viewModeRef,
|
|
99
|
+
}: NudgeSelectionArgs,
|
|
100
|
+
direction: "up" | "right" | "down" | "left",
|
|
101
|
+
largeStep: boolean,
|
|
102
|
+
) {
|
|
103
|
+
trace("structure", "nudge", { direction, largeStep });
|
|
104
|
+
if (!canEditDesign) return;
|
|
105
|
+
const nudgeAmounts = editorPreferences.nudge;
|
|
106
|
+
const freeTranslation = resolveNudgeIntent({
|
|
107
|
+
direction,
|
|
108
|
+
largeStep,
|
|
109
|
+
amounts: nudgeAmounts,
|
|
110
|
+
});
|
|
111
|
+
const dx = freeTranslation.kind === "translate" ? freeTranslation.dx : 0;
|
|
112
|
+
const dy = freeTranslation.kind === "translate" ? freeTranslation.dy : 0;
|
|
113
|
+
|
|
114
|
+
// Screen frames nudge through the same geometry-commit path mouse-drag
|
|
115
|
+
// uses, so a held arrow key gets handleGeometryCommit's ~800ms undo
|
|
116
|
+
// coalescing instead of one history entry per keypress.
|
|
117
|
+
if (
|
|
118
|
+
viewModeRef.current === "overview" &&
|
|
119
|
+
overviewSelectedScreenIds.length > 0 &&
|
|
120
|
+
!overviewSelectionTargetsElement({
|
|
121
|
+
selectedElement,
|
|
122
|
+
selectedLayerIds: selectedLayerIdsState,
|
|
123
|
+
fileIds: files.map((file) => file.id),
|
|
124
|
+
})
|
|
125
|
+
) {
|
|
126
|
+
const before = getCanvasFrameGeometry(designDataJsonRef.current);
|
|
127
|
+
const after = cloneCanvasFrameGeometry(before);
|
|
128
|
+
let changed = false;
|
|
129
|
+
overviewSelectedScreenIds.forEach((screenId) => {
|
|
130
|
+
const screenIndex = overviewScreens.findIndex(
|
|
131
|
+
(screen) => screen.id === screenId,
|
|
132
|
+
);
|
|
133
|
+
const screen =
|
|
134
|
+
screenIndex >= 0 ? overviewScreens[screenIndex] : undefined;
|
|
135
|
+
const fallbackGeometry =
|
|
136
|
+
screenIndex >= 0
|
|
137
|
+
? getInitialFrameGeometry(screenIndex, {
|
|
138
|
+
width: screen?.width ?? 1280,
|
|
139
|
+
height: screen?.height ?? 2560,
|
|
140
|
+
})
|
|
141
|
+
: boardFileId === screenId
|
|
142
|
+
? boardFrameGeometry
|
|
143
|
+
: undefined;
|
|
144
|
+
if (!fallbackGeometry) return;
|
|
145
|
+
const current = { ...fallbackGeometry, ...before[screenId] };
|
|
146
|
+
after[screenId] = {
|
|
147
|
+
...current,
|
|
148
|
+
x: current.x + dx,
|
|
149
|
+
y: current.y + dy,
|
|
150
|
+
};
|
|
151
|
+
changed = true;
|
|
152
|
+
});
|
|
153
|
+
if (changed) {
|
|
154
|
+
handleGeometryCommit(before, after, { source: "keyboard" });
|
|
155
|
+
}
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Selecting in the layers tree fills selectedLayerTargets before the
|
|
160
|
+
// bridge round-trip fills selectedElement, so keying off the latter
|
|
161
|
+
// alone silently drops the first nudge after every tree selection.
|
|
162
|
+
const nudgeTarget = selectedElement?.selector
|
|
163
|
+
? selectedElement
|
|
164
|
+
: selectedLayerTargetsRef.current[0]?.elementInfo;
|
|
165
|
+
if (!nudgeTarget?.selector) return;
|
|
166
|
+
|
|
167
|
+
const intent = resolveElementNudgeIntent({
|
|
168
|
+
content: activeFile ? getFreshActiveContent() : "",
|
|
169
|
+
selectedElement: nudgeTarget,
|
|
170
|
+
direction,
|
|
171
|
+
largeStep,
|
|
172
|
+
amounts: nudgeAmounts,
|
|
173
|
+
});
|
|
174
|
+
if (intent.kind === "none") return;
|
|
175
|
+
if (intent.kind === "reorder") {
|
|
176
|
+
const patch = applyVisualEdit(intent.content, {
|
|
177
|
+
kind: "moveNode",
|
|
178
|
+
target: { nodeId: intent.targetNodeId },
|
|
179
|
+
anchor: { nodeId: intent.anchorNodeId },
|
|
180
|
+
placement: intent.placement,
|
|
181
|
+
} satisfies MoveNodeEditIntent);
|
|
182
|
+
if (patch.result.status !== "applied") return;
|
|
183
|
+
applyLocalContentUpdate(patch.content, { skipPreview: true });
|
|
184
|
+
// A node with no stable `data-agent-native-node-id` has its id derived
|
|
185
|
+
// from path/offset, and the move changes both — so the pre-move id
|
|
186
|
+
// finds nothing and the selection has to be re-resolved by identity.
|
|
187
|
+
const movedNode =
|
|
188
|
+
patch.projection.nodes.find(
|
|
189
|
+
(node) =>
|
|
190
|
+
node.id === intent.targetNodeId ||
|
|
191
|
+
node.dataAttributes["data-agent-native-node-id"] ===
|
|
192
|
+
intent.targetNodeId,
|
|
193
|
+
) ??
|
|
194
|
+
resolveCodeLayerNodeFromElementInfo(patch.projection, selectedElement);
|
|
195
|
+
if (movedNode) {
|
|
196
|
+
setSelectedElement(elementInfoFromCodeLayerNode(movedNode));
|
|
197
|
+
setSelectedLayerIdsState([movedNode.id]);
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
hideSelectionChromeForNudge();
|
|
203
|
+
const left = parseFloat(nudgeTarget.computedStyles.left || "0") || 0;
|
|
204
|
+
const top = parseFloat(nudgeTarget.computedStyles.top || "0") || 0;
|
|
205
|
+
commitVisualStyles(nudgeTarget.selector, {
|
|
206
|
+
position:
|
|
207
|
+
nudgeTarget.computedStyles.position === "static"
|
|
208
|
+
? "relative"
|
|
209
|
+
: nudgeTarget.computedStyles.position || "relative",
|
|
210
|
+
left: `${Math.round(left + intent.dx)}px`,
|
|
211
|
+
top: `${Math.round(top + intent.dy)}px`,
|
|
212
|
+
});
|
|
213
|
+
}
|
package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computeReparentedChildPosition,
|
|
3
|
+
normalizePoisonedBoardNestedCoords,
|
|
4
|
+
} from "@shared/board-file";
|
|
5
|
+
import {
|
|
6
|
+
applyVisualEdit,
|
|
7
|
+
buildCodeLayerProjection,
|
|
8
|
+
moveNodeBetweenDocuments,
|
|
9
|
+
} from "@shared/code-layer";
|
|
10
|
+
import type { Dispatch, SetStateAction } from "react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
|
|
13
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
14
|
+
import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
|
|
15
|
+
import {
|
|
16
|
+
codeLayerPatchMessage,
|
|
17
|
+
elementInfoFromCodeLayerNode,
|
|
18
|
+
} from "@/pages/design-editor/code-layer-state";
|
|
19
|
+
import type { ContentHistoryEntry } from "@/pages/design-editor/history";
|
|
20
|
+
import {
|
|
21
|
+
getAbsolutePositioningForNodeInHtml,
|
|
22
|
+
removeAbsolutePositioningFromNodeInHtml,
|
|
23
|
+
setAbsolutePositioningForNodeInHtml,
|
|
24
|
+
warnIfPoisonedBoardCoordsNormalized,
|
|
25
|
+
} from "@/pages/design-editor/html-layer-positioning";
|
|
26
|
+
|
|
27
|
+
export interface OverviewPrimitiveReparentArgs {
|
|
28
|
+
applyFileContentUpdate: (
|
|
29
|
+
fileId: string,
|
|
30
|
+
nextContent: string,
|
|
31
|
+
options?: {
|
|
32
|
+
refreshPreview?: boolean;
|
|
33
|
+
skipPreview?: boolean;
|
|
34
|
+
forcePreviewFullDocument?: boolean;
|
|
35
|
+
persist?: boolean;
|
|
36
|
+
recordHistory?: boolean;
|
|
37
|
+
updatedAt?: string;
|
|
38
|
+
clipboardMutation?: ClipboardContentMutationPublication;
|
|
39
|
+
},
|
|
40
|
+
) => void;
|
|
41
|
+
boardFileId: string | undefined;
|
|
42
|
+
canEditDesign: boolean;
|
|
43
|
+
getScreenContent: (screenId: string) => string;
|
|
44
|
+
recordContentHistoryEntry: (entry: ContentHistoryEntry) => void;
|
|
45
|
+
setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
|
|
46
|
+
setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
|
|
47
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function runOverviewPrimitiveReparent(
|
|
51
|
+
{
|
|
52
|
+
applyFileContentUpdate,
|
|
53
|
+
boardFileId,
|
|
54
|
+
canEditDesign,
|
|
55
|
+
getScreenContent,
|
|
56
|
+
recordContentHistoryEntry,
|
|
57
|
+
setSelectedElement,
|
|
58
|
+
setSelectedLayerIdsState,
|
|
59
|
+
t,
|
|
60
|
+
}: OverviewPrimitiveReparentArgs,
|
|
61
|
+
{
|
|
62
|
+
sourceNodeId,
|
|
63
|
+
sourceScreenId,
|
|
64
|
+
targetNodeId,
|
|
65
|
+
targetScreenId,
|
|
66
|
+
placement = "inside",
|
|
67
|
+
}: {
|
|
68
|
+
sourceNodeId: string;
|
|
69
|
+
sourceScreenId: string;
|
|
70
|
+
targetNodeId: string;
|
|
71
|
+
targetScreenId: string;
|
|
72
|
+
placement?: "before" | "after" | "inside";
|
|
73
|
+
},
|
|
74
|
+
) {
|
|
75
|
+
if (!canEditDesign) return;
|
|
76
|
+
|
|
77
|
+
if (sourceScreenId === targetScreenId) {
|
|
78
|
+
// --- Same-screen reparent ---
|
|
79
|
+
const baseContent = getScreenContent(sourceScreenId);
|
|
80
|
+
if (!baseContent) return;
|
|
81
|
+
|
|
82
|
+
// 1. Move the node relative to the target anchor. For "inside" the
|
|
83
|
+
// anchor is the container itself (append); for "before"/"after" the
|
|
84
|
+
// anchor is a sibling child already inside an auto-layout container
|
|
85
|
+
// (flow-insert at that index) — applyMoveNodeEdit's
|
|
86
|
+
// prepareMovedFragmentForParent already strips absolute positioning
|
|
87
|
+
// from the moved fragment's root when the destination parent is a
|
|
88
|
+
// flow container, regardless of which placement resolved it there.
|
|
89
|
+
const movePatch = applyVisualEdit(baseContent, {
|
|
90
|
+
kind: "moveNode",
|
|
91
|
+
target: { nodeId: sourceNodeId },
|
|
92
|
+
anchor: { nodeId: targetNodeId },
|
|
93
|
+
placement,
|
|
94
|
+
});
|
|
95
|
+
if (movePatch.result.status !== "applied") {
|
|
96
|
+
toast.error(
|
|
97
|
+
codeLayerPatchMessage(
|
|
98
|
+
movePatch.result.message,
|
|
99
|
+
t("designEditor.toasts.layerMoveFailed"),
|
|
100
|
+
),
|
|
101
|
+
{ duration: 4000 },
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const movedNodeAttrId =
|
|
107
|
+
movePatch.projection.nodes.find(
|
|
108
|
+
(n) =>
|
|
109
|
+
n.dataAttributes["data-agent-native-node-id"] === sourceNodeId ||
|
|
110
|
+
n.id === sourceNodeId,
|
|
111
|
+
)?.dataAttributes["data-agent-native-node-id"] ?? sourceNodeId;
|
|
112
|
+
|
|
113
|
+
if (placement !== "inside") {
|
|
114
|
+
// Auto-layout flow-insert: belt-and-suspenders alongside
|
|
115
|
+
// prepareMovedFragmentForParent above — make sure the moved node
|
|
116
|
+
// itself carries no leftover position/left/top so it renders as a
|
|
117
|
+
// pure flow child at its new index instead of an absolute layer
|
|
118
|
+
// sitting on top of its new siblings.
|
|
119
|
+
const flowContent = removeAbsolutePositioningFromNodeInHtml(
|
|
120
|
+
movePatch.content,
|
|
121
|
+
movedNodeAttrId,
|
|
122
|
+
);
|
|
123
|
+
applyFileContentUpdate(sourceScreenId, flowContent, {
|
|
124
|
+
skipPreview: true,
|
|
125
|
+
});
|
|
126
|
+
const nextProjection = buildCodeLayerProjection(flowContent);
|
|
127
|
+
const movedNodeAfter = nextProjection.nodes.find(
|
|
128
|
+
(n) =>
|
|
129
|
+
n.dataAttributes["data-agent-native-node-id"] === sourceNodeId ||
|
|
130
|
+
n.id === sourceNodeId,
|
|
131
|
+
);
|
|
132
|
+
if (movedNodeAfter) {
|
|
133
|
+
setSelectedLayerIdsState([movedNodeAfter.id]);
|
|
134
|
+
setSelectedElement(elementInfoFromCodeLayerNode(movedNodeAfter));
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sourcePosition = getAbsolutePositioningForNodeInHtml(
|
|
140
|
+
baseContent,
|
|
141
|
+
sourceNodeId,
|
|
142
|
+
);
|
|
143
|
+
const targetPosition = getAbsolutePositioningForNodeInHtml(
|
|
144
|
+
baseContent,
|
|
145
|
+
targetNodeId,
|
|
146
|
+
);
|
|
147
|
+
// Rebase the moved node's left/top to be PARENT-relative.
|
|
148
|
+
// computeReparentedChildPosition strips the board-surface offset
|
|
149
|
+
// (65536-multiples) from either side first, so a source that was
|
|
150
|
+
// persisted in board-iframe viewport coordinates (the historic
|
|
151
|
+
// container-drop poison — see BOARD_SURFACE_CONTENT_OFFSET_PX in
|
|
152
|
+
// shared/board-file.ts) still comes out as a sane parent-relative
|
|
153
|
+
// position instead of an off-world near-65536 value.
|
|
154
|
+
const rebasedContent =
|
|
155
|
+
sourcePosition && targetPosition
|
|
156
|
+
? setAbsolutePositioningForNodeInHtml(
|
|
157
|
+
movePatch.content,
|
|
158
|
+
movedNodeAttrId,
|
|
159
|
+
computeReparentedChildPosition(sourcePosition, targetPosition),
|
|
160
|
+
)
|
|
161
|
+
: movePatch.content;
|
|
162
|
+
// Board safety net: if any nested coordinate still carries the
|
|
163
|
+
// surface-offset fingerprint (e.g. the position pair above could not
|
|
164
|
+
// be resolved and the rebase was skipped), normalize the final
|
|
165
|
+
// content so a nested board child can never persist off-world.
|
|
166
|
+
const nextContent = (() => {
|
|
167
|
+
if (!boardFileId || sourceScreenId !== boardFileId) {
|
|
168
|
+
return rebasedContent;
|
|
169
|
+
}
|
|
170
|
+
const normalized = normalizePoisonedBoardNestedCoords(rebasedContent);
|
|
171
|
+
warnIfPoisonedBoardCoordsNormalized(sourceScreenId, normalized);
|
|
172
|
+
return normalized.html;
|
|
173
|
+
})();
|
|
174
|
+
|
|
175
|
+
applyFileContentUpdate(sourceScreenId, nextContent, {
|
|
176
|
+
skipPreview: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Re-select the moved node.
|
|
180
|
+
const nextProjection = buildCodeLayerProjection(nextContent);
|
|
181
|
+
const movedNodeAfter = nextProjection.nodes.find(
|
|
182
|
+
(n) =>
|
|
183
|
+
n.dataAttributes["data-agent-native-node-id"] === sourceNodeId ||
|
|
184
|
+
n.id === sourceNodeId,
|
|
185
|
+
);
|
|
186
|
+
if (movedNodeAfter) {
|
|
187
|
+
setSelectedLayerIdsState([movedNodeAfter.id]);
|
|
188
|
+
setSelectedElement(elementInfoFromCodeLayerNode(movedNodeAfter));
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- Cross-screen reparent ---
|
|
194
|
+
const sourceContent = getScreenContent(sourceScreenId);
|
|
195
|
+
const destContent = getScreenContent(targetScreenId);
|
|
196
|
+
if (!sourceContent || !destContent) return;
|
|
197
|
+
|
|
198
|
+
// Resolve data-agent-native-node-id attributes for moveNodeBetweenDocuments.
|
|
199
|
+
const sourceProjection = buildCodeLayerProjection(sourceContent);
|
|
200
|
+
const destProjection = buildCodeLayerProjection(destContent);
|
|
201
|
+
const sourceNode = sourceProjection.nodes.find(
|
|
202
|
+
(n) =>
|
|
203
|
+
n.dataAttributes["data-agent-native-node-id"] === sourceNodeId ||
|
|
204
|
+
n.id === sourceNodeId,
|
|
205
|
+
);
|
|
206
|
+
const anchorNode = destProjection.nodes.find(
|
|
207
|
+
(n) =>
|
|
208
|
+
n.dataAttributes["data-agent-native-node-id"] === targetNodeId ||
|
|
209
|
+
n.id === targetNodeId,
|
|
210
|
+
);
|
|
211
|
+
const nodeAttrId =
|
|
212
|
+
sourceNode?.dataAttributes["data-agent-native-node-id"] ?? sourceNodeId;
|
|
213
|
+
const anchorAttrId =
|
|
214
|
+
anchorNode?.dataAttributes["data-agent-native-node-id"] ?? targetNodeId;
|
|
215
|
+
|
|
216
|
+
const result = moveNodeBetweenDocuments(sourceContent, destContent, {
|
|
217
|
+
nodeId: nodeAttrId,
|
|
218
|
+
anchorNodeId: anchorAttrId,
|
|
219
|
+
placement,
|
|
220
|
+
});
|
|
221
|
+
if (result.status !== "applied") {
|
|
222
|
+
toast.error(
|
|
223
|
+
codeLayerPatchMessage(
|
|
224
|
+
result.message,
|
|
225
|
+
t("designEditor.toasts.layerMoveFailed"),
|
|
226
|
+
),
|
|
227
|
+
{ duration: 4000 },
|
|
228
|
+
);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
// Finding 8: the requested anchor placement landed inside a
|
|
232
|
+
// <template> interior and was redirected to a real DOM slot right
|
|
233
|
+
// after the enclosing template instead — let the user know the drop
|
|
234
|
+
// wasn't silently discarded, just relocated nearby.
|
|
235
|
+
if (result.anchorRedirected) {
|
|
236
|
+
toast(t("designEditor.toasts.layerMoveRedirected"), {
|
|
237
|
+
duration: 4000,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const destNodeAttrId = result.movedNodeId ?? nodeAttrId;
|
|
242
|
+
// "inside" rebases to a parent-relative absolute position (matches the
|
|
243
|
+
// same-screen branch above and historic behavior for dropping onto a
|
|
244
|
+
// plain absolute container). "before"/"after" is an auto-layout
|
|
245
|
+
// flow-insert — moveNodeBetweenDocuments's prepareMovedFragmentForParent
|
|
246
|
+
// already stripped absolute positioning from the moved fragment's
|
|
247
|
+
// root, so just belt-and-suspenders clear it again by node id rather
|
|
248
|
+
// than reintroducing position:absolute via the rebase below.
|
|
249
|
+
const nextDestContent = (() => {
|
|
250
|
+
const rebasedDestContent = (() => {
|
|
251
|
+
if (placement !== "inside") {
|
|
252
|
+
return removeAbsolutePositioningFromNodeInHtml(
|
|
253
|
+
result.destHtml,
|
|
254
|
+
destNodeAttrId,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const sourcePosition = getAbsolutePositioningForNodeInHtml(
|
|
258
|
+
sourceContent,
|
|
259
|
+
nodeAttrId,
|
|
260
|
+
);
|
|
261
|
+
const targetPosition = getAbsolutePositioningForNodeInHtml(
|
|
262
|
+
destContent,
|
|
263
|
+
anchorAttrId,
|
|
264
|
+
);
|
|
265
|
+
// Same parent-relative rebase + board-poison stripping as the
|
|
266
|
+
// same-screen branch above (see computeReparentedChildPosition).
|
|
267
|
+
return sourcePosition && targetPosition
|
|
268
|
+
? setAbsolutePositioningForNodeInHtml(
|
|
269
|
+
result.destHtml,
|
|
270
|
+
destNodeAttrId,
|
|
271
|
+
computeReparentedChildPosition(sourcePosition, targetPosition),
|
|
272
|
+
)
|
|
273
|
+
: result.destHtml;
|
|
274
|
+
})();
|
|
275
|
+
if (!boardFileId || targetScreenId !== boardFileId) {
|
|
276
|
+
return rebasedDestContent;
|
|
277
|
+
}
|
|
278
|
+
const normalized = normalizePoisonedBoardNestedCoords(rebasedDestContent);
|
|
279
|
+
warnIfPoisonedBoardCoordsNormalized(targetScreenId, normalized);
|
|
280
|
+
return normalized.html;
|
|
281
|
+
})();
|
|
282
|
+
|
|
283
|
+
recordContentHistoryEntry({
|
|
284
|
+
changes: [
|
|
285
|
+
{
|
|
286
|
+
fileId: sourceScreenId,
|
|
287
|
+
before: sourceContent,
|
|
288
|
+
after: result.sourceHtml,
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
fileId: targetScreenId,
|
|
292
|
+
before: destContent,
|
|
293
|
+
after: nextDestContent,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
applyFileContentUpdate(sourceScreenId, result.sourceHtml, {
|
|
299
|
+
recordHistory: false,
|
|
300
|
+
refreshPreview: false,
|
|
301
|
+
forcePreviewFullDocument: true,
|
|
302
|
+
});
|
|
303
|
+
applyFileContentUpdate(targetScreenId, nextDestContent, {
|
|
304
|
+
recordHistory: false,
|
|
305
|
+
refreshPreview: false,
|
|
306
|
+
forcePreviewFullDocument: true,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Re-select the moved node in the destination.
|
|
310
|
+
const finalProjection = buildCodeLayerProjection(nextDestContent);
|
|
311
|
+
const movedNodeFinal = finalProjection.nodes.find(
|
|
312
|
+
(n) => n.dataAttributes["data-agent-native-node-id"] === destNodeAttrId,
|
|
313
|
+
);
|
|
314
|
+
if (movedNodeFinal) {
|
|
315
|
+
setSelectedLayerIdsState([movedNodeFinal.id]);
|
|
316
|
+
setSelectedElement(elementInfoFromCodeLayerNode(movedNodeFinal));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
2
|
+
import type { CanvasFrameGeometryById } from "@shared/canvas-frames";
|
|
3
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
4
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
8
|
+
import type { DesignClipboardScreenEntry } from "@/lib/design-import";
|
|
9
|
+
import {
|
|
10
|
+
nextDuplicatedFilename,
|
|
11
|
+
normalizedDesignFileType,
|
|
12
|
+
reassignDuplicatedNodeIds,
|
|
13
|
+
} from "@/pages/design-editor/canvas-primitive-insert";
|
|
14
|
+
import type { DesignFile, DesignTool } from "@/pages/design-editor/types";
|
|
15
|
+
|
|
16
|
+
export interface PasteCopiedScreensArgs {
|
|
17
|
+
canEditDesign: boolean;
|
|
18
|
+
canvasFrameGeometryById: CanvasFrameGeometryById;
|
|
19
|
+
createFileMutation: ReturnType<
|
|
20
|
+
typeof useActionMutation<undefined, undefined, "create-file">
|
|
21
|
+
>;
|
|
22
|
+
files: DesignFile[];
|
|
23
|
+
id: string | undefined;
|
|
24
|
+
pasteCascadeRef: RefObject<number>;
|
|
25
|
+
queryClient: QueryClient;
|
|
26
|
+
queueFrameGeometrySave: (geometryById: CanvasFrameGeometryById) => void;
|
|
27
|
+
setActiveFileId: Dispatch<SetStateAction<string | null>>;
|
|
28
|
+
setActiveTool: Dispatch<SetStateAction<DesignTool>>;
|
|
29
|
+
setOverviewSelectedScreenIds: Dispatch<SetStateAction<string[]>>;
|
|
30
|
+
setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
|
|
31
|
+
setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
|
|
32
|
+
setViewMode: Dispatch<SetStateAction<"single" | "overview">>;
|
|
33
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
34
|
+
viewModeRef: RefObject<"single" | "overview">;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function runPasteCopiedScreens(
|
|
38
|
+
{
|
|
39
|
+
canEditDesign,
|
|
40
|
+
canvasFrameGeometryById,
|
|
41
|
+
createFileMutation,
|
|
42
|
+
files,
|
|
43
|
+
id,
|
|
44
|
+
pasteCascadeRef,
|
|
45
|
+
queryClient,
|
|
46
|
+
queueFrameGeometrySave,
|
|
47
|
+
setActiveFileId,
|
|
48
|
+
setActiveTool,
|
|
49
|
+
setOverviewSelectedScreenIds,
|
|
50
|
+
setSelectedElement,
|
|
51
|
+
setSelectedLayerIdsState,
|
|
52
|
+
setViewMode,
|
|
53
|
+
t,
|
|
54
|
+
viewModeRef,
|
|
55
|
+
}: PasteCopiedScreensArgs,
|
|
56
|
+
screens: DesignClipboardScreenEntry[],
|
|
57
|
+
position?: { x: number; y: number },
|
|
58
|
+
) {
|
|
59
|
+
if (!id || !canEditDesign || screens.length === 0) return;
|
|
60
|
+
const pasteOffset = 32 + pasteCascadeRef.current * 16;
|
|
61
|
+
pasteCascadeRef.current += 1;
|
|
62
|
+
screens.forEach((screen, index) => {
|
|
63
|
+
const filename = nextDuplicatedFilename(files, screen.filename);
|
|
64
|
+
const content = reassignDuplicatedNodeIds(screen.content);
|
|
65
|
+
const sourceGeometry = screen.canvasFrame;
|
|
66
|
+
createFileMutation.mutate(
|
|
67
|
+
{
|
|
68
|
+
designId: id,
|
|
69
|
+
filename,
|
|
70
|
+
content,
|
|
71
|
+
fileType: normalizedDesignFileType(screen.fileType ?? "html"),
|
|
72
|
+
} as any,
|
|
73
|
+
{
|
|
74
|
+
onSuccess: (result: any) => {
|
|
75
|
+
const nextId = typeof result?.id === "string" ? result.id : null;
|
|
76
|
+
queryClient.invalidateQueries({
|
|
77
|
+
queryKey: ["action", "get-design"],
|
|
78
|
+
});
|
|
79
|
+
if (!nextId) return;
|
|
80
|
+
const nextX = position
|
|
81
|
+
? position.x + index * 16
|
|
82
|
+
: (sourceGeometry?.x ?? 0) + pasteOffset;
|
|
83
|
+
const nextY = position
|
|
84
|
+
? position.y + index * 16
|
|
85
|
+
: (sourceGeometry?.y ?? 0) + pasteOffset;
|
|
86
|
+
queueFrameGeometrySave({
|
|
87
|
+
...canvasFrameGeometryById,
|
|
88
|
+
[nextId]: {
|
|
89
|
+
...sourceGeometry,
|
|
90
|
+
x: nextX,
|
|
91
|
+
y: nextY,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
if (index === screens.length - 1) {
|
|
95
|
+
setActiveFileId(nextId);
|
|
96
|
+
setActiveTool("move");
|
|
97
|
+
setSelectedElement(null);
|
|
98
|
+
setSelectedLayerIdsState([nextId]);
|
|
99
|
+
viewModeRef.current = "overview";
|
|
100
|
+
setViewMode("overview");
|
|
101
|
+
setOverviewSelectedScreenIds([nextId]);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
onError: (error: unknown) => {
|
|
105
|
+
toast.error(
|
|
106
|
+
error instanceof Error
|
|
107
|
+
? error.message
|
|
108
|
+
: t("designEditor.toasts.screenDuplicateError"),
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
}
|