@agent-native/core 0.163.3 → 0.164.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
  3. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
  4. package/corpus/templates/analytics/netlify.toml +2 -0
  5. package/corpus/templates/assets/netlify.toml +2 -0
  6. package/corpus/templates/brain/netlify.toml +2 -0
  7. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
  8. package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
  9. package/corpus/templates/calendar/netlify.toml +2 -0
  10. package/corpus/templates/chat/netlify.toml +2 -0
  11. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
  12. package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
  13. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
  14. package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
  15. package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
  16. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
  17. package/corpus/templates/clips/app/routes/_index.tsx +10 -2
  18. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
  19. package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
  20. package/corpus/templates/clips/netlify.toml +2 -0
  21. package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
  22. package/corpus/templates/content/netlify.toml +2 -0
  23. package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
  24. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
  25. package/corpus/templates/design/AGENTS.md +3 -1
  26. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
  27. package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
  28. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
  29. package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
  30. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
  31. package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
  32. package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
  33. package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
  34. package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
  35. package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
  36. package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
  37. package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
  38. package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
  39. package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
  40. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
  41. package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
  42. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
  43. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
  44. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
  45. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
  46. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
  47. package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
  48. package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
  49. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
  50. package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
  51. package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
  52. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
  53. package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
  54. package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
  55. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
  56. package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
  57. package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
  58. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
  59. package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
  60. package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
  61. package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
  62. package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
  63. package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
  64. package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
  65. package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
  66. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
  67. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
  68. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
  69. package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
  70. package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
  71. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
  72. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
  73. package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
  74. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
  75. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
  76. package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
  77. package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
  78. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
  79. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
  80. package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
  81. package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
  82. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
  83. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
  84. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
  85. package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
  86. package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
  87. package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
  88. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
  89. package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
  90. package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
  91. package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
  92. package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
  93. package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
  94. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
  95. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
  96. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
  97. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
  98. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
  99. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
  100. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
  101. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
  102. package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
  103. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
  104. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
  105. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
  106. package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
  107. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
  108. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
  109. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
  110. package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
  111. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
  112. package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
  113. package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
  114. package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
  115. package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
  116. package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
  117. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
  118. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
  119. package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
  120. package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
  121. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
  122. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
  123. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
  124. package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
  126. package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
  127. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
  128. package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
  129. package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
  130. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
  131. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
  132. package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
  133. package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
  134. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
  135. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
  136. package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
  137. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
  138. package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
  139. package/corpus/templates/design/netlify.toml +2 -0
  140. package/corpus/templates/design/shared/board-file.ts +2 -1
  141. package/corpus/templates/dispatch/netlify.toml +2 -0
  142. package/corpus/templates/forms/netlify.toml +2 -0
  143. package/corpus/templates/mail/netlify.toml +2 -0
  144. package/corpus/templates/plan/netlify.toml +2 -0
  145. package/corpus/templates/slides/netlify.toml +2 -0
  146. package/dist/agent/engine/builder-engine.d.ts +6 -4
  147. package/dist/agent/engine/builder-engine.js +91 -26
  148. package/dist/agent/engine/registry.js +16 -0
  149. package/dist/cli/code-agent-agent-tools.d.ts +2 -0
  150. package/dist/cli/code-agent-agent-tools.js +188 -0
  151. package/dist/cli/code-agent-collaboration.d.ts +34 -0
  152. package/dist/cli/code-agent-collaboration.js +104 -0
  153. package/dist/cli/code-agent-executor.js +6 -0
  154. package/dist/cli/code-agent-schedules.d.ts +54 -0
  155. package/dist/cli/code-agent-schedules.js +198 -0
  156. package/dist/client/AgentPanel.js +1 -1
  157. package/dist/client/ConnectBuilderCard.js +5 -2
  158. package/dist/client/EnvironmentBadge.js +6 -5
  159. package/dist/client/chat/run-recovery.js +3 -2
  160. package/dist/client/chat/tool-call-display.js +2 -3
  161. package/dist/client/components/CodeRequiredDialog.js +4 -2
  162. package/dist/client/i18n.js +5 -4
  163. package/dist/client/settings/SettingsPanel.js +1 -1
  164. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  165. package/dist/client/settings/useBuilderStatus.d.ts +6 -1
  166. package/dist/client/settings/useBuilderStatus.js +22 -33
  167. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  168. package/dist/code-agents/index.d.ts +2 -0
  169. package/dist/code-agents/index.js +2 -0
  170. package/dist/collab/routes.d.ts +1 -1
  171. package/dist/localization/shared.d.ts +2 -0
  172. package/dist/localization/shared.js +5 -0
  173. package/dist/mcp-client/oauth-client.d.ts +1 -0
  174. package/dist/mcp-client/oauth-client.js +50 -1
  175. package/dist/observability/routes.d.ts +1 -1
  176. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  177. package/dist/resources/handlers.d.ts +1 -1
  178. package/dist/server/auth-marketing-locales.d.ts +7 -0
  179. package/dist/server/auth-marketing-locales.js +1322 -0
  180. package/dist/server/auth-marketing.d.ts +1 -0
  181. package/dist/server/auth-marketing.js +35 -6
  182. package/dist/server/auth.js +4 -17
  183. package/dist/server/builder-browser.d.ts +22 -0
  184. package/dist/server/builder-browser.js +77 -1
  185. package/dist/server/builder-oauth.d.ts +50 -0
  186. package/dist/server/builder-oauth.js +263 -0
  187. package/dist/server/core-routes-plugin.d.ts +11 -0
  188. package/dist/server/core-routes-plugin.js +291 -415
  189. package/dist/server/credential-provider.d.ts +1 -1
  190. package/dist/server/credential-provider.js +1 -1
  191. package/dist/server/csrf.js +2 -2
  192. package/dist/server/onboarding-html.js +52 -120
  193. package/dist/server/realtime-token.d.ts +1 -1
  194. package/dist/server/transcribe-voice.d.ts +1 -1
  195. package/dist/settings/store.d.ts +9 -0
  196. package/dist/settings/store.js +19 -0
  197. package/dist/templates/chat/netlify.toml +2 -0
  198. package/dist/vite/client.js +35 -37
  199. package/docs/content/deployment.mdx +2 -0
  200. package/docs/content/multi-app-workspace.mdx +1 -1
  201. package/package.json +2 -2
  202. package/src/templates/chat/netlify.toml +2 -0
@@ -0,0 +1,216 @@
1
+ import {
2
+ applyVisualEdit,
3
+ buildCodeLayerProjection,
4
+ removeCodeLayerNodeFromHtml,
5
+ } from "@shared/code-layer";
6
+ import { normalizeDesignSourceType } from "@shared/source-mode";
7
+ import type { Dispatch, SetStateAction } from "react";
8
+ import { toast } from "sonner";
9
+
10
+ import type { ElementInfo } from "@/components/design/types";
11
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
12
+ import {
13
+ bridgeSourceIdForCodeLayerNode,
14
+ codeLayerNodeMatchesBridgeTarget,
15
+ codeLayerPatchMessage,
16
+ preferredCodeLayerSelector,
17
+ resolveCodeLayerNodeFromBridge,
18
+ resolveCodeLayerNodeFromElementInfo,
19
+ } from "@/pages/design-editor/code-layer-state";
20
+ import type { LiveScreenSnapshot } from "@/pages/design-editor/command-types";
21
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
22
+ import { updateElementContentInHtml } from "@/pages/design-editor/text-edit-utils";
23
+ import type {
24
+ DesignFile,
25
+ DesignTool,
26
+ EditorMode,
27
+ } from "@/pages/design-editor/types";
28
+
29
+ export interface ScreenTextContentChangeArgs {
30
+ activeFile: DesignFile;
31
+ applyFileContentUpdate: (
32
+ fileId: string,
33
+ nextContent: string,
34
+ options?: {
35
+ refreshPreview?: boolean;
36
+ skipPreview?: boolean;
37
+ forcePreviewFullDocument?: boolean;
38
+ persist?: boolean;
39
+ recordHistory?: boolean;
40
+ updatedAt?: string;
41
+ clipboardMutation?: ClipboardContentMutationPublication;
42
+ },
43
+ ) => void;
44
+ canEditDesign: boolean;
45
+ designSourceType: "inline" | "localhost" | "fusion";
46
+ finalizePendingTextCreation: (
47
+ fileId: string,
48
+ nodeIds: readonly (string | null | undefined)[],
49
+ finalContent: string,
50
+ ) => boolean;
51
+ getScreenContent: (screenId: string) => string;
52
+ handleTextContentChange: (
53
+ selector: string,
54
+ value: string,
55
+ elementInfo?: ElementInfo,
56
+ details?: { html?: string; originalValue?: string; originalHtml?: string },
57
+ ) => void;
58
+ liveScreenSnapshotsById: Record<string, LiveScreenSnapshot>;
59
+ overviewScreens: OverviewScreen[];
60
+ recordPendingLiveTextEdit: (
61
+ screenId: string,
62
+ selector: string,
63
+ value: string,
64
+ elementInfo?: ElementInfo,
65
+ details?: { html?: string; originalValue?: string; originalHtml?: string },
66
+ ) => void;
67
+ setActiveFileId: Dispatch<SetStateAction<string | null>>;
68
+ setActiveTool: Dispatch<SetStateAction<DesignTool>>;
69
+ setMode: Dispatch<SetStateAction<EditorMode>>;
70
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
71
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
72
+ t: (key: string, options?: Record<string, unknown>) => string;
73
+ updateLiveScreenSnapshotContent: (
74
+ screenId: string,
75
+ html: string,
76
+ options?: { recordHistory?: boolean },
77
+ ) => boolean;
78
+ }
79
+
80
+ export function runScreenTextContentChange(
81
+ {
82
+ activeFile,
83
+ applyFileContentUpdate,
84
+ canEditDesign,
85
+ designSourceType,
86
+ finalizePendingTextCreation,
87
+ getScreenContent,
88
+ handleTextContentChange,
89
+ liveScreenSnapshotsById,
90
+ overviewScreens,
91
+ recordPendingLiveTextEdit,
92
+ setActiveFileId,
93
+ setActiveTool,
94
+ setMode,
95
+ setSelectedElement,
96
+ setSelectedLayerIdsState,
97
+ t,
98
+ updateLiveScreenSnapshotContent,
99
+ }: ScreenTextContentChangeArgs,
100
+ screenId: string,
101
+ selector: string,
102
+ value: string,
103
+ elementInfo?: ElementInfo,
104
+ details?: {
105
+ html?: string;
106
+ originalValue?: string;
107
+ originalHtml?: string;
108
+ },
109
+ ) {
110
+ if (screenId === activeFile?.id) {
111
+ handleTextContentChange(selector, value, elementInfo, details);
112
+ return;
113
+ }
114
+ if (!canEditDesign) return;
115
+ const overviewScreen = overviewScreens.find(
116
+ (screen) => screen.id === screenId,
117
+ );
118
+ const screenSourceType =
119
+ normalizeDesignSourceType(overviewScreen?.sourceType) ?? designSourceType;
120
+ if (screenSourceType === "localhost") {
121
+ recordPendingLiveTextEdit(screenId, selector, value, elementInfo, details);
122
+ setActiveFileId(screenId);
123
+ setActiveTool("move");
124
+ setMode("edit");
125
+ return;
126
+ }
127
+ const liveSnapshot = liveScreenSnapshotsById[screenId];
128
+ const baseContent = liveSnapshot?.html ?? getScreenContent(screenId);
129
+ const projection = buildCodeLayerProjection(baseContent);
130
+ const targetInfo = elementInfo ? { ...elementInfo, selector } : null;
131
+ const targetNode = targetInfo
132
+ ? resolveCodeLayerNodeFromElementInfo(projection, targetInfo)
133
+ : resolveCodeLayerNodeFromBridge(projection, selector);
134
+ const isEmpty = value.trim().length === 0;
135
+ const removedContent =
136
+ isEmpty && targetNode
137
+ ? removeCodeLayerNodeFromHtml(baseContent, targetNode)
138
+ : null;
139
+ const patch = !removedContent
140
+ ? applyVisualEdit(baseContent, {
141
+ kind: "textContent",
142
+ target: targetNode ? { nodeId: targetNode.id } : { selector },
143
+ value,
144
+ html: details?.html,
145
+ })
146
+ : null;
147
+ const nextContent =
148
+ removedContent ??
149
+ (patch?.result.status === "applied" ? patch.content : null) ??
150
+ updateElementContentInHtml(baseContent, selector, value, details?.html);
151
+ if (!nextContent) {
152
+ toast.error(
153
+ codeLayerPatchMessage(
154
+ patch?.result.message,
155
+ t("designEditor.patchProof.selectorMissing"),
156
+ ),
157
+ { duration: 4000 },
158
+ );
159
+ return;
160
+ }
161
+ const finalizedCreation = finalizePendingTextCreation(
162
+ screenId,
163
+ [
164
+ elementInfo?.sourceId,
165
+ targetNode?.id,
166
+ targetNode ? bridgeSourceIdForCodeLayerNode(targetNode) : null,
167
+ ],
168
+ nextContent,
169
+ );
170
+ if (liveSnapshot) {
171
+ updateLiveScreenSnapshotContent(screenId, nextContent, {
172
+ recordHistory: !finalizedCreation,
173
+ });
174
+ } else {
175
+ applyFileContentUpdate(screenId, nextContent, {
176
+ skipPreview: true,
177
+ recordHistory: !finalizedCreation,
178
+ });
179
+ }
180
+ setActiveFileId(screenId);
181
+ // T8: see the matching note in handleTextContentChange — commit
182
+ // should hand back to the move tool, not re-arm text.
183
+ setActiveTool("move");
184
+ setMode("edit");
185
+ if (removedContent) {
186
+ setSelectedElement(null);
187
+ setSelectedLayerIdsState([]);
188
+ return;
189
+ }
190
+ const nextProjection = buildCodeLayerProjection(nextContent);
191
+ const nextNode = targetNode
192
+ ? nextProjection.nodes.find((node) =>
193
+ codeLayerNodeMatchesBridgeTarget(
194
+ node,
195
+ selector,
196
+ bridgeSourceIdForCodeLayerNode(targetNode),
197
+ ),
198
+ )
199
+ : null;
200
+ if (nextNode) setSelectedLayerIdsState([nextNode.id]);
201
+ setSelectedElement((previous) => {
202
+ const base =
203
+ elementInfo ?? (previous?.selector === selector ? previous : undefined);
204
+ return base
205
+ ? {
206
+ ...base,
207
+ sourceId: nextNode
208
+ ? bridgeSourceIdForCodeLayerNode(nextNode)
209
+ : base.sourceId,
210
+ selector: nextNode ? preferredCodeLayerSelector(nextNode) : selector,
211
+ textContent: value.slice(0, 200),
212
+ htmlContent: details?.html,
213
+ }
214
+ : previous;
215
+ });
216
+ }
@@ -0,0 +1,108 @@
1
+ import { buildCodeLayerProjection } from "@shared/code-layer";
2
+ import { toast } from "sonner";
3
+
4
+ import type { ElementInfo } from "@/components/design/types";
5
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
6
+ import { insertClonedHtmlLayer } from "@/pages/design-editor/clone-and-pen-edit";
7
+ import {
8
+ codeLayerSelectorAliases,
9
+ resolveCodeLayerNodeFromBridge,
10
+ resolveCodeLayerNodeFromElementInfo,
11
+ } from "@/pages/design-editor/code-layer-state";
12
+ import type { DesignFile } from "@/pages/design-editor/types";
13
+
14
+ export interface ScreenVisualDuplicateChangeArgs {
15
+ activeFile: DesignFile;
16
+ applyFileContentUpdate: (
17
+ fileId: string,
18
+ nextContent: string,
19
+ options?: {
20
+ refreshPreview?: boolean;
21
+ skipPreview?: boolean;
22
+ forcePreviewFullDocument?: boolean;
23
+ persist?: boolean;
24
+ recordHistory?: boolean;
25
+ updatedAt?: string;
26
+ clipboardMutation?: ClipboardContentMutationPublication;
27
+ },
28
+ ) => void;
29
+ canEditDesign: boolean;
30
+ getScreenContent: (screenId: string) => string;
31
+ handleVisualDuplicateChange: (
32
+ selector: string,
33
+ cloneHtml: string,
34
+ elementInfo?: ElementInfo,
35
+ details?: {
36
+ sourceId?: string;
37
+ anchorSelector?: string;
38
+ anchorSourceId?: string;
39
+ placement?: "before" | "after" | "inside";
40
+ },
41
+ ) => boolean;
42
+ t: (key: string, options?: Record<string, unknown>) => string;
43
+ }
44
+
45
+ export function runScreenVisualDuplicateChange(
46
+ {
47
+ activeFile,
48
+ applyFileContentUpdate,
49
+ canEditDesign,
50
+ getScreenContent,
51
+ handleVisualDuplicateChange,
52
+ t,
53
+ }: ScreenVisualDuplicateChangeArgs,
54
+ screenId: string,
55
+ selector: string,
56
+ cloneHtml: string,
57
+ elementInfo?: ElementInfo,
58
+ details?: {
59
+ sourceId?: string;
60
+ anchorSelector?: string;
61
+ anchorSourceId?: string;
62
+ placement?: "before" | "after" | "inside";
63
+ },
64
+ ) {
65
+ if (screenId === activeFile?.id) {
66
+ return (
67
+ handleVisualDuplicateChange(selector, cloneHtml, elementInfo, details) !==
68
+ false
69
+ );
70
+ }
71
+ if (!canEditDesign) return false;
72
+ const baseContent = getScreenContent(screenId);
73
+ const projection = buildCodeLayerProjection(baseContent);
74
+ const targetInfo = elementInfo
75
+ ? {
76
+ ...elementInfo,
77
+ selector,
78
+ sourceId: details?.sourceId ?? elementInfo.sourceId,
79
+ }
80
+ : null;
81
+ const targetNode = targetInfo
82
+ ? resolveCodeLayerNodeFromElementInfo(projection, targetInfo)
83
+ : resolveCodeLayerNodeFromBridge(projection, selector, details?.sourceId);
84
+ const anchorNode = resolveCodeLayerNodeFromBridge(
85
+ projection,
86
+ details?.anchorSelector,
87
+ details?.anchorSourceId,
88
+ );
89
+ const nextContent = insertClonedHtmlLayer(baseContent, cloneHtml, {
90
+ targetSelectors: targetNode
91
+ ? codeLayerSelectorAliases(targetNode)
92
+ : [selector],
93
+ anchorSelectors: anchorNode
94
+ ? codeLayerSelectorAliases(anchorNode)
95
+ : details?.anchorSelector
96
+ ? [details.anchorSelector]
97
+ : undefined,
98
+ placement: details?.placement ?? "after",
99
+ });
100
+ if (!nextContent) {
101
+ toast.error(t("designEditor.toasts.layerMoveFailed"), {
102
+ duration: 4000,
103
+ });
104
+ return false;
105
+ }
106
+ applyFileContentUpdate(screenId, nextContent, { skipPreview: true });
107
+ return true;
108
+ }
@@ -0,0 +1,280 @@
1
+ import { stripBoardSurfaceOffsetFromCoord } from "@shared/board-file";
2
+ import { applyVisualEdit, buildCodeLayerProjection } from "@shared/code-layer";
3
+ import { normalizeDesignSourceType } from "@shared/source-mode";
4
+ import type { Dispatch, SetStateAction } from "react";
5
+ import { toast } from "sonner";
6
+
7
+ import { dndHostLog } from "@/components/design/dnd-debug";
8
+ import type { ElementInfo } from "@/components/design/types";
9
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
10
+ import {
11
+ bridgeSourceIdForCodeLayerNode,
12
+ codeLayerPatchMessage,
13
+ elementInfoFromCodeLayerNode,
14
+ resolveCodeLayerNodeFromBridge,
15
+ resolveCodeLayerNodeFromElementInfo,
16
+ } from "@/pages/design-editor/code-layer-state";
17
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
18
+ import {
19
+ isAbsoluteCodeLayerNode,
20
+ removeAbsolutePositioningFromNodeInHtml,
21
+ setAbsolutePositioningForNodeInHtml,
22
+ setFlowPositioningOverrideForNodeInHtml,
23
+ } from "@/pages/design-editor/html-layer-positioning";
24
+ import type { DesignFile } from "@/pages/design-editor/types";
25
+
26
+ export interface ScreenVisualStructureChangeArgs {
27
+ activeFile: DesignFile;
28
+ applyFileContentUpdate: (
29
+ fileId: string,
30
+ nextContent: string,
31
+ options?: {
32
+ refreshPreview?: boolean;
33
+ skipPreview?: boolean;
34
+ forcePreviewFullDocument?: boolean;
35
+ persist?: boolean;
36
+ recordHistory?: boolean;
37
+ updatedAt?: string;
38
+ clipboardMutation?: ClipboardContentMutationPublication;
39
+ },
40
+ ) => void;
41
+ canEditDesign: boolean;
42
+ designSourceType: "inline" | "localhost" | "fusion";
43
+ getScreenContent: (screenId: string) => string;
44
+ handleVisualStructureChange: (
45
+ selector: string,
46
+ anchorSelector: string,
47
+ placement: "before" | "after" | "inside",
48
+ elementInfo?: ElementInfo,
49
+ details?: {
50
+ sourceId?: string;
51
+ anchorSourceId?: string;
52
+ anchorElementInfo?: ElementInfo;
53
+ requestId?: string;
54
+ dropMode?: "flow-insert" | "absolute-container";
55
+ forceFlowPositionOverride?: boolean;
56
+ sourceRect?: { x: number; y: number; width: number; height: number };
57
+ anchorRect?: { x: number; y: number; width: number; height: number };
58
+ insertedHtml?: string;
59
+ replaced?: true;
60
+ replacementSelector?: string;
61
+ replacementSourceId?: string;
62
+ },
63
+ ) => boolean | "pending";
64
+ overviewScreens: OverviewScreen[];
65
+ recordPendingLiveStructureEdit: (
66
+ screenId: string,
67
+ selector: string,
68
+ anchorSelector: string,
69
+ placement: "before" | "after" | "inside",
70
+ elementInfo?: ElementInfo,
71
+ details?: {
72
+ sourceId?: string;
73
+ anchorSourceId?: string;
74
+ anchorElementInfo?: ElementInfo;
75
+ requestId?: string;
76
+ dropMode?: "flow-insert" | "absolute-container";
77
+ forceFlowPositionOverride?: boolean;
78
+ sourceRect?: { x: number; y: number; width: number; height: number };
79
+ anchorRect?: { x: number; y: number; width: number; height: number };
80
+ insertedHtml?: string;
81
+ replaced?: true;
82
+ replacementSelector?: string;
83
+ replacementSourceId?: string;
84
+ removed?: true;
85
+ },
86
+ ) => void;
87
+ setActiveFileId: Dispatch<SetStateAction<string | null>>;
88
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
89
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
90
+ t: (key: string, options?: Record<string, unknown>) => string;
91
+ }
92
+
93
+ export function runScreenVisualStructureChange(
94
+ {
95
+ activeFile,
96
+ applyFileContentUpdate,
97
+ canEditDesign,
98
+ designSourceType,
99
+ getScreenContent,
100
+ handleVisualStructureChange,
101
+ overviewScreens,
102
+ recordPendingLiveStructureEdit,
103
+ setActiveFileId,
104
+ setSelectedElement,
105
+ setSelectedLayerIdsState,
106
+ t,
107
+ }: ScreenVisualStructureChangeArgs,
108
+ screenId: string,
109
+ selector: string,
110
+ anchorSelector: string,
111
+ placement: "before" | "after" | "inside",
112
+ elementInfo?: ElementInfo,
113
+ details?: {
114
+ sourceId?: string;
115
+ anchorSourceId?: string;
116
+ anchorElementInfo?: ElementInfo;
117
+ requestId?: string;
118
+ dropMode?: "flow-insert" | "absolute-container";
119
+ forceFlowPositionOverride?: boolean;
120
+ sourceRect?: { x: number; y: number; width: number; height: number };
121
+ anchorRect?: { x: number; y: number; width: number; height: number };
122
+ /** Markup this change introduced; the subject does not exist in the
123
+ * screen's source yet, so it must be added rather than relocated. */
124
+ insertedHtml?: string;
125
+ replaced?: true;
126
+ replacementSelector?: string;
127
+ replacementSourceId?: string;
128
+ },
129
+ ) {
130
+ if (screenId === activeFile?.id) {
131
+ return handleVisualStructureChange(
132
+ selector,
133
+ anchorSelector,
134
+ placement,
135
+ elementInfo,
136
+ details,
137
+ );
138
+ }
139
+ if (!canEditDesign) return false;
140
+ const overviewScreen = overviewScreens.find(
141
+ (screen) => screen.id === screenId,
142
+ );
143
+ const screenSourceType =
144
+ normalizeDesignSourceType(overviewScreen?.sourceType) ?? designSourceType;
145
+ if (screenSourceType === "localhost") {
146
+ recordPendingLiveStructureEdit(
147
+ screenId,
148
+ selector,
149
+ anchorSelector,
150
+ placement,
151
+ elementInfo,
152
+ details,
153
+ );
154
+ return "pending";
155
+ }
156
+ const baseContent = getScreenContent(screenId);
157
+ const projection = buildCodeLayerProjection(baseContent);
158
+ const resolveBridgeNode = (targetSelector: string, sourceId?: string) =>
159
+ resolveCodeLayerNodeFromBridge(projection, targetSelector, sourceId);
160
+ const targetInfo = elementInfo
161
+ ? {
162
+ ...elementInfo,
163
+ selector,
164
+ sourceId: details?.sourceId ?? elementInfo.sourceId,
165
+ }
166
+ : null;
167
+ const targetNode = targetInfo
168
+ ? resolveCodeLayerNodeFromElementInfo(projection, targetInfo)
169
+ : resolveBridgeNode(selector, details?.sourceId);
170
+ const anchorNode = resolveBridgeNode(anchorSelector, details?.anchorSourceId);
171
+ const patch = applyVisualEdit(baseContent, {
172
+ kind: "moveNode",
173
+ target: targetNode ? { nodeId: targetNode.id } : { selector },
174
+ anchor: anchorNode
175
+ ? { nodeId: anchorNode.id }
176
+ : { selector: anchorSelector },
177
+ placement,
178
+ });
179
+ dndHostLog("persist:rewrite", {
180
+ status: patch.result.status,
181
+ message: patch.result.message,
182
+ });
183
+ if (patch.result.status !== "applied") {
184
+ toast.error(
185
+ codeLayerPatchMessage(
186
+ patch.result.message,
187
+ t("designEditor.toasts.layerMoveFailed"),
188
+ ),
189
+ { duration: 4000 },
190
+ );
191
+ return false;
192
+ }
193
+ const movedNodeAttrId =
194
+ targetNode?.dataAttributes["data-agent-native-node-id"] ??
195
+ details?.sourceId ??
196
+ elementInfo?.sourceId ??
197
+ (patch.result.after?.nodeId
198
+ ? patch.projection.nodes.find(
199
+ (node) => node.id === patch.result.after?.nodeId,
200
+ )?.dataAttributes["data-agent-native-node-id"]
201
+ : undefined);
202
+ // Same board-surface offset-poison guard as handleVisualStructureChange
203
+ // above: strip the 65536 fingerprint from the bridge's rect-space
204
+ // offset before persisting, and refresh the preview when it fired so
205
+ // the bridge's equally-off optimistic placement gets corrected.
206
+ const rawAbsoluteContainerOffset =
207
+ details?.dropMode === "absolute-container" &&
208
+ details.sourceRect &&
209
+ details.anchorRect
210
+ ? {
211
+ x: details.sourceRect.x - details.anchorRect.x,
212
+ y: details.sourceRect.y - details.anchorRect.y,
213
+ }
214
+ : null;
215
+ const absoluteContainerOffset = rawAbsoluteContainerOffset
216
+ ? {
217
+ x: stripBoardSurfaceOffsetFromCoord(rawAbsoluteContainerOffset.x),
218
+ y: stripBoardSurfaceOffsetFromCoord(rawAbsoluteContainerOffset.y),
219
+ }
220
+ : null;
221
+ const absoluteOffsetWasPoisoned = Boolean(
222
+ rawAbsoluteContainerOffset &&
223
+ absoluteContainerOffset &&
224
+ (rawAbsoluteContainerOffset.x !== absoluteContainerOffset.x ||
225
+ rawAbsoluteContainerOffset.y !== absoluteContainerOffset.y),
226
+ );
227
+ const nextContent =
228
+ movedNodeAttrId && details?.dropMode === "absolute-container"
229
+ ? absoluteContainerOffset
230
+ ? setAbsolutePositioningForNodeInHtml(
231
+ patch.content,
232
+ movedNodeAttrId,
233
+ absoluteContainerOffset,
234
+ )
235
+ : patch.content
236
+ : movedNodeAttrId &&
237
+ details?.dropMode === "flow-insert" &&
238
+ details.forceFlowPositionOverride
239
+ ? setFlowPositioningOverrideForNodeInHtml(
240
+ patch.content,
241
+ movedNodeAttrId,
242
+ )
243
+ : isAbsoluteCodeLayerNode(targetNode) && movedNodeAttrId
244
+ ? removeAbsolutePositioningFromNodeInHtml(
245
+ patch.content,
246
+ movedNodeAttrId,
247
+ )
248
+ : patch.content;
249
+ const nextProjection = buildCodeLayerProjection(nextContent);
250
+ applyFileContentUpdate(
251
+ screenId,
252
+ nextContent,
253
+ absoluteOffsetWasPoisoned
254
+ ? { forcePreviewFullDocument: true }
255
+ : { skipPreview: true },
256
+ );
257
+ const movedNode =
258
+ (movedNodeAttrId
259
+ ? nextProjection.nodes.find(
260
+ (node) =>
261
+ node.dataAttributes["data-agent-native-node-id"] ===
262
+ movedNodeAttrId,
263
+ )
264
+ : null) ??
265
+ resolveCodeLayerNodeFromBridge(
266
+ nextProjection,
267
+ selector,
268
+ details?.sourceId ??
269
+ elementInfo?.sourceId ??
270
+ (targetNode ? bridgeSourceIdForCodeLayerNode(targetNode) : undefined),
271
+ );
272
+ if (movedNode) {
273
+ setActiveFileId(screenId);
274
+ setSelectedLayerIdsState([movedNode.id]);
275
+ setSelectedElement(elementInfoFromCodeLayerNode(movedNode));
276
+ } else {
277
+ setSelectedElement(null);
278
+ }
279
+ return true;
280
+ }