@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,364 @@
1
+ import { useActionMutation } from "@agent-native/core/client/hooks";
2
+ import type { CanvasFrameGeometryById } from "@shared/canvas-frames";
3
+ import type { QueryClient } from "@tanstack/react-query";
4
+ import type { Dispatch, RefObject, SetStateAction } from "react";
5
+ import { toast } from "sonner";
6
+
7
+ import type { ElementInfo } from "@/components/design/types";
8
+ import type { ClipboardContentLineage } from "@/lib/clipboard-content-lineage";
9
+ import { cloneCanvasFrameGeometry } from "@/pages/design-editor/design-data-geometry-utils";
10
+ import type { UndoRedoOrderKind } from "@/pages/design-editor/editor-state";
11
+ import type {
12
+ ContentHistoryChange,
13
+ ContentHistoryEntry,
14
+ FileCreationHistoryEntry,
15
+ FileDeletionHistoryEntry,
16
+ GeometryHistoryEntry,
17
+ GeometryHistorySelection,
18
+ } from "@/pages/design-editor/history";
19
+ import {
20
+ MAX_DESIGN_UNDO_STACK,
21
+ filterFileDeletionHistoryEntry,
22
+ getContentHistoryChanges,
23
+ pruneFileCreationHistoryStack,
24
+ pruneGeometryHistoryEntryForDeletedFiles,
25
+ removeRecentUndoRedoOrderKinds,
26
+ } from "@/pages/design-editor/history";
27
+ import type { DesignFile } from "@/pages/design-editor/types";
28
+
29
+ export interface DeleteFilesArgs {
30
+ activeFile: DesignFile;
31
+ canvasFrameGeometryById: CanvasFrameGeometryById;
32
+ clearRedoStacks: () => void;
33
+ clipboardPasteRedoStackRef: RefObject<ContentHistoryChange[]>;
34
+ clipboardPasteUndoStackRef: RefObject<ContentHistoryChange[]>;
35
+ contentRedoSelectionStackRef: RefObject<
36
+ (GeometryHistorySelection | undefined)[]
37
+ >;
38
+ contentRedoStackRef: RefObject<ContentHistoryEntry[]>;
39
+ contentUndoSelectionStackRef: RefObject<
40
+ (GeometryHistorySelection | undefined)[]
41
+ >;
42
+ contentUndoStackRef: RefObject<ContentHistoryEntry[]>;
43
+ deleteFileMutation: ReturnType<
44
+ typeof useActionMutation<undefined, undefined, "delete-file">
45
+ >;
46
+ fileCreationRedoStackRef: RefObject<FileCreationHistoryEntry[]>;
47
+ fileCreationUndoStackRef: RefObject<FileCreationHistoryEntry[]>;
48
+ fileDeletionUndoStackRef: RefObject<FileDeletionHistoryEntry[]>;
49
+ fileHistoryMutationPendingRef: RefObject<boolean>;
50
+ files: DesignFile[];
51
+ geometryRedoStackRef: RefObject<GeometryHistoryEntry[]>;
52
+ geometryUndoStackRef: RefObject<GeometryHistoryEntry[]>;
53
+ historyOrderRef: RefObject<UndoRedoOrderKind[]>;
54
+ id: string | undefined;
55
+ latestClipboardMutationContentRef: RefObject<
56
+ Map<string, ClipboardContentLineage>
57
+ >;
58
+ localContentRedoStackRef: RefObject<ContentHistoryChange[]>;
59
+ localContentUndoStackRef: RefObject<ContentHistoryChange[]>;
60
+ queryClient: QueryClient;
61
+ redoOrderRef: RefObject<UndoRedoOrderKind[]>;
62
+ setActiveFileId: Dispatch<SetStateAction<string | null>>;
63
+ setSelectedElement: Dispatch<SetStateAction<ElementInfo | null>>;
64
+ setSelectedLayerIdsState: Dispatch<SetStateAction<string[]>>;
65
+ syncUndoRedoState: () => void;
66
+ t: (key: string, options?: Record<string, unknown>) => string;
67
+ writeFrameGeometrySnapshot: (
68
+ geometryById: CanvasFrameGeometryById,
69
+ options?: { syncViewportFrameIds?: string[]; pinHeightFrameIds?: string[] },
70
+ ) => void;
71
+ }
72
+
73
+ export function runDeleteFiles(
74
+ {
75
+ activeFile,
76
+ canvasFrameGeometryById,
77
+ clearRedoStacks,
78
+ clipboardPasteRedoStackRef,
79
+ clipboardPasteUndoStackRef,
80
+ contentRedoSelectionStackRef,
81
+ contentRedoStackRef,
82
+ contentUndoSelectionStackRef,
83
+ contentUndoStackRef,
84
+ deleteFileMutation,
85
+ fileCreationRedoStackRef,
86
+ fileCreationUndoStackRef,
87
+ fileDeletionUndoStackRef,
88
+ fileHistoryMutationPendingRef,
89
+ files,
90
+ geometryRedoStackRef,
91
+ geometryUndoStackRef,
92
+ historyOrderRef,
93
+ id,
94
+ latestClipboardMutationContentRef,
95
+ localContentRedoStackRef,
96
+ localContentUndoStackRef,
97
+ queryClient,
98
+ redoOrderRef,
99
+ setActiveFileId,
100
+ setSelectedElement,
101
+ setSelectedLayerIdsState,
102
+ syncUndoRedoState,
103
+ t,
104
+ writeFrameGeometrySnapshot,
105
+ }: DeleteFilesArgs,
106
+ filesToDelete: DesignFile[],
107
+ options?: {
108
+ // U12 fix: undoFileCreation pushes the just-undone create onto the
109
+ // file-creation REDO stack, then calls this function to soft-delete
110
+ // the same file it just pushed for. Without this flag the filename-
111
+ // keyed prune below (which exists to drop a redo entry when its file
112
+ // is hard-deleted directly, NOT via undo) would immediately remove
113
+ // the entry undoFileCreation just pushed, leaving redo permanently
114
+ // empty after every screen-create/duplicate undo.
115
+ skipFileCreationRedoPrune?: boolean;
116
+ // A user-confirmed screen deletion is a normal editor operation, not
117
+ // an irreversible special case. Capture the complete rows + frame
118
+ // geometry and add one grouped undo entry after every delete succeeds.
119
+ recordDeletionHistory?: boolean;
120
+ onMutationSettled?: (
121
+ deletedFiles: DesignFile[],
122
+ failedFiles: DesignFile[],
123
+ ) => void;
124
+ },
125
+ ) {
126
+ if (!filesToDelete.length) return;
127
+ const deleteIds = new Set(filesToDelete.map((file) => file.id));
128
+ const nextActiveFile = files.find((file) => !deleteIds.has(file.id));
129
+ const nextGeometry = cloneCanvasFrameGeometry(canvasFrameGeometryById);
130
+ const deletionHistoryEntry: FileDeletionHistoryEntry | null =
131
+ options?.recordDeletionHistory
132
+ ? {
133
+ files: filesToDelete.map((file) => ({
134
+ ...file,
135
+ geometry: canvasFrameGeometryById[file.id],
136
+ })),
137
+ }
138
+ : null;
139
+ if (deletionHistoryEntry) {
140
+ clearRedoStacks();
141
+ fileHistoryMutationPendingRef.current = true;
142
+ syncUndoRedoState();
143
+ }
144
+ filesToDelete.forEach((file) => {
145
+ delete nextGeometry[file.id];
146
+ });
147
+
148
+ const nextGeometryUndoStack: GeometryHistoryEntry[] = [];
149
+ let removedGeometryUndoEntries = 0;
150
+ geometryUndoStackRef.current.forEach((entry) => {
151
+ const pruned = pruneGeometryHistoryEntryForDeletedFiles(entry, deleteIds);
152
+ if (!pruned) {
153
+ removedGeometryUndoEntries += 1;
154
+ return;
155
+ }
156
+ nextGeometryUndoStack.push(pruned);
157
+ });
158
+ geometryUndoStackRef.current = nextGeometryUndoStack;
159
+ historyOrderRef.current = removeRecentUndoRedoOrderKinds(
160
+ historyOrderRef.current,
161
+ "geometry",
162
+ removedGeometryUndoEntries,
163
+ );
164
+
165
+ const nextGeometryRedoStack: GeometryHistoryEntry[] = [];
166
+ let removedGeometryRedoEntries = 0;
167
+ geometryRedoStackRef.current.forEach((entry) => {
168
+ const pruned = pruneGeometryHistoryEntryForDeletedFiles(entry, deleteIds);
169
+ if (!pruned) {
170
+ removedGeometryRedoEntries += 1;
171
+ return;
172
+ }
173
+ nextGeometryRedoStack.push(pruned);
174
+ });
175
+ geometryRedoStackRef.current = nextGeometryRedoStack;
176
+ redoOrderRef.current = removeRecentUndoRedoOrderKinds(
177
+ redoOrderRef.current,
178
+ "geometry",
179
+ removedGeometryRedoEntries,
180
+ );
181
+
182
+ // Selection-restore stacks are index-aligned with their matching
183
+ // content stack (see contentUndoSelectionStackRef's doc comment) —
184
+ // iterate with the index so a dropped entry (remainingChanges.length
185
+ // === 0) drops its selection snapshot too, keeping both arrays in sync.
186
+ const nextContentUndoStack: ContentHistoryEntry[] = [];
187
+ const nextContentUndoSelectionStack: (
188
+ | GeometryHistorySelection
189
+ | undefined
190
+ )[] = [];
191
+ let removedContentUndoEntries = 0;
192
+ contentUndoStackRef.current.forEach((entry, index) => {
193
+ const remainingChanges = getContentHistoryChanges(entry).filter(
194
+ (change) => !deleteIds.has(change.fileId),
195
+ );
196
+ if (remainingChanges.length === 0) {
197
+ removedContentUndoEntries += 1;
198
+ return;
199
+ }
200
+ nextContentUndoStack.push(
201
+ remainingChanges.length === 1
202
+ ? remainingChanges[0]
203
+ : { changes: remainingChanges },
204
+ );
205
+ nextContentUndoSelectionStack.push(
206
+ contentUndoSelectionStackRef.current[index],
207
+ );
208
+ });
209
+ contentUndoStackRef.current = nextContentUndoStack;
210
+ contentUndoSelectionStackRef.current = nextContentUndoSelectionStack;
211
+ historyOrderRef.current = removeRecentUndoRedoOrderKinds(
212
+ historyOrderRef.current,
213
+ "file-content",
214
+ removedContentUndoEntries,
215
+ );
216
+ const nextContentRedoStack: ContentHistoryEntry[] = [];
217
+ const nextContentRedoSelectionStack: (
218
+ | GeometryHistorySelection
219
+ | undefined
220
+ )[] = [];
221
+ let removedContentRedoEntries = 0;
222
+ contentRedoStackRef.current.forEach((entry, index) => {
223
+ const remainingChanges = getContentHistoryChanges(entry).filter(
224
+ (change) => !deleteIds.has(change.fileId),
225
+ );
226
+ if (remainingChanges.length === 0) {
227
+ removedContentRedoEntries += 1;
228
+ return;
229
+ }
230
+ nextContentRedoStack.push(
231
+ remainingChanges.length === 1
232
+ ? remainingChanges[0]
233
+ : { changes: remainingChanges },
234
+ );
235
+ nextContentRedoSelectionStack.push(
236
+ contentRedoSelectionStackRef.current[index],
237
+ );
238
+ });
239
+ contentRedoStackRef.current = nextContentRedoStack;
240
+ contentRedoSelectionStackRef.current = nextContentRedoSelectionStack;
241
+ redoOrderRef.current = removeRecentUndoRedoOrderKinds(
242
+ redoOrderRef.current,
243
+ "file-content",
244
+ removedContentRedoEntries,
245
+ );
246
+ localContentUndoStackRef.current = localContentUndoStackRef.current.filter(
247
+ (change) => !deleteIds.has(change.fileId),
248
+ );
249
+ localContentRedoStackRef.current = localContentRedoStackRef.current.filter(
250
+ (change) => !deleteIds.has(change.fileId),
251
+ );
252
+
253
+ // U12: a file-created entry is resolved by filename at undo/redo time
254
+ // (it doesn't carry an id, since the id isn't known until the create
255
+ // mutation resolves), so prune it here by filename when the file it
256
+ // refers to is being hard-deleted directly.
257
+ const deletedFilenames = new Set(filesToDelete.map((file) => file.filename));
258
+ const prunedFileCreationUndo = pruneFileCreationHistoryStack(
259
+ fileCreationUndoStackRef.current,
260
+ deletedFilenames,
261
+ );
262
+ fileCreationUndoStackRef.current = prunedFileCreationUndo.stack;
263
+ historyOrderRef.current = removeRecentUndoRedoOrderKinds(
264
+ historyOrderRef.current,
265
+ "file-created",
266
+ prunedFileCreationUndo.removed,
267
+ );
268
+ // U12 fix: undoFileCreation calls performDeleteFiles to soft-delete the
269
+ // file it is undoing AFTER pushing that same entry onto the redo stack
270
+ // (so redo can recreate it). Pruning the redo stack by filename here
271
+ // would immediately drop the entry undoFileCreation just pushed —
272
+ // redo would never survive an undo. skipFileCreationRedoPrune lets
273
+ // that caller opt out; every other caller (direct hard-delete from the
274
+ // overview/panel) still gets the filename-keyed prune so a redo entry
275
+ // pointing at a since-hard-deleted file cannot resurrect it.
276
+ const prunedFileCreationRedo = pruneFileCreationHistoryStack(
277
+ fileCreationRedoStackRef.current,
278
+ deletedFilenames,
279
+ { skip: options?.skipFileCreationRedoPrune },
280
+ );
281
+ fileCreationRedoStackRef.current = prunedFileCreationRedo.stack;
282
+ redoOrderRef.current = removeRecentUndoRedoOrderKinds(
283
+ redoOrderRef.current,
284
+ "file-created",
285
+ prunedFileCreationRedo.removed,
286
+ );
287
+
288
+ writeFrameGeometrySnapshot(nextGeometry);
289
+ queryClient.setQueryData(["action", "get-design", { id }], (old: any) => {
290
+ if (!old || typeof old !== "object" || !Array.isArray(old.files)) {
291
+ return old;
292
+ }
293
+ return {
294
+ ...old,
295
+ files: old.files.filter((file: DesignFile) => !deleteIds.has(file.id)),
296
+ };
297
+ });
298
+
299
+ if (activeFile && deleteIds.has(activeFile.id) && nextActiveFile) {
300
+ setActiveFileId(nextActiveFile.id);
301
+ }
302
+ setSelectedElement(null);
303
+ setSelectedLayerIdsState([]);
304
+
305
+ void Promise.allSettled(
306
+ filesToDelete.map((file) =>
307
+ deleteFileMutation.mutateAsync({ id: file.id } as any),
308
+ ),
309
+ ).then((results) => {
310
+ const deletedFiles = filesToDelete.filter((_, index) => {
311
+ const result = results[index];
312
+ return (
313
+ result?.status === "fulfilled" &&
314
+ (result.value as { deleted?: boolean } | undefined)?.deleted !== false
315
+ );
316
+ });
317
+ const deletedIds = new Set(deletedFiles.map((file) => file.id));
318
+ const failedFiles = filesToDelete.filter(
319
+ (file) => !deletedIds.has(file.id),
320
+ );
321
+
322
+ if (deletionHistoryEntry && deletedFiles.length > 0) {
323
+ fileDeletionUndoStackRef.current = [
324
+ ...fileDeletionUndoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
325
+ filterFileDeletionHistoryEntry(deletionHistoryEntry, deletedIds),
326
+ ];
327
+ historyOrderRef.current = [
328
+ ...historyOrderRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
329
+ "file-deleted",
330
+ ];
331
+ }
332
+
333
+ const rejected = results.find(
334
+ (result): result is PromiseRejectedResult => result.status === "rejected",
335
+ );
336
+ if (failedFiles.length > 0) {
337
+ queryClient.invalidateQueries({
338
+ queryKey: ["action", "get-design"],
339
+ });
340
+ if (rejected) {
341
+ toast.error(
342
+ rejected.reason instanceof Error
343
+ ? rejected.reason.message
344
+ : t("common.genericError"),
345
+ );
346
+ }
347
+ }
348
+
349
+ if (deletionHistoryEntry) {
350
+ fileHistoryMutationPendingRef.current = false;
351
+ clipboardPasteUndoStackRef.current = [];
352
+ clipboardPasteRedoStackRef.current = [];
353
+ latestClipboardMutationContentRef.current.clear();
354
+ }
355
+ options?.onMutationSettled?.(deletedFiles, failedFiles);
356
+ syncUndoRedoState();
357
+ });
358
+
359
+ // File-backed screen deletion is not a geometry-only edit. The screen rows
360
+ // are hard-deleted, so suppress MultiScreenCanvas' local frame-history
361
+ // entry; otherwise undo would restore geometry for files that no longer
362
+ // exist.
363
+ syncUndoRedoState();
364
+ }