@agent-native/core 0.163.4 → 0.164.3
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/components/agents/FeatureFlagsFleetPanel.tsx +78 -19
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
- package/corpus/templates/analytics/app/lib/use-auto-focus-select.ts +4 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +6 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
- package/corpus/templates/analytics/app/routes/_index.tsx +3 -2
- package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +7 -2
- package/corpus/templates/analytics/app/routes/dashboard.tsx +3 -2
- package/corpus/templates/analytics/app/routes/overview.tsx +3 -2
- package/corpus/templates/analytics/app/routes/traffic.tsx +3 -2
- package/corpus/templates/analytics/changelog/2026-08-18-verify-cross-app-feature-flag-changes.md +6 -0
- package/corpus/templates/analytics/netlify.toml +2 -0
- package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +196 -48
- package/corpus/templates/analytics/server/plugins/feature-flags.ts +5 -0
- package/corpus/templates/analytics/shared/feature-flags.ts +10 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +5 -1
- package/corpus/templates/assets/app/routes/brand-kits._index.tsx +4 -1
- package/corpus/templates/assets/app/routes/libraries.tsx +4 -1
- package/corpus/templates/assets/app/routes/picker.tsx +4 -1
- 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/_app.wispr.tsx +2 -1
- package/corpus/templates/clips/app/routes/_index.tsx +7 -3
- 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/app/routes/examples.tsx +2 -1
- 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/app/routes/_index.tsx +3 -2
- package/corpus/templates/forms/netlify.toml +2 -0
- package/corpus/templates/mail/app/routes/_index.tsx +3 -2
- 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/corpus/templates/tasks/app/routes/_index.tsx +2 -1
- 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/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.js +10 -4
- 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/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- 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/server/ssr-handler.js +13 -4
- package/dist/settings/store.d.ts +9 -0
- package/dist/settings/store.js +19 -0
- package/dist/shared/cache-control.d.ts +22 -0
- package/dist/shared/cache-control.js +25 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -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,521 @@
|
|
|
1
|
+
import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
|
|
2
|
+
import {
|
|
3
|
+
buildCodeLayerProjection,
|
|
4
|
+
buildCodeLayerTree,
|
|
5
|
+
removeCodeLayerNodeFromHtml,
|
|
6
|
+
} from "@shared/code-layer";
|
|
7
|
+
import type { Dispatch, RefObject, SetStateAction } from "react";
|
|
8
|
+
import * as Y from "yjs";
|
|
9
|
+
|
|
10
|
+
import { trace } from "@/components/design/design-trace";
|
|
11
|
+
import type { ElementInfo } from "@/components/design/types";
|
|
12
|
+
import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
|
|
13
|
+
import {
|
|
14
|
+
codeLayerSelectorAliases,
|
|
15
|
+
collectCodeLayerAncestors,
|
|
16
|
+
collectCodeLayerSubtreeDataNodeIds,
|
|
17
|
+
elementInfoFromCodeLayerNode,
|
|
18
|
+
liveDeleteSelectorGroups,
|
|
19
|
+
preferredCodeLayerSelector,
|
|
20
|
+
removeEmptyGeneratedGroupWrappers,
|
|
21
|
+
resolveCodeLayerNodeFromElementInfo,
|
|
22
|
+
shouldDeleteThroughLiveScreen,
|
|
23
|
+
} from "@/pages/design-editor/code-layer-state";
|
|
24
|
+
import type {
|
|
25
|
+
LiveScreenSnapshot,
|
|
26
|
+
ResponsiveEditScope,
|
|
27
|
+
SelectedCanvasLayerSnapshot,
|
|
28
|
+
} from "@/pages/design-editor/command-types";
|
|
29
|
+
import { applyScopedVisualStyleEdit } from "@/pages/design-editor/pending-edits";
|
|
30
|
+
import { removeElementFromHtml } from "@/pages/design-editor/text-edit-utils";
|
|
31
|
+
import type { DesignFile } from "@/pages/design-editor/types";
|
|
32
|
+
|
|
33
|
+
export interface DeleteSelectionArgs {
|
|
34
|
+
activeBreakpointUpperBoundPx: number | null;
|
|
35
|
+
activeBreakpointWidthStateRef: RefObject<number | undefined>;
|
|
36
|
+
activeCanvasSourceType: "inline" | "localhost" | "fusion";
|
|
37
|
+
activeFile: DesignFile;
|
|
38
|
+
applyFileContentUpdate: (
|
|
39
|
+
fileId: string,
|
|
40
|
+
nextContent: string,
|
|
41
|
+
options?: {
|
|
42
|
+
refreshPreview?: boolean;
|
|
43
|
+
skipPreview?: boolean;
|
|
44
|
+
forcePreviewFullDocument?: boolean;
|
|
45
|
+
persist?: boolean;
|
|
46
|
+
recordHistory?: boolean;
|
|
47
|
+
updatedAt?: string;
|
|
48
|
+
clipboardMutation?: ClipboardContentMutationPublication;
|
|
49
|
+
},
|
|
50
|
+
) => void;
|
|
51
|
+
applyLocalContentUpdate: (
|
|
52
|
+
nextContent: string,
|
|
53
|
+
options?: {
|
|
54
|
+
refreshPreview?: boolean;
|
|
55
|
+
skipPreview?: boolean;
|
|
56
|
+
forcePreviewFullDocument?: boolean;
|
|
57
|
+
immediateSave?: boolean;
|
|
58
|
+
persist?: boolean;
|
|
59
|
+
recordHistory?: boolean;
|
|
60
|
+
historyBeforeContent?: string;
|
|
61
|
+
updatedAt?: string;
|
|
62
|
+
clipboardMutation?: ClipboardContentMutationPublication;
|
|
63
|
+
},
|
|
64
|
+
) => void;
|
|
65
|
+
canEditDesign: boolean;
|
|
66
|
+
codeLayerOwnerByNodeIdRef: RefObject<
|
|
67
|
+
Map<
|
|
68
|
+
string,
|
|
69
|
+
{
|
|
70
|
+
fileId: string;
|
|
71
|
+
node: CodeLayerNode;
|
|
72
|
+
tree: CodeLayerTreeNode[];
|
|
73
|
+
runtimeOnly: boolean;
|
|
74
|
+
}
|
|
75
|
+
>
|
|
76
|
+
>;
|
|
77
|
+
deleteRuntimeElement: (
|
|
78
|
+
selector?: string | null,
|
|
79
|
+
candidates?: readonly string[],
|
|
80
|
+
requestId?: string,
|
|
81
|
+
) => boolean;
|
|
82
|
+
files: DesignFile[];
|
|
83
|
+
getFreshActiveContent: () => string;
|
|
84
|
+
getScreenContent: (screenId: string) => string;
|
|
85
|
+
getSelectedLayerSnapshots: () => SelectedCanvasLayerSnapshot[];
|
|
86
|
+
liveScreenSnapshotsById: Record<string, LiveScreenSnapshot>;
|
|
87
|
+
previousMotionFileIdRef: RefObject<string | null>;
|
|
88
|
+
pruneMotionTracksByNodeId: (nodeIdsToRemove: Set<string>) => void;
|
|
89
|
+
recordPendingLiveStructureEdit: (
|
|
90
|
+
screenId: string,
|
|
91
|
+
selector: string,
|
|
92
|
+
anchorSelector: string,
|
|
93
|
+
placement: "before" | "after" | "inside",
|
|
94
|
+
elementInfo?: ElementInfo,
|
|
95
|
+
details?: {
|
|
96
|
+
sourceId?: string;
|
|
97
|
+
anchorSourceId?: string;
|
|
98
|
+
anchorElementInfo?: ElementInfo;
|
|
99
|
+
requestId?: string;
|
|
100
|
+
dropMode?: "flow-insert" | "absolute-container";
|
|
101
|
+
forceFlowPositionOverride?: boolean;
|
|
102
|
+
sourceRect?: { x: number; y: number; width: number; height: number };
|
|
103
|
+
anchorRect?: { x: number; y: number; width: number; height: number };
|
|
104
|
+
insertedHtml?: string;
|
|
105
|
+
replaced?: true;
|
|
106
|
+
replacementSelector?: string;
|
|
107
|
+
replacementSourceId?: string;
|
|
108
|
+
removed?: true;
|
|
109
|
+
},
|
|
110
|
+
) => void;
|
|
111
|
+
responsiveEditScopeRef: RefObject<ResponsiveEditScope>;
|
|
112
|
+
selectedElement: ElementInfo | null;
|
|
113
|
+
selectedLayerIdsState: string[];
|
|
114
|
+
setOverviewSelectedScreenIds: Dispatch<SetStateAction<string[]>>;
|
|
115
|
+
setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
|
|
116
|
+
setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
|
|
117
|
+
syncLiveScreenSnapshotPreview: (screenId: string, html: string) => void;
|
|
118
|
+
undoManagerRef: RefObject<Y.UndoManager | null>;
|
|
119
|
+
updateLiveScreenSnapshotContent: (
|
|
120
|
+
screenId: string,
|
|
121
|
+
html: string,
|
|
122
|
+
options?: { recordHistory?: boolean },
|
|
123
|
+
) => boolean;
|
|
124
|
+
viewModeRef: RefObject<"single" | "overview">;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function runDeleteSelection({
|
|
128
|
+
activeBreakpointUpperBoundPx,
|
|
129
|
+
activeBreakpointWidthStateRef,
|
|
130
|
+
activeCanvasSourceType,
|
|
131
|
+
activeFile,
|
|
132
|
+
applyFileContentUpdate,
|
|
133
|
+
applyLocalContentUpdate,
|
|
134
|
+
canEditDesign,
|
|
135
|
+
codeLayerOwnerByNodeIdRef,
|
|
136
|
+
deleteRuntimeElement,
|
|
137
|
+
files,
|
|
138
|
+
getFreshActiveContent,
|
|
139
|
+
getScreenContent,
|
|
140
|
+
getSelectedLayerSnapshots,
|
|
141
|
+
liveScreenSnapshotsById,
|
|
142
|
+
previousMotionFileIdRef,
|
|
143
|
+
pruneMotionTracksByNodeId,
|
|
144
|
+
recordPendingLiveStructureEdit,
|
|
145
|
+
responsiveEditScopeRef,
|
|
146
|
+
selectedElement,
|
|
147
|
+
selectedLayerIdsState,
|
|
148
|
+
setOverviewSelectedScreenIds,
|
|
149
|
+
setSelectedElement,
|
|
150
|
+
setSelectedLayerIdsState,
|
|
151
|
+
syncLiveScreenSnapshotPreview,
|
|
152
|
+
undoManagerRef,
|
|
153
|
+
updateLiveScreenSnapshotContent,
|
|
154
|
+
viewModeRef,
|
|
155
|
+
}: DeleteSelectionArgs) {
|
|
156
|
+
trace("structure", "delete", { layers: selectedLayerIdsState.length });
|
|
157
|
+
if (!canEditDesign) return;
|
|
158
|
+
// U19: delete is a discrete one-shot action — see the matching note in
|
|
159
|
+
// handlePasteSelection.
|
|
160
|
+
undoManagerRef.current?.stopCapturing();
|
|
161
|
+
// BUG-DELETE-LIVE-NAMESPACE: the projections below are built from the
|
|
162
|
+
// fetched source snapshot, whose node ids are a different namespace from
|
|
163
|
+
// the live document's — see liveDeleteSelectorGroups for why a selector
|
|
164
|
+
// taken from them silently removed nothing in the iframe.
|
|
165
|
+
const runtimeAliasGroups = selectedLayerIdsState
|
|
166
|
+
.map((layerId) => codeLayerOwnerByNodeIdRef.current.get(layerId))
|
|
167
|
+
.filter((owner) => owner?.runtimeOnly)
|
|
168
|
+
.map((owner) => codeLayerSelectorAliases(owner!.node));
|
|
169
|
+
const liveSelectionSelectors = [
|
|
170
|
+
selectedElement?.runtimeSelector,
|
|
171
|
+
selectedElement?.runtimeSourceId
|
|
172
|
+
? `[data-agent-native-node-id="${selectedElement.runtimeSourceId}"]`
|
|
173
|
+
: undefined,
|
|
174
|
+
selectedElement?.selector,
|
|
175
|
+
].filter((selector): selector is string => Boolean(selector));
|
|
176
|
+
const hasLiveDeleteTarget =
|
|
177
|
+
runtimeAliasGroups.length > 0 || liveSelectionSelectors.length > 0;
|
|
178
|
+
const deleteFromLiveDom = (fallbackSelectors: readonly string[]) => {
|
|
179
|
+
liveDeleteSelectorGroups({
|
|
180
|
+
runtimeAliasGroups,
|
|
181
|
+
liveSelectionSelectors,
|
|
182
|
+
fallbackSelectors,
|
|
183
|
+
}).forEach((aliases) => deleteRuntimeElement(aliases[0], aliases));
|
|
184
|
+
};
|
|
185
|
+
// BUG-DELETE-LIVE-PENDING: a live screen's source is the running app, so
|
|
186
|
+
// the delete cannot be written into DesignFile.content the way an inline
|
|
187
|
+
// screen's is. It removes the node from the running DOM and queues a
|
|
188
|
+
// pending live edit for the coding agent — the same split
|
|
189
|
+
// handleVisualStructureChange already makes for a localhost drag-move.
|
|
190
|
+
// Recording it is also what makes Cmd+Z work: undo pops this entry and the
|
|
191
|
+
// requestId it carries tells the bridge to re-attach the node it detached.
|
|
192
|
+
if (
|
|
193
|
+
activeFile &&
|
|
194
|
+
shouldDeleteThroughLiveScreen({
|
|
195
|
+
screenSourceType: activeCanvasSourceType,
|
|
196
|
+
runtimeAliasGroups,
|
|
197
|
+
liveSelectionSelectors,
|
|
198
|
+
})
|
|
199
|
+
) {
|
|
200
|
+
// Only the active screen's canvas registers the runtime bridge, so its
|
|
201
|
+
// is the only live DOM a host-driven delete can reach.
|
|
202
|
+
const runtimeTargets = selectedLayerIdsState
|
|
203
|
+
.map((layerId) => codeLayerOwnerByNodeIdRef.current.get(layerId))
|
|
204
|
+
.filter((owner) => owner?.runtimeOnly && owner.fileId === activeFile.id)
|
|
205
|
+
.map((owner) => ({
|
|
206
|
+
aliases: codeLayerSelectorAliases(owner!.node),
|
|
207
|
+
info: elementInfoFromCodeLayerNode(owner!.node),
|
|
208
|
+
sourceId:
|
|
209
|
+
owner!.node.dataAttributes["data-agent-native-node-id"] ??
|
|
210
|
+
owner!.node.id,
|
|
211
|
+
}));
|
|
212
|
+
const targets =
|
|
213
|
+
runtimeTargets.length > 0
|
|
214
|
+
? runtimeTargets
|
|
215
|
+
: [
|
|
216
|
+
{
|
|
217
|
+
aliases: Array.from(new Set(liveSelectionSelectors)),
|
|
218
|
+
info: selectedElement ?? undefined,
|
|
219
|
+
sourceId:
|
|
220
|
+
selectedElement?.runtimeSourceId ??
|
|
221
|
+
selectedElement?.sourceId ??
|
|
222
|
+
undefined,
|
|
223
|
+
},
|
|
224
|
+
];
|
|
225
|
+
let deletedAny = false;
|
|
226
|
+
for (const target of targets) {
|
|
227
|
+
const primary = target.aliases[0];
|
|
228
|
+
if (!primary) continue;
|
|
229
|
+
const requestId = `delete-${Date.now().toString(36)}-${Math.random()
|
|
230
|
+
.toString(16)
|
|
231
|
+
.slice(2)}`;
|
|
232
|
+
if (!deleteRuntimeElement(primary, target.aliases, requestId)) continue;
|
|
233
|
+
deletedAny = true;
|
|
234
|
+
recordPendingLiveStructureEdit(
|
|
235
|
+
activeFile.id,
|
|
236
|
+
primary,
|
|
237
|
+
// A removal has no anchor; `placement` is carried only because the
|
|
238
|
+
// pending-edit shape is shared with moves and inserts.
|
|
239
|
+
"",
|
|
240
|
+
"after",
|
|
241
|
+
target.info,
|
|
242
|
+
{ sourceId: target.sourceId, requestId, removed: true },
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (!deletedAny) return;
|
|
246
|
+
setSelectedElement(null);
|
|
247
|
+
setSelectedLayerIdsState([]);
|
|
248
|
+
if (viewModeRef.current === "overview") {
|
|
249
|
+
setOverviewSelectedScreenIds([]);
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const snapshots = getSelectedLayerSnapshots();
|
|
254
|
+
if (snapshots.length > 0) {
|
|
255
|
+
const activeRuntimeSelectors: string[] = [];
|
|
256
|
+
let shouldDeleteActiveLiveDom = false;
|
|
257
|
+
let didDelete = false;
|
|
258
|
+
// U14: motion tracks left targeting a deleted node's id would animate
|
|
259
|
+
// nothing. Collected across every deleted subtree in the active file
|
|
260
|
+
// (tracks aren't kept per-file, only for whichever file's timeline is
|
|
261
|
+
// currently loaded) and pruned from motionTracks once after the loop.
|
|
262
|
+
let orphanedTrackNodeIds: Set<string> | null = null;
|
|
263
|
+
for (const file of files) {
|
|
264
|
+
const group = snapshots.filter(
|
|
265
|
+
(snapshot) => snapshot.sourceFileId === file.id,
|
|
266
|
+
);
|
|
267
|
+
if (group.length === 0) continue;
|
|
268
|
+
// BUG-DELETE-LIVE-SNAPSHOT: see the matching note in
|
|
269
|
+
// getSelectedLayerSnapshots — file.content is a bare URL for a
|
|
270
|
+
// localhost/live-snapshot screen, so removeCodeLayerNodeFromHtml
|
|
271
|
+
// below could never find anything to remove. Use the live snapshot
|
|
272
|
+
// HTML when this screen has one.
|
|
273
|
+
const liveSnapshot = liveScreenSnapshotsById[file.id];
|
|
274
|
+
const originalContent = liveSnapshot?.html ?? getScreenContent(file.id);
|
|
275
|
+
let content = originalContent;
|
|
276
|
+
const projection = buildCodeLayerProjection(content);
|
|
277
|
+
const tree = buildCodeLayerTree(projection);
|
|
278
|
+
const nodesById = new Map(
|
|
279
|
+
projection.nodes.map((node) => [node.id, node]),
|
|
280
|
+
);
|
|
281
|
+
const selectedNodeIds = new Set(
|
|
282
|
+
group.map((snapshot) => snapshot.node.id),
|
|
283
|
+
);
|
|
284
|
+
const nodes = group
|
|
285
|
+
.map((snapshot) =>
|
|
286
|
+
projection.nodes.find(
|
|
287
|
+
(node) =>
|
|
288
|
+
node.id === snapshot.node.id ||
|
|
289
|
+
node.dataAttributes["data-agent-native-node-id"] ===
|
|
290
|
+
snapshot.rootNodeId,
|
|
291
|
+
),
|
|
292
|
+
)
|
|
293
|
+
.filter((node): node is CodeLayerNode => Boolean(node?.source))
|
|
294
|
+
.filter(
|
|
295
|
+
(node) =>
|
|
296
|
+
!collectCodeLayerAncestors(tree, node.id).some((ancestorId) =>
|
|
297
|
+
selectedNodeIds.has(ancestorId),
|
|
298
|
+
),
|
|
299
|
+
)
|
|
300
|
+
.sort((a, b) => (b.source?.start ?? 0) - (a.source?.start ?? 0));
|
|
301
|
+
if (nodes.length === 0) continue;
|
|
302
|
+
const removedSelectors: string[] = [];
|
|
303
|
+
// L25: track each deleted node's former parent (by stable
|
|
304
|
+
// data-agent-native-node-id) so we can sweep for now-empty generated
|
|
305
|
+
// "Group" wrappers once every deletion in this file is applied. Only
|
|
306
|
+
// meaningful for the structural-removal path below — a
|
|
307
|
+
// breakpoint-scoped display:none write never empties a parent (the
|
|
308
|
+
// node is still in the DOM, just hidden at that width).
|
|
309
|
+
const formerParentAttrIds = new Set<string>();
|
|
310
|
+
// Item 7b — while a breakpoint is the active edit target, Delete
|
|
311
|
+
// must not structurally remove the element (that would remove it at
|
|
312
|
+
// EVERY width, defeating the point of scoping). Instead it writes a
|
|
313
|
+
// display:none override scoped to the active breakpoint's upper
|
|
314
|
+
// bound, through the exact same planBreakpointStyleWrite routing
|
|
315
|
+
// regular style edits use (applyScopedVisualStyleEdit / see
|
|
316
|
+
// commitVisualStyles' matching upperBoundPx-gated branch above).
|
|
317
|
+
// Only file.id === activeFile?.id can be the breakpoint-scoped
|
|
318
|
+
// target — activeBreakpointUpperBoundPx describes the ACTIVE
|
|
319
|
+
// screen's viewport scope, not other files' — so a multi-screen
|
|
320
|
+
// overview selection spanning other screens still deletes those
|
|
321
|
+
// structurally.
|
|
322
|
+
const useBreakpointScopedDelete =
|
|
323
|
+
activeBreakpointWidthStateRef.current !== undefined &&
|
|
324
|
+
file.id === activeFile?.id &&
|
|
325
|
+
activeBreakpointUpperBoundPx != null;
|
|
326
|
+
for (const node of nodes) {
|
|
327
|
+
if (useBreakpointScopedDelete) {
|
|
328
|
+
const nodeId =
|
|
329
|
+
node.dataAttributes["data-agent-native-node-id"] ?? node.id;
|
|
330
|
+
const patch = applyScopedVisualStyleEdit({
|
|
331
|
+
content,
|
|
332
|
+
target: { nodeId },
|
|
333
|
+
property: "display",
|
|
334
|
+
value: "none",
|
|
335
|
+
upperBoundPx: activeBreakpointUpperBoundPx,
|
|
336
|
+
lowerBoundPx:
|
|
337
|
+
responsiveEditScopeRef.current === "only"
|
|
338
|
+
? activeBreakpointWidthStateRef.current
|
|
339
|
+
: null,
|
|
340
|
+
});
|
|
341
|
+
if (patch.result.status !== "applied") continue;
|
|
342
|
+
content = patch.content;
|
|
343
|
+
// Not a structural removal: the node stays selectable at Base /
|
|
344
|
+
// a wider breakpoint, so it must not be treated as "removed"
|
|
345
|
+
// for the runtime-selector cleanup, former-parent sweep, or
|
|
346
|
+
// motion-track pruning below.
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (node.parentId) {
|
|
350
|
+
const parentNode = nodesById.get(node.parentId);
|
|
351
|
+
const parentAttrId =
|
|
352
|
+
parentNode?.dataAttributes["data-agent-native-node-id"];
|
|
353
|
+
if (parentAttrId) formerParentAttrIds.add(parentAttrId);
|
|
354
|
+
}
|
|
355
|
+
const next = removeCodeLayerNodeFromHtml(content, node);
|
|
356
|
+
if (!next) continue;
|
|
357
|
+
const selector = preferredCodeLayerSelector(node);
|
|
358
|
+
if (selector) removedSelectors.push(selector);
|
|
359
|
+
content = next;
|
|
360
|
+
if (file.id === previousMotionFileIdRef.current) {
|
|
361
|
+
const subtreeIds = collectCodeLayerSubtreeDataNodeIds(
|
|
362
|
+
tree,
|
|
363
|
+
node.id,
|
|
364
|
+
nodesById,
|
|
365
|
+
);
|
|
366
|
+
if (subtreeIds.size > 0) {
|
|
367
|
+
orphanedTrackNodeIds ??= new Set();
|
|
368
|
+
subtreeIds.forEach((id) => orphanedTrackNodeIds!.add(id));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (content === originalContent) continue;
|
|
373
|
+
if (!useBreakpointScopedDelete) {
|
|
374
|
+
content = removeEmptyGeneratedGroupWrappers(
|
|
375
|
+
content,
|
|
376
|
+
formerParentAttrIds,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
if (file.id === activeFile?.id && !useBreakpointScopedDelete) {
|
|
380
|
+
activeRuntimeSelectors.push(...removedSelectors);
|
|
381
|
+
shouldDeleteActiveLiveDom = true;
|
|
382
|
+
}
|
|
383
|
+
didDelete = true;
|
|
384
|
+
if (liveSnapshot) {
|
|
385
|
+
// Records the same ContentHistoryChange shape as
|
|
386
|
+
// applyFileContentUpdate, so this delete gets a real undo/redo
|
|
387
|
+
// entry that now also re-syncs the live iframe (see
|
|
388
|
+
// syncLiveScreenSnapshotPreview) instead of only updating the
|
|
389
|
+
// model liveScreenSnapshotsById state.
|
|
390
|
+
const updated = updateLiveScreenSnapshotContent(file.id, content);
|
|
391
|
+
if (updated && useBreakpointScopedDelete) {
|
|
392
|
+
syncLiveScreenSnapshotPreview(file.id, content);
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
// Item 5 (edit-flash) parity: a breakpoint-scoped write can become a
|
|
396
|
+
// width-scoped class OR a managed @media rule (planBreakpointStyleWrite),
|
|
397
|
+
// neither of which the runtime bridge's inline-style shortcut can
|
|
398
|
+
// preview correctly — force a full preview refresh the same way
|
|
399
|
+
// commitVisualStyles does for breakpoint-scoped style commits,
|
|
400
|
+
// instead of the optimistic refreshPreview:false structural-delete
|
|
401
|
+
// path.
|
|
402
|
+
applyFileContentUpdate(file.id, content, {
|
|
403
|
+
refreshPreview: false,
|
|
404
|
+
forcePreviewFullDocument: useBreakpointScopedDelete,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// A live screen's snapshot rewrite can come up empty (different id
|
|
409
|
+
// namespace) while the live-DOM delete is still the real, visible
|
|
410
|
+
// operation — bail only when NEITHER has anything to remove.
|
|
411
|
+
if (!didDelete && !hasLiveDeleteTarget) return;
|
|
412
|
+
if (orphanedTrackNodeIds) {
|
|
413
|
+
const idsToRemove = orphanedTrackNodeIds;
|
|
414
|
+
// U14 fix: mark motion dirty when a track is actually pruned so the
|
|
415
|
+
// autosave/remove-motion-timeline path persists the cleanup. Without
|
|
416
|
+
// this the filtered tracks live only in memory and the stale managed
|
|
417
|
+
// CSS + timeline row reappear on reload. markMotionTracksDirty is only
|
|
418
|
+
// invoked when the filter drops at least one track; a redundant call
|
|
419
|
+
// (e.g. a StrictMode double render) is harmless — it just bumps the
|
|
420
|
+
// autosave revision, which the autosave effect dedupes.
|
|
421
|
+
pruneMotionTracksByNodeId(idsToRemove);
|
|
422
|
+
}
|
|
423
|
+
if (shouldDeleteActiveLiveDom) {
|
|
424
|
+
deleteFromLiveDom(activeRuntimeSelectors);
|
|
425
|
+
}
|
|
426
|
+
setSelectedElement(null);
|
|
427
|
+
setSelectedLayerIdsState([]);
|
|
428
|
+
if (viewModeRef.current === "overview") {
|
|
429
|
+
setOverviewSelectedScreenIds([]);
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (!selectedElement?.selector) return;
|
|
435
|
+
const activeLiveSnapshot = activeFile
|
|
436
|
+
? liveScreenSnapshotsById[activeFile.id]
|
|
437
|
+
: undefined;
|
|
438
|
+
const baseContent = activeLiveSnapshot?.html ?? getFreshActiveContent();
|
|
439
|
+
// Item 7b — same breakpoint-scoped display:none routing as the
|
|
440
|
+
// multi-layer-snapshot branch above, for the single-runtime-selected-
|
|
441
|
+
// element fallback path (e.g. single-screen canvas click-select with no
|
|
442
|
+
// layers-panel snapshot).
|
|
443
|
+
if (
|
|
444
|
+
activeBreakpointWidthStateRef.current !== undefined &&
|
|
445
|
+
activeBreakpointUpperBoundPx != null
|
|
446
|
+
) {
|
|
447
|
+
const projection = buildCodeLayerProjection(baseContent);
|
|
448
|
+
const targetNode = resolveCodeLayerNodeFromElementInfo(
|
|
449
|
+
projection,
|
|
450
|
+
selectedElement,
|
|
451
|
+
);
|
|
452
|
+
const nodeId =
|
|
453
|
+
targetNode?.dataAttributes["data-agent-native-node-id"] ??
|
|
454
|
+
targetNode?.id ??
|
|
455
|
+
selectedElement.sourceId;
|
|
456
|
+
const patch = nodeId
|
|
457
|
+
? applyScopedVisualStyleEdit({
|
|
458
|
+
content: baseContent,
|
|
459
|
+
target: { nodeId },
|
|
460
|
+
property: "display",
|
|
461
|
+
value: "none",
|
|
462
|
+
upperBoundPx: activeBreakpointUpperBoundPx,
|
|
463
|
+
lowerBoundPx:
|
|
464
|
+
responsiveEditScopeRef.current === "only"
|
|
465
|
+
? activeBreakpointWidthStateRef.current
|
|
466
|
+
: null,
|
|
467
|
+
})
|
|
468
|
+
: null;
|
|
469
|
+
if (patch && patch.result.status === "applied") {
|
|
470
|
+
if (activeLiveSnapshot) {
|
|
471
|
+
if (updateLiveScreenSnapshotContent(activeFile!.id, patch.content)) {
|
|
472
|
+
syncLiveScreenSnapshotPreview(activeFile!.id, patch.content);
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
applyLocalContentUpdate(patch.content, {
|
|
476
|
+
refreshPreview: false,
|
|
477
|
+
forcePreviewFullDocument: true,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
setSelectedElement(null);
|
|
481
|
+
setSelectedLayerIdsState([]);
|
|
482
|
+
}
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const nextContent = removeElementFromHtml(
|
|
486
|
+
baseContent,
|
|
487
|
+
selectedElement.selector,
|
|
488
|
+
);
|
|
489
|
+
if (!nextContent) return;
|
|
490
|
+
// U14: orphan-track cleanup for the single-element fallback path too.
|
|
491
|
+
if (
|
|
492
|
+
selectedElement.sourceId &&
|
|
493
|
+
previousMotionFileIdRef.current === activeFile?.id
|
|
494
|
+
) {
|
|
495
|
+
const projection = buildCodeLayerProjection(baseContent);
|
|
496
|
+
const tree = buildCodeLayerTree(projection);
|
|
497
|
+
const nodesById = new Map(projection.nodes.map((node) => [node.id, node]));
|
|
498
|
+
const targetNode = projection.nodes.find(
|
|
499
|
+
(node) =>
|
|
500
|
+
node.id === selectedElement.sourceId ||
|
|
501
|
+
node.dataAttributes["data-agent-native-node-id"] ===
|
|
502
|
+
selectedElement.sourceId,
|
|
503
|
+
);
|
|
504
|
+
const subtreeIds = targetNode
|
|
505
|
+
? collectCodeLayerSubtreeDataNodeIds(tree, targetNode.id, nodesById)
|
|
506
|
+
: new Set<string>();
|
|
507
|
+
if (subtreeIds.size > 0) {
|
|
508
|
+
// U14 fix: same as the multi-layer path above — persist the orphan
|
|
509
|
+
// cleanup by marking motion dirty when a track is actually pruned.
|
|
510
|
+
pruneMotionTracksByNodeId(subtreeIds);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
deleteFromLiveDom([selectedElement.selector]);
|
|
514
|
+
if (activeLiveSnapshot) {
|
|
515
|
+
updateLiveScreenSnapshotContent(activeFile!.id, nextContent);
|
|
516
|
+
} else {
|
|
517
|
+
applyLocalContentUpdate(nextContent, { refreshPreview: false });
|
|
518
|
+
}
|
|
519
|
+
setSelectedElement(null);
|
|
520
|
+
setSelectedLayerIdsState([]);
|
|
521
|
+
}
|
package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useActionMutation } from "@agent-native/core/client/hooks";
|
|
2
|
+
import { toast } from "sonner";
|
|
3
|
+
|
|
4
|
+
import type { DesignFile } from "@/pages/design-editor/types";
|
|
5
|
+
|
|
6
|
+
export interface DetachInstanceMenuActionArgs {
|
|
7
|
+
activeContent: string;
|
|
8
|
+
activeFile: DesignFile;
|
|
9
|
+
activeFileId: string | null;
|
|
10
|
+
detachComponentInstanceMutation: ReturnType<
|
|
11
|
+
typeof useActionMutation<undefined, undefined, "detach-component-instance">
|
|
12
|
+
>;
|
|
13
|
+
handleComponentPropApplied: (
|
|
14
|
+
fileId: string,
|
|
15
|
+
nextContent: string,
|
|
16
|
+
updatedAt?: string,
|
|
17
|
+
) => void;
|
|
18
|
+
id: string | undefined;
|
|
19
|
+
selectedComponentNodeId: string | undefined;
|
|
20
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function runDetachInstanceMenuAction({
|
|
24
|
+
activeContent,
|
|
25
|
+
activeFile,
|
|
26
|
+
activeFileId,
|
|
27
|
+
detachComponentInstanceMutation,
|
|
28
|
+
handleComponentPropApplied,
|
|
29
|
+
id,
|
|
30
|
+
selectedComponentNodeId,
|
|
31
|
+
t,
|
|
32
|
+
}: DetachInstanceMenuActionArgs) {
|
|
33
|
+
if (!id || !selectedComponentNodeId) return;
|
|
34
|
+
detachComponentInstanceMutation.mutate(
|
|
35
|
+
{
|
|
36
|
+
designId: id,
|
|
37
|
+
nodeId: selectedComponentNodeId,
|
|
38
|
+
fileId: activeFileId ?? undefined,
|
|
39
|
+
...(activeContent
|
|
40
|
+
? {
|
|
41
|
+
source: {
|
|
42
|
+
currentContent: activeContent,
|
|
43
|
+
...(activeFile?.updatedAt
|
|
44
|
+
? { revision: activeFile.updatedAt }
|
|
45
|
+
: {}),
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
: {}),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
onSuccess: (result: {
|
|
52
|
+
detached?: boolean;
|
|
53
|
+
conflict?: boolean;
|
|
54
|
+
ctaRequired?: boolean;
|
|
55
|
+
error?: string;
|
|
56
|
+
ctaMessage?: string;
|
|
57
|
+
note?: string;
|
|
58
|
+
fileId?: string;
|
|
59
|
+
content?: string;
|
|
60
|
+
updatedAt?: string;
|
|
61
|
+
}) => {
|
|
62
|
+
if (result.conflict || result.ctaRequired) {
|
|
63
|
+
toast.error(
|
|
64
|
+
result.error ??
|
|
65
|
+
result.ctaMessage ??
|
|
66
|
+
t("designEditor.componentInstances.detachFailed"),
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (result.detached) {
|
|
71
|
+
toast(result.note ?? t("designEditor.componentInstances.detached"));
|
|
72
|
+
if (
|
|
73
|
+
typeof result.fileId === "string" &&
|
|
74
|
+
typeof result.content === "string"
|
|
75
|
+
) {
|
|
76
|
+
handleComponentPropApplied(
|
|
77
|
+
result.fileId,
|
|
78
|
+
result.content,
|
|
79
|
+
result.updatedAt,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
onError: () =>
|
|
85
|
+
toast.error(t("designEditor.componentInstances.detachFailed")),
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
}
|