@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,517 @@
1
+ import { computeReparentedChildPosition } from "@shared/board-file";
2
+ import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
3
+ import {
4
+ applyVisualEdit,
5
+ buildCodeLayerProjection,
6
+ buildCodeLayerTree,
7
+ moveNodeBetweenDocuments,
8
+ } from "@shared/code-layer";
9
+ import type { Dispatch, RefObject, SetStateAction } from "react";
10
+ import { toast } from "sonner";
11
+
12
+ import type { LayersPanelMoveIntent } from "@/components/design/LayersPanel";
13
+ import type {
14
+ ElementInfo,
15
+ RuntimeStructureMoveRequest,
16
+ } from "@/components/design/types";
17
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
18
+ import type { EffectiveCodeLayerState } from "@/pages/design-editor/code-layer-state";
19
+ import {
20
+ bridgeSourceIdForCodeLayerNode,
21
+ codeLayerPatchMessage,
22
+ collectCodeLayerAncestors,
23
+ elementInfoFromCodeLayerNode,
24
+ findMovedCodeLayerNodeInProjection,
25
+ removeEmptyGeneratedGroupWrappers,
26
+ } from "@/pages/design-editor/code-layer-state";
27
+ import {
28
+ getLayerMoveIterationOrder,
29
+ getLayerMoveSourceContent,
30
+ } from "@/pages/design-editor/editor-state";
31
+ import type {
32
+ ContentHistoryChange,
33
+ ContentHistoryEntry,
34
+ } from "@/pages/design-editor/history";
35
+ import {
36
+ getAbsolutePositioningForNodeInHtml,
37
+ setAbsolutePositioningForNodeInHtml,
38
+ } from "@/pages/design-editor/html-layer-positioning";
39
+ import { resolveRuntimeStructureMoveExecutionMode } from "@/pages/design-editor/react-semantic-handoff";
40
+ import type { DesignFile } from "@/pages/design-editor/types";
41
+
42
+ export interface LayerMoveArgs {
43
+ activeFile: DesignFile;
44
+ applyFileContentUpdate: (
45
+ fileId: string,
46
+ nextContent: string,
47
+ options?: {
48
+ refreshPreview?: boolean;
49
+ skipPreview?: boolean;
50
+ forcePreviewFullDocument?: boolean;
51
+ persist?: boolean;
52
+ recordHistory?: boolean;
53
+ updatedAt?: string;
54
+ clipboardMutation?: ClipboardContentMutationPublication;
55
+ },
56
+ ) => void;
57
+ canEditDesign: boolean;
58
+ canMoveLayer: (intent: LayersPanelMoveIntent) => boolean;
59
+ codeLayerOwnerByNodeId: Map<
60
+ string,
61
+ {
62
+ fileId: string;
63
+ node: CodeLayerNode;
64
+ tree: CodeLayerTreeNode[];
65
+ runtimeOnly: boolean;
66
+ }
67
+ >;
68
+ effectiveCodeLayerState: EffectiveCodeLayerState;
69
+ files: DesignFile[];
70
+ getFreshActiveContent: () => string;
71
+ handleLayerMoveToScreen: (
72
+ intent: LayersPanelMoveIntent,
73
+ targetFileId: string,
74
+ ) => void;
75
+ handleScreenLayerMove: (intent: LayersPanelMoveIntent) => void;
76
+ recordContentHistoryEntry: (entry: ContentHistoryEntry) => void;
77
+ recordLocalContentHistoryEntry: (change: ContentHistoryChange) => void;
78
+ runtimeStructureMoveRevisionRef: RefObject<number>;
79
+ sendRuntimeLayerMoveSemanticHandoff: (
80
+ subjectLayerId: string,
81
+ targetLayerId: string,
82
+ placement: "before" | "after" | "inside",
83
+ ) => boolean;
84
+ setExpandedLayerIds: Dispatch<SetStateAction<string[]>>;
85
+ setRuntimeStructureMoveRequest: Dispatch<
86
+ SetStateAction<(RuntimeStructureMoveRequest & { screenId: string }) | null>
87
+ >;
88
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
89
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
90
+ t: (key: string, options?: Record<string, unknown>) => string;
91
+ viewModeRef: RefObject<"single" | "overview">;
92
+ visualScreenFileIds: Set<string>;
93
+ }
94
+
95
+ export function runLayerMove(
96
+ {
97
+ activeFile,
98
+ applyFileContentUpdate,
99
+ canEditDesign,
100
+ canMoveLayer,
101
+ codeLayerOwnerByNodeId,
102
+ effectiveCodeLayerState,
103
+ files,
104
+ getFreshActiveContent,
105
+ handleLayerMoveToScreen,
106
+ handleScreenLayerMove,
107
+ recordContentHistoryEntry,
108
+ recordLocalContentHistoryEntry,
109
+ runtimeStructureMoveRevisionRef,
110
+ sendRuntimeLayerMoveSemanticHandoff,
111
+ setExpandedLayerIds,
112
+ setRuntimeStructureMoveRequest,
113
+ setSelectedElement,
114
+ setSelectedLayerIdsState,
115
+ t,
116
+ viewModeRef,
117
+ visualScreenFileIds,
118
+ }: LayerMoveArgs,
119
+ intent: LayersPanelMoveIntent,
120
+ ) {
121
+ if (!canEditDesign) return;
122
+ if (!canMoveLayer(intent)) return;
123
+ if (
124
+ intent.draggedIds.length > 0 &&
125
+ intent.draggedIds.every((draggedId) => visualScreenFileIds.has(draggedId))
126
+ ) {
127
+ handleScreenLayerMove(intent);
128
+ return;
129
+ }
130
+ const targetOwner = codeLayerOwnerByNodeId.get(intent.targetId);
131
+ if (!targetOwner) {
132
+ const targetFile = files.find((file) => file.id === intent.targetId);
133
+ if (targetFile) {
134
+ handleLayerMoveToScreen(intent, targetFile.id);
135
+ }
136
+ return;
137
+ }
138
+ const runtimeDraggedOwner =
139
+ intent.draggedIds.length === 1
140
+ ? codeLayerOwnerByNodeId.get(intent.draggedIds[0]!)
141
+ : undefined;
142
+ if (targetOwner.runtimeOnly || runtimeDraggedOwner?.runtimeOnly) {
143
+ if (!runtimeDraggedOwner) {
144
+ return;
145
+ }
146
+ const executionMode = resolveRuntimeStructureMoveExecutionMode({
147
+ subjectRuntimeOnly: runtimeDraggedOwner.runtimeOnly,
148
+ targetRuntimeOnly: targetOwner.runtimeOnly,
149
+ sourceScreenId: runtimeDraggedOwner.fileId,
150
+ targetScreenId: targetOwner.fileId,
151
+ });
152
+ if (executionMode === "screen-bridge") {
153
+ // Keep the existing fast, optimistic in-iframe path when one
154
+ // screen-scoped bridge owns both runtime endpoints. Cross-screen or
155
+ // mixed runtime/source ownership cannot be represented by that
156
+ // one-screen StructureMove message and must go through the semantic
157
+ // coding-agent handoff below.
158
+ runtimeStructureMoveRevisionRef.current += 1;
159
+ setRuntimeStructureMoveRequest({
160
+ requestId: runtimeStructureMoveRevisionRef.current,
161
+ screenId: targetOwner.fileId,
162
+ subject: {
163
+ selector: runtimeDraggedOwner.node.selector,
164
+ sourceId: bridgeSourceIdForCodeLayerNode(runtimeDraggedOwner.node),
165
+ },
166
+ anchor: {
167
+ selector: targetOwner.node.selector,
168
+ sourceId: bridgeSourceIdForCodeLayerNode(targetOwner.node),
169
+ },
170
+ placement: intent.placement,
171
+ });
172
+ return;
173
+ }
174
+ sendRuntimeLayerMoveSemanticHandoff(
175
+ intent.draggedIds[0]!,
176
+ intent.targetId,
177
+ intent.placement,
178
+ );
179
+ return;
180
+ }
181
+ // L8: locked/hidden is no longer a blocker for using this row as a drop
182
+ // anchor (see canMoveLayer) — only dragging a LOCKED row is blocked,
183
+ // checked per-draggedId below. Hidden rows are draggable.
184
+ const freshActiveContent = getFreshActiveContent();
185
+ const destFile = files.find((file) => file.id === targetOwner.fileId);
186
+ const destContent =
187
+ targetOwner.fileId === activeFile?.id
188
+ ? freshActiveContent
189
+ : (destFile?.content ?? "");
190
+ if (!destContent) return;
191
+
192
+ // L17: a single ordered insert pipeline for a MIXED same-file/cross-file
193
+ // multi-drag. Previously same-file drags were all applied as one batch
194
+ // (each inserted at the shared anchor), THEN cross-file drags were
195
+ // applied as a second batch — so a selection like [same-file A,
196
+ // cross-file B, same-file C] (in panel-visual order) would always end
197
+ // up as A,C,B relative to the anchor instead of preserving A,B,C,
198
+ // because the two source kinds never interleaved against one another.
199
+ // Fix: classify each dragged id but keep ONE combined list in the
200
+ // original intent.draggedIds order (already translated from panel order
201
+ // into DOM order at the LayersPanel callback boundary), then iterate
202
+ // that single list once, dispatching each item through the same-file or
203
+ // cross-file primitive against the shared running nextDestContent /
204
+ // sourceContentMap state so mixed sequences interleave in the intended
205
+ // order.
206
+ const movedNodeSnapshots = new Map<string, CodeLayerNode>();
207
+ type ClassifiedDrag =
208
+ | { draggedId: string; kind: "same-file" }
209
+ | { draggedId: string; kind: "cross-file"; sourceFileId: string };
210
+ const classifiedDrags: ClassifiedDrag[] = [];
211
+ for (const draggedId of intent.draggedIds) {
212
+ const draggedOwner = codeLayerOwnerByNodeId.get(draggedId);
213
+ if (
214
+ draggedId === intent.targetId ||
215
+ !draggedOwner ||
216
+ // L8: only LOCKED dragged rows are blocked; hidden rows may be
217
+ // dragged/reordered like any other layer.
218
+ effectiveCodeLayerState.lockedIds.has(draggedId)
219
+ ) {
220
+ continue;
221
+ }
222
+ // L15: mirror canMoveLayer's per-drag ancestor-of-target guard here.
223
+ // canMoveLayer only gates the whole intent (true if ANY dragged id is
224
+ // valid), so a mixed multi-drag where one id is an ancestor of the
225
+ // drop target would otherwise reach applyVisualEdit/moveNode for that
226
+ // id, which always reports "conflict" (the anchor is inside the
227
+ // dragged element) — a spurious per-id failure toast for a case we
228
+ // can just silently skip, exactly like the other guards above.
229
+ if (
230
+ draggedOwner.fileId === targetOwner.fileId &&
231
+ collectCodeLayerAncestors(targetOwner.tree, intent.targetId).includes(
232
+ draggedId,
233
+ )
234
+ ) {
235
+ continue;
236
+ }
237
+ movedNodeSnapshots.set(draggedId, draggedOwner.node);
238
+ if (draggedOwner.fileId === targetOwner.fileId) {
239
+ classifiedDrags.push({ draggedId, kind: "same-file" });
240
+ } else {
241
+ classifiedDrags.push({
242
+ draggedId,
243
+ kind: "cross-file",
244
+ sourceFileId: draggedOwner.fileId,
245
+ });
246
+ }
247
+ }
248
+
249
+ const movedIdOrder = classifiedDrags.map((drag) => drag.draggedId);
250
+
251
+ // L25: track each dragged node's former parent (by fileId + stable
252
+ // data-agent-native-node-id), so that once every move in this intent
253
+ // is applied we can sweep each touched file for now-empty generated
254
+ // "Group" wrappers left behind by the move.
255
+ const formerParentAttrIdsByFileId = new Map<string, Set<string>>();
256
+ for (const drag of classifiedDrags) {
257
+ const draggedOwner = codeLayerOwnerByNodeId.get(drag.draggedId);
258
+ const parentId = draggedOwner?.node.parentId;
259
+ if (!parentId) continue;
260
+ const parentAttrId =
261
+ codeLayerOwnerByNodeId.get(parentId)?.node.dataAttributes[
262
+ "data-agent-native-node-id"
263
+ ];
264
+ if (!parentAttrId) continue;
265
+ const fileId = draggedOwner.fileId;
266
+ const set = formerParentAttrIdsByFileId.get(fileId) ?? new Set<string>();
267
+ set.add(parentAttrId);
268
+ formerParentAttrIdsByFileId.set(fileId, set);
269
+ }
270
+
271
+ let nextDestContent = destContent;
272
+ let moved = false;
273
+ const sourceContentMap = new Map<string, string>();
274
+ const sourceOriginalContentMap = new Map<string, string>();
275
+ const movedNodeIdByDraggedId = new Map<string, string>();
276
+
277
+ for (const drag of getLayerMoveIterationOrder(
278
+ classifiedDrags,
279
+ intent.placement,
280
+ )) {
281
+ const { draggedId } = drag;
282
+ if (drag.kind === "same-file") {
283
+ const draggedOwner = codeLayerOwnerByNodeId.get(draggedId);
284
+ // L2: a panel drop can reparent the node onto a different container
285
+ // (not just reorder among the same siblings) — e.g. dropping
286
+ // "inside" a different frame, or "before"/"after" an anchor that
287
+ // lives under a different parent than the dragged node's current
288
+ // one. The overview canvas reparent path
289
+ // (handleOverviewPrimitiveReparent) already rebases absolute
290
+ // left/top in that case so the element keeps its visual position
291
+ // instead of teleporting to (0,0)-relative-to-new-parent; mirror
292
+ // that here for the panel/tree move path.
293
+ const targetOwnerNode = codeLayerOwnerByNodeId.get(intent.targetId);
294
+ const newParentAttrId =
295
+ intent.placement === "inside"
296
+ ? intent.targetId
297
+ : (targetOwnerNode?.node.parentId ?? null);
298
+ const isCrossParent = Boolean(
299
+ draggedOwner &&
300
+ newParentAttrId &&
301
+ draggedOwner.node.parentId !== newParentAttrId,
302
+ );
303
+ const prevContentForRebase = nextDestContent;
304
+ const patch = applyVisualEdit(nextDestContent, {
305
+ kind: "moveNode",
306
+ target: { nodeId: draggedId },
307
+ anchor: { nodeId: intent.targetId },
308
+ placement: intent.placement,
309
+ });
310
+ if (patch.result.status !== "applied") {
311
+ toast.error(
312
+ codeLayerPatchMessage(
313
+ patch.result.message,
314
+ t("designEditor.toasts.layerMoveFailed"),
315
+ ),
316
+ { duration: 4000 },
317
+ );
318
+ continue;
319
+ }
320
+ nextDestContent = patch.content;
321
+ if (isCrossParent && newParentAttrId) {
322
+ const movedNodeAttrId =
323
+ patch.projection.nodes.find(
324
+ (n) =>
325
+ n.dataAttributes["data-agent-native-node-id"] === draggedId ||
326
+ n.id === draggedId,
327
+ )?.dataAttributes["data-agent-native-node-id"] ?? draggedId;
328
+ const sourcePosition = getAbsolutePositioningForNodeInHtml(
329
+ prevContentForRebase,
330
+ draggedId,
331
+ );
332
+ const targetPosition = getAbsolutePositioningForNodeInHtml(
333
+ prevContentForRebase,
334
+ newParentAttrId,
335
+ );
336
+ if (sourcePosition && targetPosition) {
337
+ // Same parent-relative rebase as the canvas reparent path —
338
+ // computeReparentedChildPosition also strips the historic
339
+ // board-surface offset poison (65536-multiples) from either
340
+ // side so a panel move of a poisoned nested board child heals
341
+ // its coordinates instead of preserving them.
342
+ nextDestContent = setAbsolutePositioningForNodeInHtml(
343
+ nextDestContent,
344
+ movedNodeAttrId,
345
+ computeReparentedChildPosition(sourcePosition, targetPosition),
346
+ );
347
+ }
348
+ }
349
+ moved = true;
350
+ } else {
351
+ const { sourceFileId } = drag;
352
+ const srcFile = files.find((f) => f.id === sourceFileId);
353
+ if (!srcFile) continue;
354
+ const currentSourceContent = getLayerMoveSourceContent({
355
+ sourceFileId,
356
+ activeFileId: activeFile?.id,
357
+ activeContent: freshActiveContent,
358
+ sourceFileContent: srcFile.content,
359
+ sourceContentMap,
360
+ });
361
+ if (!sourceOriginalContentMap.has(sourceFileId)) {
362
+ sourceOriginalContentMap.set(sourceFileId, currentSourceContent);
363
+ }
364
+
365
+ // The dragged node's data-agent-native-node-id is the node id
366
+ // tracked by code-layer. Look up the actual attribute value from
367
+ // the owner.
368
+ const draggedOwner = codeLayerOwnerByNodeId.get(draggedId);
369
+ const nodeAttrId =
370
+ draggedOwner?.node.dataAttributes["data-agent-native-node-id"] ??
371
+ draggedId;
372
+ const anchorAttrId =
373
+ codeLayerOwnerByNodeId.get(intent.targetId)?.node.dataAttributes[
374
+ "data-agent-native-node-id"
375
+ ] ?? intent.targetId;
376
+
377
+ const result = moveNodeBetweenDocuments(
378
+ currentSourceContent,
379
+ nextDestContent,
380
+ {
381
+ nodeId: nodeAttrId,
382
+ anchorNodeId: anchorAttrId,
383
+ placement: intent.placement,
384
+ },
385
+ );
386
+ if (result.status !== "applied") {
387
+ toast.error(
388
+ codeLayerPatchMessage(
389
+ result.message,
390
+ t("designEditor.toasts.layerMoveFailed"),
391
+ ),
392
+ { duration: 4000 },
393
+ );
394
+ continue;
395
+ }
396
+ sourceContentMap.set(sourceFileId, result.sourceHtml);
397
+ nextDestContent = result.destHtml;
398
+ movedNodeIdByDraggedId.set(draggedId, result.movedNodeId ?? nodeAttrId);
399
+ moved = true;
400
+ }
401
+ }
402
+
403
+ if (!moved) return;
404
+
405
+ // L25: sweep every touched file for now-empty generated "Group"
406
+ // wrappers left behind once their last child moved away, and remove
407
+ // them. Applied per-file against whichever content variable currently
408
+ // holds that file's post-move state.
409
+ for (const [fileId, parentAttrIds] of formerParentAttrIdsByFileId) {
410
+ if (fileId === targetOwner.fileId) {
411
+ nextDestContent = removeEmptyGeneratedGroupWrappers(
412
+ nextDestContent,
413
+ parentAttrIds,
414
+ );
415
+ } else if (sourceContentMap.has(fileId)) {
416
+ sourceContentMap.set(
417
+ fileId,
418
+ removeEmptyGeneratedGroupWrappers(
419
+ sourceContentMap.get(fileId)!,
420
+ parentAttrIds,
421
+ ),
422
+ );
423
+ }
424
+ }
425
+
426
+ const finalDestProjection =
427
+ nextDestContent !== destContent
428
+ ? buildCodeLayerProjection(nextDestContent)
429
+ : null;
430
+ const finalDestTree = finalDestProjection
431
+ ? buildCodeLayerTree(finalDestProjection)
432
+ : [];
433
+ const movedNodesAfterMove = movedIdOrder
434
+ .map((draggedId) => {
435
+ const node = movedNodeSnapshots.get(draggedId);
436
+ return node && finalDestProjection
437
+ ? findMovedCodeLayerNodeInProjection(
438
+ finalDestProjection,
439
+ node,
440
+ movedNodeIdByDraggedId.get(draggedId),
441
+ )
442
+ : null;
443
+ })
444
+ .filter((node): node is CodeLayerNode => Boolean(node));
445
+
446
+ if (movedNodesAfterMove.length > 0) {
447
+ setSelectedLayerIdsState(movedNodesAfterMove.map((node) => node.id));
448
+ const lastMovedNode = movedNodesAfterMove[movedNodesAfterMove.length - 1];
449
+ if (lastMovedNode && targetOwner.fileId === activeFile?.id) {
450
+ setSelectedElement(elementInfoFromCodeLayerNode(lastMovedNode));
451
+ }
452
+ const movedAncestorIds = movedNodesAfterMove.flatMap((node) =>
453
+ collectCodeLayerAncestors(finalDestTree, node.id),
454
+ );
455
+ setExpandedLayerIds((current) => {
456
+ const next = new Set(current);
457
+ next.add(targetOwner.fileId);
458
+ movedAncestorIds.forEach((ancestorId) => next.add(ancestorId));
459
+ return next.size === current.length ? current : Array.from(next);
460
+ });
461
+ }
462
+
463
+ const hasCrossFileMoves = sourceContentMap.size > 0;
464
+ if (hasCrossFileMoves) {
465
+ const crossFileChanges = [
466
+ ...Array.from(sourceContentMap.entries()).map(
467
+ ([sourceFileId, newSourceContent]) => ({
468
+ fileId: sourceFileId,
469
+ before:
470
+ sourceOriginalContentMap.get(sourceFileId) ??
471
+ files.find((file) => file.id === sourceFileId)?.content ??
472
+ "",
473
+ after: newSourceContent,
474
+ }),
475
+ ),
476
+ ...(nextDestContent !== destContent
477
+ ? [
478
+ {
479
+ fileId: targetOwner.fileId,
480
+ before: destContent,
481
+ after: nextDestContent,
482
+ },
483
+ ]
484
+ : []),
485
+ ];
486
+ // recordContentHistoryEntry writes to the overview-only global stack
487
+ // and (when the change touches the active file) clears the live
488
+ // single-mode Yjs undo stack as a side effect. Outside overview mode
489
+ // that stack is never consulted by handleUndo, so a cross-file move
490
+ // made while a single screen is focused would both go unrecorded and
491
+ // wipe that screen's undo history (see U5). Record into the local
492
+ // per-file stack instead so single-mode Cmd+Z can reach it.
493
+ if (viewModeRef.current === "overview") {
494
+ recordContentHistoryEntry({ changes: crossFileChanges });
495
+ } else {
496
+ crossFileChanges.forEach((change) =>
497
+ recordLocalContentHistoryEntry(change),
498
+ );
499
+ }
500
+ }
501
+
502
+ // Persist source files that changed.
503
+ for (const [sourceFileId, newSourceContent] of sourceContentMap) {
504
+ applyFileContentUpdate(sourceFileId, newSourceContent, {
505
+ recordHistory: !hasCrossFileMoves,
506
+ refreshPreview: false,
507
+ });
508
+ }
509
+
510
+ // Persist dest file (which may also be the active file).
511
+ if (nextDestContent !== destContent) {
512
+ applyFileContentUpdate(targetOwner.fileId, nextDestContent, {
513
+ recordHistory: !hasCrossFileMoves,
514
+ refreshPreview: false,
515
+ });
516
+ }
517
+ }