@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,247 @@
1
+ import { useActionMutation } from "@agent-native/core/client/hooks";
2
+ import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
3
+ import { sourceContentHash } from "@shared/source-workspace";
4
+ import type { QueryClient } from "@tanstack/react-query";
5
+ import type { Dispatch, SetStateAction } from "react";
6
+ import { toast } from "sonner";
7
+
8
+ import type { ClipboardContentMutationPublication } from "@/lib/clipboard-content-lineage";
9
+ import { renameFilenamePreservingExtension } from "@/pages/design-editor/code-layer-state";
10
+ import type { OverviewScreen } from "@/pages/design-editor/derive/overview-screens";
11
+ import { TAB_ID } from "@/pages/design-editor/editor-session";
12
+ import { setCodeLayerAttributeInHtml } from "@/pages/design-editor/html-layer-positioning";
13
+ import { resolveOverviewScreenSourceType } from "@/pages/design-editor/pending-edits";
14
+ import { shouldIncludeScreenRenameContentOverride } from "@/pages/design-editor/selection-state";
15
+ import type { DesignFile } from "@/pages/design-editor/types";
16
+
17
+ export interface LayerRenameArgs {
18
+ activeFile: DesignFile;
19
+ applyFileContentUpdate: (
20
+ fileId: string,
21
+ nextContent: string,
22
+ options?: {
23
+ refreshPreview?: boolean;
24
+ skipPreview?: boolean;
25
+ forcePreviewFullDocument?: boolean;
26
+ persist?: boolean;
27
+ recordHistory?: boolean;
28
+ updatedAt?: string;
29
+ clipboardMutation?: ClipboardContentMutationPublication;
30
+ },
31
+ ) => void;
32
+ canEditDesign: boolean;
33
+ codeLayerOwnerByNodeId: Map<
34
+ string,
35
+ {
36
+ fileId: string;
37
+ node: CodeLayerNode;
38
+ tree: CodeLayerTreeNode[];
39
+ runtimeOnly: boolean;
40
+ }
41
+ >;
42
+ designSourceType: "inline" | "localhost" | "fusion";
43
+ files: DesignFile[];
44
+ getFreshActiveContent: () => string;
45
+ getScreenContent: (screenId: string) => string;
46
+ id: string | undefined;
47
+ overviewScreens: OverviewScreen[];
48
+ queryClient: QueryClient;
49
+ renameScreenMutation: ReturnType<
50
+ typeof useActionMutation<undefined, undefined, "rename-screen">
51
+ >;
52
+ serverFiles: DesignFile[];
53
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
54
+ t: (key: string, options?: Record<string, unknown>) => string;
55
+ }
56
+
57
+ export function runLayerRename(
58
+ {
59
+ activeFile,
60
+ applyFileContentUpdate,
61
+ canEditDesign,
62
+ codeLayerOwnerByNodeId,
63
+ designSourceType,
64
+ files,
65
+ getFreshActiveContent,
66
+ getScreenContent,
67
+ id,
68
+ overviewScreens,
69
+ queryClient,
70
+ renameScreenMutation,
71
+ serverFiles,
72
+ setSelectedLayerIdsState,
73
+ t,
74
+ }: LayerRenameArgs,
75
+ layerId: string,
76
+ name: string,
77
+ ) {
78
+ if (!canEditDesign) return;
79
+ const renamedFile = files.find((file) => file.id === layerId);
80
+ if (renamedFile) {
81
+ const nextFilename = renameFilenamePreservingExtension(
82
+ renamedFile.filename,
83
+ name,
84
+ );
85
+ if (nextFilename === renamedFile.filename) return;
86
+ const previousFilename = renamedFile.filename;
87
+ const contentOverrides = serverFiles.flatMap((file) => {
88
+ const screen = overviewScreens.find(
89
+ (candidate) => candidate.id === file.id,
90
+ );
91
+ if (!screen) return [];
92
+ const freshContent = getScreenContent(file.id);
93
+ if (
94
+ !shouldIncludeScreenRenameContentOverride({
95
+ fileType: file.fileType,
96
+ sourceType: resolveOverviewScreenSourceType(screen, designSourceType),
97
+ persistedContent: file.content,
98
+ freshContent,
99
+ })
100
+ ) {
101
+ return [];
102
+ }
103
+ return [
104
+ {
105
+ fileId: file.id,
106
+ content: freshContent,
107
+ expectedVersionHash: sourceContentHash(file.content),
108
+ },
109
+ ];
110
+ });
111
+
112
+ // Rename is one atomic server mutation: it validates uniqueness and
113
+ // updates the filename plus every exact data-screen reference (self
114
+ // links included) in one transaction. Apply the filename optimistically
115
+ // because it does not rebuild iframe content; the committed HTML
116
+ // snapshots returned below are then patched into live previews/Yjs in
117
+ // place, avoiding the old rename -> N independent save race and white
118
+ // flashes from srcdoc reloads.
119
+ queryClient.setQueryData(["action", "get-design", { id }], (old: any) => {
120
+ if (!old || typeof old !== "object" || !Array.isArray(old.files)) {
121
+ return old;
122
+ }
123
+ return {
124
+ ...old,
125
+ files: old.files.map((file: DesignFile) =>
126
+ file.id === layerId ? { ...file, filename: nextFilename } : file,
127
+ ),
128
+ };
129
+ });
130
+
131
+ renameScreenMutation.mutate(
132
+ {
133
+ id: layerId,
134
+ name,
135
+ requestSource: TAB_ID,
136
+ contentOverrides,
137
+ } as any,
138
+ {
139
+ onSuccess: (rawResult: unknown) => {
140
+ const result = rawResult as {
141
+ filename?: string;
142
+ files?: Array<{
143
+ id: string;
144
+ filename: string;
145
+ content: string;
146
+ updatedAt: string;
147
+ contentChanged: boolean;
148
+ }>;
149
+ };
150
+ const committedFiles = Array.isArray(result.files)
151
+ ? result.files
152
+ : [];
153
+ committedFiles.forEach((file) => {
154
+ if (!file.contentChanged) return;
155
+ applyFileContentUpdate(file.id, file.content, {
156
+ forcePreviewFullDocument: true,
157
+ persist: false,
158
+ recordHistory: false,
159
+ updatedAt: file.updatedAt,
160
+ });
161
+ });
162
+ queryClient.setQueryData(
163
+ ["action", "get-design", { id }],
164
+ (old: any) => {
165
+ if (
166
+ !old ||
167
+ typeof old !== "object" ||
168
+ !Array.isArray(old.files)
169
+ ) {
170
+ return old;
171
+ }
172
+ const committedById = new Map(
173
+ committedFiles.map((file) => [file.id, file]),
174
+ );
175
+ return {
176
+ ...old,
177
+ files: old.files.map((file: DesignFile) => {
178
+ const committed = committedById.get(file.id);
179
+ if (!committed) return file;
180
+ return {
181
+ ...file,
182
+ filename: committed.filename,
183
+ content: committed.content,
184
+ updatedAt: committed.updatedAt,
185
+ };
186
+ }),
187
+ };
188
+ },
189
+ );
190
+ },
191
+ onError: (error) => {
192
+ // Roll back only this optimistic filename. Do not restore a
193
+ // whole cached design snapshot because the user or a peer may
194
+ // have edited content while the mutation was in flight.
195
+ queryClient.setQueryData(
196
+ ["action", "get-design", { id }],
197
+ (old: any) => {
198
+ if (
199
+ !old ||
200
+ typeof old !== "object" ||
201
+ !Array.isArray(old.files)
202
+ ) {
203
+ return old;
204
+ }
205
+ return {
206
+ ...old,
207
+ files: old.files.map((file: DesignFile) =>
208
+ file.id === layerId && file.filename === nextFilename
209
+ ? { ...file, filename: previousFilename }
210
+ : file,
211
+ ),
212
+ };
213
+ },
214
+ );
215
+ void queryClient.invalidateQueries({
216
+ queryKey: ["action", "get-design", { id }],
217
+ });
218
+ toast.error(
219
+ error instanceof Error ? error.message : t("common.genericError"),
220
+ );
221
+ },
222
+ },
223
+ );
224
+ return;
225
+ }
226
+
227
+ const owner = codeLayerOwnerByNodeId.get(layerId);
228
+ const node = owner?.node;
229
+ if (!owner || !node) return;
230
+ const sourceFile = files.find((file) => file.id === owner.fileId);
231
+ const sourceContent =
232
+ owner.fileId === activeFile?.id
233
+ ? getFreshActiveContent()
234
+ : (sourceFile?.content ?? "");
235
+ if (!sourceContent) return;
236
+ const nextContent = setCodeLayerAttributeInHtml(
237
+ sourceContent,
238
+ node,
239
+ "data-agent-native-layer-name",
240
+ name,
241
+ );
242
+ if (!nextContent || nextContent === sourceContent) return;
243
+ applyFileContentUpdate(owner.fileId, nextContent, {
244
+ refreshPreview: false,
245
+ });
246
+ setSelectedLayerIdsState([layerId]);
247
+ }
@@ -0,0 +1,161 @@
1
+ import { isBoardFile } from "@shared/board-file";
2
+ import type { CodeLayerNode, CodeLayerTreeNode } from "@shared/code-layer";
3
+ import type { Dispatch, RefObject, SetStateAction } from "react";
4
+
5
+ import type { ElementInfo } from "@/components/design/types";
6
+ import type { EffectiveCodeLayerState } from "@/pages/design-editor/code-layer-state";
7
+ import { elementInfoFromCodeLayerNode } from "@/pages/design-editor/code-layer-state";
8
+ import {
9
+ getOverviewScreenIdsFromLayerSelection,
10
+ getSidebarCodeLayerSelectionState,
11
+ } from "@/pages/design-editor/selection-state";
12
+ import type {
13
+ DesignFile,
14
+ DesignTool,
15
+ EditorMode,
16
+ } from "@/pages/design-editor/types";
17
+
18
+ export interface LayerSelectionChangeArgs {
19
+ activeFile: DesignFile;
20
+ clearPendingOverviewLayerSelectionTimer: () => void;
21
+ codeLayerOwnerByNodeId: Map<
22
+ string,
23
+ {
24
+ fileId: string;
25
+ node: CodeLayerNode;
26
+ tree: CodeLayerTreeNode[];
27
+ runtimeOnly: boolean;
28
+ }
29
+ >;
30
+ effectiveCodeLayerState: EffectiveCodeLayerState;
31
+ files: DesignFile[];
32
+ focusDesignInspectorForSelection: () => void;
33
+ overviewSelectedScreenIds: string[];
34
+ pendingOverviewLayerSelectionRef: RefObject<string | null>;
35
+ pendingOverviewScreenSelectionRef: RefObject<string | null>;
36
+ setActiveFileId: Dispatch<SetStateAction<string | null>>;
37
+ setActiveTool: Dispatch<SetStateAction<DesignTool>>;
38
+ setCreatedOverviewLayerSelection: Dispatch<
39
+ SetStateAction<{ screenId: string; layerId: string } | null>
40
+ >;
41
+ setMode: Dispatch<SetStateAction<EditorMode>>;
42
+ setOverviewSelectedScreenIds: Dispatch<SetStateAction<string[]>>;
43
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
44
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
45
+ setViewMode: Dispatch<SetStateAction<"single" | "overview">>;
46
+ viewModeRef: RefObject<"single" | "overview">;
47
+ }
48
+
49
+ export function runLayerSelectionChange(
50
+ {
51
+ activeFile,
52
+ clearPendingOverviewLayerSelectionTimer,
53
+ codeLayerOwnerByNodeId,
54
+ effectiveCodeLayerState,
55
+ files,
56
+ focusDesignInspectorForSelection,
57
+ overviewSelectedScreenIds,
58
+ pendingOverviewLayerSelectionRef,
59
+ pendingOverviewScreenSelectionRef,
60
+ setActiveFileId,
61
+ setActiveTool,
62
+ setCreatedOverviewLayerSelection,
63
+ setMode,
64
+ setOverviewSelectedScreenIds,
65
+ setSelectedElement,
66
+ setSelectedLayerIdsState,
67
+ setViewMode,
68
+ viewModeRef,
69
+ }: LayerSelectionChangeArgs,
70
+ ids: string[],
71
+ _intent: {
72
+ additive: boolean;
73
+ currentSelectedIds?: string[];
74
+ id: string;
75
+ range: boolean;
76
+ },
77
+ ) {
78
+ const nextLayerIds = ids.filter((layerId) => !layerId.startsWith("__"));
79
+ pendingOverviewScreenSelectionRef.current = null;
80
+ pendingOverviewLayerSelectionRef.current = null;
81
+ clearPendingOverviewLayerSelectionTimer();
82
+ setCreatedOverviewLayerSelection(null);
83
+ setSelectedLayerIdsState(nextLayerIds);
84
+ const screenFileIds = files
85
+ .filter((file) => !isBoardFile(file.filename))
86
+ .map((file) => file.id);
87
+ if (viewModeRef.current === "overview") {
88
+ setOverviewSelectedScreenIds(
89
+ getOverviewScreenIdsFromLayerSelection({
90
+ fileIds: screenFileIds,
91
+ layerIds: nextLayerIds,
92
+ }),
93
+ );
94
+ }
95
+ const selectedId = nextLayerIds[nextLayerIds.length - 1];
96
+ if (!selectedId) {
97
+ setSelectedElement(null);
98
+ return;
99
+ }
100
+ const codeLayerOwner = codeLayerOwnerByNodeId.get(selectedId);
101
+ if (codeLayerOwner) {
102
+ const ownerIsScreenFile = screenFileIds.includes(codeLayerOwner.fileId);
103
+ if (viewModeRef.current === "overview") {
104
+ pendingOverviewScreenSelectionRef.current = ownerIsScreenFile
105
+ ? codeLayerOwner.fileId
106
+ : null;
107
+ pendingOverviewLayerSelectionRef.current = selectedId;
108
+ }
109
+ if (codeLayerOwner.fileId !== activeFile?.id) {
110
+ setActiveFileId(codeLayerOwner.fileId);
111
+ }
112
+ const nextSelectionState = getSidebarCodeLayerSelectionState({
113
+ currentViewMode: viewModeRef.current,
114
+ ownerFileId: codeLayerOwner.fileId,
115
+ overviewSelectedScreenIds,
116
+ screenFileIds,
117
+ });
118
+ viewModeRef.current = nextSelectionState.viewMode;
119
+ setViewMode(nextSelectionState.viewMode);
120
+ if (nextSelectionState.viewMode === "overview") {
121
+ setOverviewSelectedScreenIds(
122
+ nextSelectionState.overviewSelectedScreenIds,
123
+ );
124
+ }
125
+ const layerCanvasBlocked =
126
+ effectiveCodeLayerState.lockedIds.has(codeLayerOwner.fileId) ||
127
+ effectiveCodeLayerState.hiddenIds.has(codeLayerOwner.fileId) ||
128
+ effectiveCodeLayerState.lockedIds.has(selectedId) ||
129
+ effectiveCodeLayerState.hiddenIds.has(selectedId);
130
+ if (layerCanvasBlocked) {
131
+ setSelectedElement(null);
132
+ focusDesignInspectorForSelection();
133
+ setActiveTool("move");
134
+ setMode("edit");
135
+ return;
136
+ }
137
+ setSelectedElement(elementInfoFromCodeLayerNode(codeLayerOwner.node));
138
+ focusDesignInspectorForSelection();
139
+ setActiveTool("move");
140
+ setMode("edit");
141
+ return;
142
+ }
143
+ if (selectedId.startsWith("element:")) return;
144
+ const fileId = selectedId.startsWith("code:")
145
+ ? selectedId.slice("code:".length)
146
+ : selectedId;
147
+ if (files.some((file) => file.id === fileId && !isBoardFile(file.filename))) {
148
+ setOverviewSelectedScreenIds([fileId]);
149
+ setActiveFileId(fileId);
150
+ setSelectedElement(null);
151
+ setSelectedLayerIdsState(
152
+ nextLayerIds.some((layerId) => files.some((file) => file.id === layerId))
153
+ ? nextLayerIds
154
+ : [fileId],
155
+ );
156
+ setActiveTool("move");
157
+ setMode("edit");
158
+ viewModeRef.current = "overview";
159
+ setViewMode("overview");
160
+ }
161
+ }
@@ -0,0 +1,122 @@
1
+ import type { Dispatch, RefObject, SetStateAction } from "react";
2
+ import { toast } from "sonner";
3
+
4
+ import type { ElementInfo } from "@/components/design/types";
5
+ import type {
6
+ PendingLiveNonStyleEdit,
7
+ PendingVisualStyleEdit,
8
+ } from "@/pages/design-editor/pending-edits";
9
+ import { resolveModeChangeView } from "@/pages/design-editor/tool-state";
10
+ import type {
11
+ DesignFile,
12
+ DesignTool,
13
+ EditorMode,
14
+ } from "@/pages/design-editor/types";
15
+
16
+ export interface ModeChangeArgs {
17
+ activeFile: DesignFile;
18
+ canEditDesign: boolean;
19
+ clearPendingLiveEditState: () => void;
20
+ enterOverviewFromZoom: (nextMode?: EditorMode) => void;
21
+ enterSingleScreen: (fileId?: string | null) => void;
22
+ files: DesignFile[];
23
+ pendingLiveNonStyleEdits: PendingLiveNonStyleEdit[];
24
+ pendingVisualStyleEdits: PendingVisualStyleEdit[];
25
+ requestPendingLiveNonStyleRevert: (
26
+ edits: readonly PendingLiveNonStyleEdit[],
27
+ ) => void;
28
+ requestPendingVisualStyleRevert: (
29
+ edits: readonly PendingVisualStyleEdit[],
30
+ ) => void;
31
+ setActiveFileId: Dispatch<SetStateAction<string | null>>;
32
+ setActiveTool: Dispatch<SetStateAction<DesignTool>>;
33
+ setDrawMode: Dispatch<SetStateAction<boolean>>;
34
+ setMode: Dispatch<SetStateAction<EditorMode>>;
35
+ setPinMode: Dispatch<SetStateAction<boolean>>;
36
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
37
+ t: (key: string, options?: Record<string, unknown>) => string;
38
+ viewModeRef: RefObject<"single" | "overview">;
39
+ }
40
+
41
+ export function runModeChange(
42
+ {
43
+ activeFile,
44
+ canEditDesign,
45
+ clearPendingLiveEditState,
46
+ enterOverviewFromZoom,
47
+ enterSingleScreen,
48
+ files,
49
+ pendingLiveNonStyleEdits,
50
+ pendingVisualStyleEdits,
51
+ requestPendingLiveNonStyleRevert,
52
+ requestPendingVisualStyleRevert,
53
+ setActiveFileId,
54
+ setActiveTool,
55
+ setDrawMode,
56
+ setMode,
57
+ setPinMode,
58
+ setSelectedElement,
59
+ t,
60
+ viewModeRef,
61
+ }: ModeChangeArgs,
62
+ next: EditorMode,
63
+ options?: {
64
+ discardPendingLiveEdits?: boolean;
65
+ pendingLiveEditsAlreadyHandled?: boolean;
66
+ targetFileId?: string;
67
+ },
68
+ ) {
69
+ const nextActiveFile = options?.targetFileId
70
+ ? files.find((file) => file.id === options.targetFileId)
71
+ : activeFile;
72
+ if (!canEditDesign && next === "annotate") return;
73
+ if ((next === "annotate" || next === "interact") && !nextActiveFile) {
74
+ return;
75
+ }
76
+ if (
77
+ next === "interact" &&
78
+ (pendingVisualStyleEdits.length > 0 ||
79
+ pendingLiveNonStyleEdits.length > 0) &&
80
+ !options?.discardPendingLiveEdits &&
81
+ !options?.pendingLiveEditsAlreadyHandled
82
+ ) {
83
+ toast.error(t("designEditor.pendingVisualStyles.interactBlocked"));
84
+ return;
85
+ }
86
+ if (options?.discardPendingLiveEdits) {
87
+ requestPendingVisualStyleRevert(pendingVisualStyleEdits);
88
+ requestPendingLiveNonStyleRevert(pendingLiveNonStyleEdits);
89
+ clearPendingLiveEditState();
90
+ }
91
+
92
+ const routing = resolveModeChangeView({
93
+ next,
94
+ viewMode: viewModeRef.current,
95
+ });
96
+ if (routing === "enter-single-interact") {
97
+ enterSingleScreen(nextActiveFile?.id);
98
+ return;
99
+ }
100
+ if (routing === "enter-overview") {
101
+ if (options?.targetFileId) setActiveFileId(options.targetFileId);
102
+ enterOverviewFromZoom(next);
103
+ return;
104
+ }
105
+ if (options?.targetFileId) setActiveFileId(options.targetFileId);
106
+ setMode(next);
107
+ setSelectedElement(null);
108
+
109
+ if (next === "annotate") {
110
+ setActiveTool("draw");
111
+ setDrawMode(true);
112
+ setPinMode(false);
113
+ } else if (next === "interact") {
114
+ setActiveTool("move");
115
+ setDrawMode(false);
116
+ setPinMode(false);
117
+ } else {
118
+ setActiveTool("move");
119
+ setDrawMode(false);
120
+ setPinMode(false);
121
+ }
122
+ }