@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,201 @@
1
+ import {
2
+ applyVisualEdit,
3
+ buildCodeLayerProjection,
4
+ removeCodeLayerNodeFromHtml,
5
+ } from "@shared/code-layer";
6
+ import type { Dispatch, SetStateAction } from "react";
7
+ import { toast } from "sonner";
8
+
9
+ import type { ElementInfo } from "@/components/design/types";
10
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
11
+ import {
12
+ bridgeSourceIdForCodeLayerNode,
13
+ codeLayerNodeMatchesBridgeTarget,
14
+ codeLayerPatchMessage,
15
+ preferredCodeLayerSelector,
16
+ resolveCodeLayerNodeFromBridge,
17
+ resolveCodeLayerNodeFromElementInfo,
18
+ } from "@/pages/design-editor/code-layer-state";
19
+ import type { LiveScreenSnapshot } from "@/pages/design-editor/command-types";
20
+ import { updateElementContentInHtml } from "@/pages/design-editor/text-edit-utils";
21
+ import type {
22
+ DesignFile,
23
+ DesignTool,
24
+ EditorMode,
25
+ } from "@/pages/design-editor/types";
26
+
27
+ export interface TextContentChangeArgs {
28
+ activeCanvasSourceType: "inline" | "localhost" | "fusion";
29
+ activeFile: DesignFile;
30
+ applyLocalContentUpdate: (
31
+ nextContent: string,
32
+ options?: {
33
+ refreshPreview?: boolean;
34
+ skipPreview?: boolean;
35
+ forcePreviewFullDocument?: boolean;
36
+ immediateSave?: boolean;
37
+ persist?: boolean;
38
+ recordHistory?: boolean;
39
+ historyBeforeContent?: string;
40
+ updatedAt?: string;
41
+ clipboardMutation?: ClipboardContentMutationPublication;
42
+ },
43
+ ) => void;
44
+ canEditDesign: boolean;
45
+ finalizePendingTextCreation: (
46
+ fileId: string,
47
+ nodeIds: readonly (string | null | undefined)[],
48
+ finalContent: string,
49
+ ) => boolean;
50
+ getFreshActiveContent: () => string;
51
+ liveScreenSnapshotsById: Record<string, LiveScreenSnapshot>;
52
+ recordPendingLiveTextEdit: (
53
+ screenId: string,
54
+ selector: string,
55
+ value: string,
56
+ elementInfo?: ElementInfo,
57
+ details?: { html?: string; originalValue?: string; originalHtml?: string },
58
+ ) => void;
59
+ setActiveTool: Dispatch<SetStateAction<DesignTool>>;
60
+ setMode: Dispatch<SetStateAction<EditorMode>>;
61
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
62
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
63
+ t: (key: string, options?: Record<string, unknown>) => string;
64
+ updateLiveScreenSnapshotContent: (
65
+ screenId: string,
66
+ html: string,
67
+ options?: { recordHistory?: boolean },
68
+ ) => boolean;
69
+ }
70
+
71
+ export function runTextContentChange(
72
+ {
73
+ activeCanvasSourceType,
74
+ activeFile,
75
+ applyLocalContentUpdate,
76
+ canEditDesign,
77
+ finalizePendingTextCreation,
78
+ getFreshActiveContent,
79
+ liveScreenSnapshotsById,
80
+ recordPendingLiveTextEdit,
81
+ setActiveTool,
82
+ setMode,
83
+ setSelectedElement,
84
+ setSelectedLayerIdsState,
85
+ t,
86
+ updateLiveScreenSnapshotContent,
87
+ }: TextContentChangeArgs,
88
+ selector: string,
89
+ value: string,
90
+ elementInfo?: ElementInfo,
91
+ details?: {
92
+ html?: string;
93
+ originalValue?: string;
94
+ originalHtml?: string;
95
+ },
96
+ ) {
97
+ if (!canEditDesign) return;
98
+ if (!activeFile) return;
99
+ if (activeCanvasSourceType === "localhost") {
100
+ recordPendingLiveTextEdit(
101
+ activeFile.id,
102
+ selector,
103
+ value,
104
+ elementInfo,
105
+ details,
106
+ );
107
+ setActiveTool("move");
108
+ setMode("edit");
109
+ return;
110
+ }
111
+ const activeLiveSnapshot = liveScreenSnapshotsById[activeFile.id];
112
+ const baseContent = activeLiveSnapshot?.html ?? getFreshActiveContent();
113
+ const projection = buildCodeLayerProjection(baseContent);
114
+ const targetInfo = elementInfo ? { ...elementInfo, selector } : null;
115
+ const targetNode = targetInfo
116
+ ? resolveCodeLayerNodeFromElementInfo(projection, targetInfo)
117
+ : resolveCodeLayerNodeFromBridge(projection, selector);
118
+ const isEmpty = value.trim().length === 0;
119
+ const removedContent =
120
+ isEmpty && targetNode
121
+ ? removeCodeLayerNodeFromHtml(baseContent, targetNode)
122
+ : null;
123
+ const patch = !removedContent
124
+ ? applyVisualEdit(baseContent, {
125
+ kind: "textContent",
126
+ target: targetNode ? { nodeId: targetNode.id } : { selector },
127
+ value,
128
+ html: details?.html,
129
+ })
130
+ : null;
131
+ const nextContent =
132
+ removedContent ??
133
+ (patch?.result.status === "applied" ? patch.content : null) ??
134
+ updateElementContentInHtml(baseContent, selector, value, details?.html);
135
+ if (!nextContent) {
136
+ toast.error(
137
+ codeLayerPatchMessage(
138
+ patch?.result.message,
139
+ t("designEditor.patchProof.selectorMissing"),
140
+ ),
141
+ { duration: 4000 },
142
+ );
143
+ return;
144
+ }
145
+ const finalizedCreation = finalizePendingTextCreation(
146
+ activeFile.id,
147
+ [
148
+ elementInfo?.sourceId,
149
+ targetNode?.id,
150
+ targetNode ? bridgeSourceIdForCodeLayerNode(targetNode) : null,
151
+ ],
152
+ nextContent,
153
+ );
154
+ if (activeLiveSnapshot) {
155
+ updateLiveScreenSnapshotContent(activeFile.id, nextContent, {
156
+ recordHistory: !finalizedCreation,
157
+ });
158
+ } else {
159
+ applyLocalContentUpdate(nextContent, {
160
+ skipPreview: true,
161
+ recordHistory: !finalizedCreation,
162
+ });
163
+ }
164
+ // T8: committing text editing should return to the move tool (matches
165
+ // the creation path, which already does this), not re-arm the text
166
+ // tool — re-arming it meant every subsequent click anywhere on the
167
+ // canvas started ANOTHER new text box instead of selecting/moving.
168
+ setActiveTool("move");
169
+ setMode("edit");
170
+ if (removedContent) {
171
+ setSelectedElement(null);
172
+ setSelectedLayerIdsState([]);
173
+ return;
174
+ }
175
+ const nextProjection = buildCodeLayerProjection(nextContent);
176
+ const nextNode = targetNode
177
+ ? nextProjection.nodes.find((node) =>
178
+ codeLayerNodeMatchesBridgeTarget(
179
+ node,
180
+ selector,
181
+ bridgeSourceIdForCodeLayerNode(targetNode),
182
+ ),
183
+ )
184
+ : null;
185
+ if (nextNode) setSelectedLayerIdsState([nextNode.id]);
186
+ setSelectedElement((previous) => {
187
+ const base =
188
+ elementInfo ?? (previous?.selector === selector ? previous : undefined);
189
+ return base
190
+ ? {
191
+ ...base,
192
+ sourceId: nextNode
193
+ ? bridgeSourceIdForCodeLayerNode(nextNode)
194
+ : base.sourceId,
195
+ selector: nextNode ? preferredCodeLayerSelector(nextNode) : selector,
196
+ textContent: value.slice(0, 200),
197
+ htmlContent: details?.html,
198
+ }
199
+ : previous;
200
+ });
201
+ }
@@ -0,0 +1,123 @@
1
+ import type {
2
+ CanvasFrameGeometry,
3
+ CanvasFrameGeometryById,
4
+ } from "@shared/canvas-frames";
5
+ import type { CodeLayerNode } from "@shared/code-layer";
6
+ import { buildCodeLayerProjection } from "@shared/code-layer";
7
+ import type { RefObject } from "react";
8
+
9
+ import { getInitialFrameGeometry } from "@/components/design/multi-screen/frame-geometry";
10
+ import type { FrameGeometry } from "@/components/design/multi-screen/types";
11
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
12
+ import {
13
+ cloneCanvasFrameGeometry,
14
+ getCanvasFrameGeometry,
15
+ } from "@/pages/design-editor/design-data-geometry-utils";
16
+ import type { AlignableRect } from "@/pages/design-editor/layout-operations";
17
+ import { computeTidyPositions } from "@/pages/design-editor/layout-operations";
18
+ import type { DesignFile } from "@/pages/design-editor/types";
19
+
20
+ export interface TidyUpArgs {
21
+ activeFile: DesignFile;
22
+ boardFileId: string | undefined;
23
+ boardFrameGeometry: FrameGeometry | undefined;
24
+ canEditDesign: boolean;
25
+ commitNodePositions: (
26
+ baseContent: string,
27
+ positions: ReadonlyMap<string, { x: number; y: number }>,
28
+ ) => boolean;
29
+ designDataJsonRef: RefObject<Record<string, unknown>>;
30
+ getActiveFileSelectedNodeIds: (content: string) => string[];
31
+ getFreshActiveContent: () => string;
32
+ getScreenGroupFootprint: (
33
+ screenId: string,
34
+ geometry: CanvasFrameGeometry,
35
+ breakpointWidthsOverride?: readonly number[],
36
+ ) => { x: number; y: number; width: number; height: number };
37
+ handleGeometryCommit: (
38
+ before: CanvasFrameGeometryById,
39
+ after: CanvasFrameGeometryById,
40
+ options?: { source?: "pointer" | "keyboard" },
41
+ ) => void;
42
+ overviewScreens: OverviewScreen[];
43
+ overviewSelectedScreenIds: string[];
44
+ rectFromCodeLayerNode: (node: CodeLayerNode) => AlignableRect;
45
+ viewModeRef: RefObject<"single" | "overview">;
46
+ }
47
+
48
+ export function runTidyUp({
49
+ activeFile,
50
+ boardFileId,
51
+ boardFrameGeometry,
52
+ canEditDesign,
53
+ commitNodePositions,
54
+ designDataJsonRef,
55
+ getActiveFileSelectedNodeIds,
56
+ getFreshActiveContent,
57
+ getScreenGroupFootprint,
58
+ handleGeometryCommit,
59
+ overviewScreens,
60
+ overviewSelectedScreenIds,
61
+ rectFromCodeLayerNode,
62
+ viewModeRef,
63
+ }: TidyUpArgs) {
64
+ if (!canEditDesign) return;
65
+
66
+ if (viewModeRef.current === "overview") {
67
+ if (overviewSelectedScreenIds.length === 0) return;
68
+ const before = getCanvasFrameGeometry(designDataJsonRef.current);
69
+ const screenRects: AlignableRect[] = [];
70
+ overviewSelectedScreenIds.forEach((screenId) => {
71
+ const screenIndex = overviewScreens.findIndex(
72
+ (screen) => screen.id === screenId,
73
+ );
74
+ const screen =
75
+ screenIndex >= 0 ? overviewScreens[screenIndex] : undefined;
76
+ const fallbackGeometry =
77
+ screenIndex >= 0
78
+ ? getInitialFrameGeometry(screenIndex, {
79
+ width: screen?.width ?? 1280,
80
+ height: screen?.height ?? 2560,
81
+ })
82
+ : boardFileId === screenId
83
+ ? boardFrameGeometry
84
+ : undefined;
85
+ if (!fallbackGeometry) return;
86
+ const geometry = { ...fallbackGeometry, ...before[screenId] };
87
+ // Pack against the breakpoint group's footprint, not just the base
88
+ // frame — otherwise tidy leaves every breakpoint row overlapping the
89
+ // neighbouring screen.
90
+ const footprint = getScreenGroupFootprint(screenId, geometry);
91
+ screenRects.push({
92
+ id: screenId,
93
+ x: footprint.x,
94
+ y: footprint.y,
95
+ width: footprint.width,
96
+ height: footprint.height,
97
+ });
98
+ });
99
+ if (screenRects.length === 0) return;
100
+ const positions = computeTidyPositions(screenRects);
101
+ if (positions.size === 0) return;
102
+ const after = cloneCanvasFrameGeometry(before);
103
+ positions.forEach((position, screenId) => {
104
+ after[screenId] = { ...after[screenId]!, ...position };
105
+ });
106
+ handleGeometryCommit(before, after);
107
+ return;
108
+ }
109
+
110
+ if (!activeFile) return;
111
+ const baseContent = getFreshActiveContent();
112
+ const nodeIds = getActiveFileSelectedNodeIds(baseContent);
113
+ if (nodeIds.length === 0) return;
114
+ const projection = buildCodeLayerProjection(baseContent);
115
+ const nodesById = new Map(projection.nodes.map((node) => [node.id, node]));
116
+ const selectedNodes = nodeIds
117
+ .map((nodeId) => nodesById.get(nodeId))
118
+ .filter((node): node is CodeLayerNode => Boolean(node));
119
+ if (selectedNodes.length === 0) return;
120
+ const selectedRects = selectedNodes.map(rectFromCodeLayerNode);
121
+ const positions = computeTidyPositions(selectedRects);
122
+ commitNodePositions(baseContent, positions);
123
+ }
@@ -0,0 +1,167 @@
1
+ import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
2
+ import { buildCodeLayerProjection } from "@shared/code-layer";
3
+
4
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
5
+ import type { LiveScreenSnapshot } from "@/pages/design-editor/command-types";
6
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
7
+ import { setCodeLayerAttributeInHtml } from "@/pages/design-editor/html-layer-positioning";
8
+ import { hasScopedLayerState } from "@/pages/design-editor/layer-state-scope";
9
+ import { resolveOverviewScreenSourceType } from "@/pages/design-editor/pending-edits";
10
+ import type { DesignFile } from "@/pages/design-editor/types";
11
+
12
+ export interface ToggleLayerHiddenArgs {
13
+ activeFile: DesignFile;
14
+ applyFileContentUpdate: (
15
+ fileId: string,
16
+ nextContent: string,
17
+ options?: {
18
+ refreshPreview?: boolean;
19
+ skipPreview?: boolean;
20
+ forcePreviewFullDocument?: boolean;
21
+ persist?: boolean;
22
+ recordHistory?: boolean;
23
+ updatedAt?: string;
24
+ clipboardMutation?: ClipboardContentMutationPublication;
25
+ },
26
+ ) => void;
27
+ applyLayerStatePreview: (
28
+ screenId: string,
29
+ layerId: string,
30
+ state: "hidden" | "locked",
31
+ enabled: boolean,
32
+ ) => void;
33
+ canEditDesign: boolean;
34
+ codeLayerOwnerByNodeId: Map<
35
+ string,
36
+ {
37
+ fileId: string;
38
+ node: CodeLayerNode;
39
+ tree: CodeLayerTreeNode[];
40
+ runtimeOnly: boolean;
41
+ }
42
+ >;
43
+ designSourceType: "inline" | "localhost" | "fusion";
44
+ files: DesignFile[];
45
+ getFreshActiveContent: () => string;
46
+ hiddenLayerIds: Set<string>;
47
+ liveScreenSnapshotsById: Record<string, LiveScreenSnapshot>;
48
+ overviewScreens: OverviewScreen[];
49
+ recordPendingLiveLayerStateEdit: (
50
+ layerId: string,
51
+ state: "hidden" | "locked",
52
+ enabled: boolean,
53
+ originalEnabled: boolean,
54
+ ) => boolean;
55
+ sendRuntimeLayerStateSemanticHandoff: (
56
+ layerId: string,
57
+ state: "locked" | "hidden",
58
+ enabled: boolean,
59
+ ) => true | "preview-only" | false;
60
+ syncLiveScreenSnapshotPreview: (screenId: string, html: string) => void;
61
+ updateLiveScreenSnapshotContent: (
62
+ screenId: string,
63
+ html: string,
64
+ options?: { recordHistory?: boolean },
65
+ ) => boolean;
66
+ }
67
+
68
+ export function runToggleLayerHidden(
69
+ {
70
+ activeFile,
71
+ applyFileContentUpdate,
72
+ applyLayerStatePreview,
73
+ canEditDesign,
74
+ codeLayerOwnerByNodeId,
75
+ designSourceType,
76
+ files,
77
+ getFreshActiveContent,
78
+ hiddenLayerIds,
79
+ liveScreenSnapshotsById,
80
+ overviewScreens,
81
+ recordPendingLiveLayerStateEdit,
82
+ sendRuntimeLayerStateSemanticHandoff,
83
+ syncLiveScreenSnapshotPreview,
84
+ updateLiveScreenSnapshotContent,
85
+ }: ToggleLayerHiddenArgs,
86
+ layerId: string,
87
+ hidden: boolean,
88
+ ) {
89
+ if (!canEditDesign) return;
90
+ const owner = codeLayerOwnerByNodeId.get(layerId);
91
+ const layerScreenId =
92
+ owner?.fileId ??
93
+ (files.some((file) => file.id === layerId)
94
+ ? layerId
95
+ : (activeFile?.id ?? layerId));
96
+ if (hasScopedLayerState(hiddenLayerIds, layerScreenId, layerId) === hidden)
97
+ return;
98
+ const ownerScreen = owner
99
+ ? overviewScreens.find((screen) => screen.id === owner.fileId)
100
+ : undefined;
101
+ if (
102
+ owner &&
103
+ resolveOverviewScreenSourceType(ownerScreen, designSourceType) ===
104
+ "localhost" &&
105
+ recordPendingLiveLayerStateEdit(layerId, "hidden", hidden, !hidden)
106
+ ) {
107
+ applyLayerStatePreview(layerScreenId, layerId, "hidden", hidden);
108
+ return;
109
+ }
110
+ if (owner?.runtimeOnly) {
111
+ if (
112
+ sendRuntimeLayerStateSemanticHandoff(layerId, "hidden", hidden) === false
113
+ ) {
114
+ return;
115
+ }
116
+ applyLayerStatePreview(layerScreenId, layerId, "hidden", hidden);
117
+ return;
118
+ }
119
+ if (files.some((file) => file.id === layerId)) {
120
+ applyLayerStatePreview(layerScreenId, layerId, "hidden", hidden);
121
+ return;
122
+ }
123
+ const node = owner?.node;
124
+ if (!owner || !node) {
125
+ applyLayerStatePreview(layerScreenId, layerId, "hidden", hidden);
126
+ return;
127
+ }
128
+ // BUG-LOCK-HIDE-LIVE-SNAPSHOT: see the matching note in
129
+ // handleToggleLayerLocked, including why `node` has to be re-resolved
130
+ // against the live snapshot content before it's usable there.
131
+ const liveSnapshot = liveScreenSnapshotsById[owner.fileId];
132
+ const nodeIdAttr = node.dataAttributes["data-agent-native-node-id"];
133
+ const liveNode =
134
+ liveSnapshot && nodeIdAttr
135
+ ? buildCodeLayerProjection(liveSnapshot.html).nodes.find(
136
+ (candidate) =>
137
+ candidate.dataAttributes["data-agent-native-node-id"] ===
138
+ nodeIdAttr,
139
+ )
140
+ : undefined;
141
+ const sourceFile = files.find((file) => file.id === owner.fileId);
142
+ const sourceContent =
143
+ liveSnapshot?.html ??
144
+ (owner.fileId === activeFile?.id
145
+ ? getFreshActiveContent()
146
+ : (sourceFile?.content ?? ""));
147
+ const targetNode = liveSnapshot ? liveNode : node;
148
+ if (sourceContent && targetNode) {
149
+ const nextContent = setCodeLayerAttributeInHtml(
150
+ sourceContent,
151
+ targetNode,
152
+ "data-agent-native-hidden",
153
+ hidden ? "true" : null,
154
+ );
155
+ if (nextContent && nextContent !== sourceContent) {
156
+ if (liveSnapshot) {
157
+ updateLiveScreenSnapshotContent(owner.fileId, nextContent);
158
+ syncLiveScreenSnapshotPreview(owner.fileId, nextContent);
159
+ } else {
160
+ applyFileContentUpdate(owner.fileId, nextContent, {
161
+ refreshPreview: false,
162
+ });
163
+ }
164
+ }
165
+ }
166
+ applyLayerStatePreview(layerScreenId, layerId, "hidden", hidden);
167
+ }
@@ -0,0 +1,175 @@
1
+ import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
2
+ import { buildCodeLayerProjection } from "@shared/code-layer";
3
+
4
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
5
+ import type { LiveScreenSnapshot } from "@/pages/design-editor/command-types";
6
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
7
+ import { setCodeLayerAttributeInHtml } from "@/pages/design-editor/html-layer-positioning";
8
+ import { hasScopedLayerState } from "@/pages/design-editor/layer-state-scope";
9
+ import { resolveOverviewScreenSourceType } from "@/pages/design-editor/pending-edits";
10
+ import type { DesignFile } from "@/pages/design-editor/types";
11
+
12
+ export interface ToggleLayerLockedArgs {
13
+ activeFile: DesignFile;
14
+ applyFileContentUpdate: (
15
+ fileId: string,
16
+ nextContent: string,
17
+ options?: {
18
+ refreshPreview?: boolean;
19
+ skipPreview?: boolean;
20
+ forcePreviewFullDocument?: boolean;
21
+ persist?: boolean;
22
+ recordHistory?: boolean;
23
+ updatedAt?: string;
24
+ clipboardMutation?: ClipboardContentMutationPublication;
25
+ },
26
+ ) => void;
27
+ applyLayerStatePreview: (
28
+ screenId: string,
29
+ layerId: string,
30
+ state: "hidden" | "locked",
31
+ enabled: boolean,
32
+ ) => void;
33
+ canEditDesign: boolean;
34
+ codeLayerOwnerByNodeId: Map<
35
+ string,
36
+ {
37
+ fileId: string;
38
+ node: CodeLayerNode;
39
+ tree: CodeLayerTreeNode[];
40
+ runtimeOnly: boolean;
41
+ }
42
+ >;
43
+ designSourceType: "inline" | "localhost" | "fusion";
44
+ files: DesignFile[];
45
+ getFreshActiveContent: () => string;
46
+ liveScreenSnapshotsById: Record<string, LiveScreenSnapshot>;
47
+ lockedLayerIds: Set<string>;
48
+ overviewScreens: OverviewScreen[];
49
+ recordPendingLiveLayerStateEdit: (
50
+ layerId: string,
51
+ state: "hidden" | "locked",
52
+ enabled: boolean,
53
+ originalEnabled: boolean,
54
+ ) => boolean;
55
+ sendRuntimeLayerStateSemanticHandoff: (
56
+ layerId: string,
57
+ state: "locked" | "hidden",
58
+ enabled: boolean,
59
+ ) => true | "preview-only" | false;
60
+ syncLiveScreenSnapshotPreview: (screenId: string, html: string) => void;
61
+ updateLiveScreenSnapshotContent: (
62
+ screenId: string,
63
+ html: string,
64
+ options?: { recordHistory?: boolean },
65
+ ) => boolean;
66
+ }
67
+
68
+ export function runToggleLayerLocked(
69
+ {
70
+ activeFile,
71
+ applyFileContentUpdate,
72
+ applyLayerStatePreview,
73
+ canEditDesign,
74
+ codeLayerOwnerByNodeId,
75
+ designSourceType,
76
+ files,
77
+ getFreshActiveContent,
78
+ liveScreenSnapshotsById,
79
+ lockedLayerIds,
80
+ overviewScreens,
81
+ recordPendingLiveLayerStateEdit,
82
+ sendRuntimeLayerStateSemanticHandoff,
83
+ syncLiveScreenSnapshotPreview,
84
+ updateLiveScreenSnapshotContent,
85
+ }: ToggleLayerLockedArgs,
86
+ layerId: string,
87
+ locked: boolean,
88
+ ) {
89
+ if (!canEditDesign) return;
90
+ const owner = codeLayerOwnerByNodeId.get(layerId);
91
+ const layerScreenId =
92
+ owner?.fileId ??
93
+ (files.some((file) => file.id === layerId)
94
+ ? layerId
95
+ : (activeFile?.id ?? layerId));
96
+ if (hasScopedLayerState(lockedLayerIds, layerScreenId, layerId) === locked)
97
+ return;
98
+ const ownerScreen = owner
99
+ ? overviewScreens.find((screen) => screen.id === owner.fileId)
100
+ : undefined;
101
+ if (
102
+ owner &&
103
+ resolveOverviewScreenSourceType(ownerScreen, designSourceType) ===
104
+ "localhost" &&
105
+ recordPendingLiveLayerStateEdit(layerId, "locked", locked, !locked)
106
+ ) {
107
+ applyLayerStatePreview(layerScreenId, layerId, "locked", locked);
108
+ return;
109
+ }
110
+ if (owner?.runtimeOnly) {
111
+ if (
112
+ sendRuntimeLayerStateSemanticHandoff(layerId, "locked", locked) === false
113
+ ) {
114
+ return;
115
+ }
116
+ applyLayerStatePreview(layerScreenId, layerId, "locked", locked);
117
+ return;
118
+ }
119
+ if (files.some((file) => file.id === layerId)) {
120
+ applyLayerStatePreview(layerScreenId, layerId, "locked", locked);
121
+ return;
122
+ }
123
+ const node = owner?.node;
124
+ if (!owner || !node) {
125
+ applyLayerStatePreview(layerScreenId, layerId, "locked", locked);
126
+ return;
127
+ }
128
+ // BUG-LOCK-HIDE-LIVE-SNAPSHOT: same fix as handleDeleteSelection —
129
+ // getFreshActiveContent()/file.content is a bare URL for a
130
+ // localhost/live-snapshot screen, so setCodeLayerAttributeInHtml below
131
+ // could never find `node` in it and this write silently no-opped.
132
+ // `node` itself is unusable against the live snapshot HTML too:
133
+ // setCodeLayerAttributeInHtml indexes by node.source.openStart/openEnd,
134
+ // raw offsets into whatever string the RUNTIME projection parsed
135
+ // (runtimeLayerSnapshotsById), not the separately-tracked live
136
+ // snapshot — re-resolve a node from that exact content by the one id
137
+ // that's stable across both (see codeLayerOwnerByNodeId's matching
138
+ // note above).
139
+ const liveSnapshot = liveScreenSnapshotsById[owner.fileId];
140
+ const nodeIdAttr = node.dataAttributes["data-agent-native-node-id"];
141
+ const liveNode =
142
+ liveSnapshot && nodeIdAttr
143
+ ? buildCodeLayerProjection(liveSnapshot.html).nodes.find(
144
+ (candidate) =>
145
+ candidate.dataAttributes["data-agent-native-node-id"] ===
146
+ nodeIdAttr,
147
+ )
148
+ : undefined;
149
+ const sourceFile = files.find((file) => file.id === owner.fileId);
150
+ const sourceContent =
151
+ liveSnapshot?.html ??
152
+ (owner.fileId === activeFile?.id
153
+ ? getFreshActiveContent()
154
+ : (sourceFile?.content ?? ""));
155
+ const targetNode = liveSnapshot ? liveNode : node;
156
+ if (sourceContent && targetNode) {
157
+ const nextContent = setCodeLayerAttributeInHtml(
158
+ sourceContent,
159
+ targetNode,
160
+ "data-agent-native-locked",
161
+ locked ? "true" : null,
162
+ );
163
+ if (nextContent && nextContent !== sourceContent) {
164
+ if (liveSnapshot) {
165
+ updateLiveScreenSnapshotContent(owner.fileId, nextContent);
166
+ syncLiveScreenSnapshotPreview(owner.fileId, nextContent);
167
+ } else {
168
+ applyFileContentUpdate(owner.fileId, nextContent, {
169
+ refreshPreview: false,
170
+ });
171
+ }
172
+ }
173
+ }
174
+ applyLayerStatePreview(layerScreenId, layerId, "locked", locked);
175
+ }