@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.
Files changed (228) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/components/agents/FeatureFlagsFleetPanel.tsx +78 -19
  3. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
  4. package/corpus/templates/analytics/app/lib/use-auto-focus-select.ts +4 -4
  5. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -0
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +6 -0
  7. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
  8. package/corpus/templates/analytics/app/routes/_index.tsx +3 -2
  9. package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +7 -2
  10. package/corpus/templates/analytics/app/routes/dashboard.tsx +3 -2
  11. package/corpus/templates/analytics/app/routes/overview.tsx +3 -2
  12. package/corpus/templates/analytics/app/routes/traffic.tsx +3 -2
  13. package/corpus/templates/analytics/changelog/2026-08-18-verify-cross-app-feature-flag-changes.md +6 -0
  14. package/corpus/templates/analytics/netlify.toml +2 -0
  15. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +196 -48
  16. package/corpus/templates/analytics/server/plugins/feature-flags.ts +5 -0
  17. package/corpus/templates/analytics/shared/feature-flags.ts +10 -0
  18. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +5 -1
  19. package/corpus/templates/assets/app/routes/brand-kits._index.tsx +4 -1
  20. package/corpus/templates/assets/app/routes/libraries.tsx +4 -1
  21. package/corpus/templates/assets/app/routes/picker.tsx +4 -1
  22. package/corpus/templates/assets/netlify.toml +2 -0
  23. package/corpus/templates/brain/netlify.toml +2 -0
  24. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
  25. package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
  26. package/corpus/templates/calendar/netlify.toml +2 -0
  27. package/corpus/templates/chat/netlify.toml +2 -0
  28. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
  29. package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
  30. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
  31. package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
  32. package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
  33. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
  34. package/corpus/templates/clips/app/routes/_app.wispr.tsx +2 -1
  35. package/corpus/templates/clips/app/routes/_index.tsx +7 -3
  36. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
  37. package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
  38. package/corpus/templates/clips/netlify.toml +2 -0
  39. package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
  40. package/corpus/templates/content/netlify.toml +2 -0
  41. package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
  42. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
  43. package/corpus/templates/design/AGENTS.md +3 -1
  44. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
  45. package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
  46. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
  47. package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
  48. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
  49. package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
  50. package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
  51. package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
  52. package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
  53. package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
  54. package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
  55. package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
  56. package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
  57. package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
  58. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
  59. package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
  60. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
  61. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
  62. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
  63. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
  64. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
  65. package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
  66. package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
  67. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
  68. package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
  69. package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
  70. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
  71. package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
  72. package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
  73. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
  74. package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
  75. package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
  76. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
  77. package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
  78. package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
  79. package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
  80. package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
  81. package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
  82. package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
  83. package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
  84. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
  85. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
  86. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
  87. package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
  88. package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
  89. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
  90. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
  91. package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
  92. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
  93. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
  94. package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
  95. package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
  96. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
  97. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
  98. package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
  99. package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
  100. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
  101. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
  102. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
  103. package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
  104. package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
  105. package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
  106. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
  107. package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
  108. package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
  109. package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
  110. package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
  111. package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
  112. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
  113. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
  114. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
  115. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
  116. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
  117. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
  118. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
  119. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
  120. package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
  121. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
  122. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
  123. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
  124. package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
  126. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
  127. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
  128. package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
  129. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
  130. package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
  131. package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
  132. package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
  133. package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
  134. package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
  135. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
  136. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
  137. package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
  138. package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
  139. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
  140. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
  141. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
  142. package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
  143. package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
  144. package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
  145. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
  146. package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
  147. package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
  148. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
  149. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
  150. package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
  151. package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
  152. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
  153. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
  154. package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
  155. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
  156. package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
  157. package/corpus/templates/design/app/routes/examples.tsx +2 -1
  158. package/corpus/templates/design/netlify.toml +2 -0
  159. package/corpus/templates/design/shared/board-file.ts +2 -1
  160. package/corpus/templates/dispatch/netlify.toml +2 -0
  161. package/corpus/templates/forms/app/routes/_index.tsx +3 -2
  162. package/corpus/templates/forms/netlify.toml +2 -0
  163. package/corpus/templates/mail/app/routes/_index.tsx +3 -2
  164. package/corpus/templates/mail/netlify.toml +2 -0
  165. package/corpus/templates/plan/netlify.toml +2 -0
  166. package/corpus/templates/slides/netlify.toml +2 -0
  167. package/corpus/templates/tasks/app/routes/_index.tsx +2 -1
  168. package/dist/agent/engine/builder-engine.d.ts +6 -4
  169. package/dist/agent/engine/builder-engine.js +91 -26
  170. package/dist/agent/engine/registry.js +16 -0
  171. package/dist/cli/code-agent-agent-tools.d.ts +2 -0
  172. package/dist/cli/code-agent-agent-tools.js +188 -0
  173. package/dist/cli/code-agent-collaboration.d.ts +34 -0
  174. package/dist/cli/code-agent-collaboration.js +104 -0
  175. package/dist/cli/code-agent-executor.js +6 -0
  176. package/dist/cli/code-agent-schedules.d.ts +54 -0
  177. package/dist/cli/code-agent-schedules.js +198 -0
  178. package/dist/client/AgentPanel.js +1 -1
  179. package/dist/client/ConnectBuilderCard.js +5 -2
  180. package/dist/client/EnvironmentBadge.js +6 -5
  181. package/dist/client/chat/run-recovery.js +3 -2
  182. package/dist/client/chat/tool-call-display.js +2 -3
  183. package/dist/client/components/CodeRequiredDialog.js +4 -2
  184. package/dist/client/i18n.js +5 -4
  185. package/dist/client/settings/SettingsPanel.js +1 -1
  186. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  187. package/dist/client/settings/useBuilderStatus.d.ts +6 -1
  188. package/dist/client/settings/useBuilderStatus.js +22 -33
  189. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  190. package/dist/code-agents/index.d.ts +2 -0
  191. package/dist/code-agents/index.js +2 -0
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/deploy/build.js +10 -4
  195. package/dist/localization/shared.d.ts +2 -0
  196. package/dist/localization/shared.js +5 -0
  197. package/dist/mcp-client/oauth-client.d.ts +1 -0
  198. package/dist/mcp-client/oauth-client.js +50 -1
  199. package/dist/notifications/routes.d.ts +3 -3
  200. package/dist/observability/routes.d.ts +1 -1
  201. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  202. package/dist/server/auth-marketing-locales.d.ts +7 -0
  203. package/dist/server/auth-marketing-locales.js +1322 -0
  204. package/dist/server/auth-marketing.d.ts +1 -0
  205. package/dist/server/auth-marketing.js +35 -6
  206. package/dist/server/auth.js +4 -17
  207. package/dist/server/builder-browser.d.ts +22 -0
  208. package/dist/server/builder-browser.js +77 -1
  209. package/dist/server/builder-oauth.d.ts +50 -0
  210. package/dist/server/builder-oauth.js +263 -0
  211. package/dist/server/core-routes-plugin.d.ts +11 -0
  212. package/dist/server/core-routes-plugin.js +291 -415
  213. package/dist/server/credential-provider.d.ts +1 -1
  214. package/dist/server/credential-provider.js +1 -1
  215. package/dist/server/csrf.js +2 -2
  216. package/dist/server/onboarding-html.js +52 -120
  217. package/dist/server/ssr-handler.js +13 -4
  218. package/dist/settings/store.d.ts +9 -0
  219. package/dist/settings/store.js +19 -0
  220. package/dist/shared/cache-control.d.ts +22 -0
  221. package/dist/shared/cache-control.js +25 -0
  222. package/dist/shared/index.d.ts +1 -0
  223. package/dist/shared/index.js +1 -0
  224. package/dist/templates/chat/netlify.toml +2 -0
  225. package/docs/content/deployment.mdx +2 -0
  226. package/docs/content/multi-app-workspace.mdx +1 -1
  227. package/package.json +2 -2
  228. 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
+ }
@@ -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
+ }