@agent-native/core 0.182.1 → 0.182.2-nightly-20260917225811
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.
- package/corpus/README.md +1 -1
- package/corpus/templates/clips/app/root.tsx +59 -10
- package/corpus/templates/clips/changelog/2026-09-17-desktop-popover-closes-when-clicking-away.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-17-self-hosted-extension-sign-in.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/background.ts +45 -25
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +43 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +55 -6
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +107 -19
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +1 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +54 -22
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +174 -14
- package/corpus/templates/design/actions/apply-visual-edit.ts +3 -1
- package/corpus/templates/design/actions/create-component.ts +173 -12
- package/corpus/templates/design/actions/create-design-from-template.ts +50 -35
- package/corpus/templates/design/actions/create-design-version.ts +21 -0
- package/corpus/templates/design/actions/create-file.ts +35 -33
- package/corpus/templates/design/actions/delete-design.ts +29 -28
- package/corpus/templates/design/actions/delete-file.ts +154 -23
- package/corpus/templates/design/actions/generate-design.ts +28 -16
- package/corpus/templates/design/actions/get-component-details.ts +272 -33
- package/corpus/templates/design/actions/get-design-version.ts +9 -1
- package/corpus/templates/design/actions/go-to-main-component.ts +37 -35
- package/corpus/templates/design/actions/index-components.ts +241 -126
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +12 -9
- package/corpus/templates/design/actions/present-design-variants.ts +68 -72
- package/corpus/templates/design/actions/rename-component.ts +294 -0
- package/corpus/templates/design/actions/rename-screen.ts +10 -10
- package/corpus/templates/design/actions/update-file.ts +327 -330
- package/corpus/templates/design/actions/update-screen-source.ts +2 -43
- package/corpus/templates/design/app/components/design/EditPanel.tsx +17 -1
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +228 -24
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +17 -1
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +151 -32
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +4 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +8 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +55 -33
- package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +2 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +4 -2
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +273 -60
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +9 -2
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +57 -9
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +13 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +14 -0
- package/corpus/templates/design/app/components/design/types.ts +9 -1
- package/corpus/templates/design/app/hooks/use-design-versions.ts +3 -2
- package/corpus/templates/design/app/i18n-breakpoints.ts +0 -110
- package/corpus/templates/design/app/i18n-data.ts +0 -10
- package/corpus/templates/design/app/pages/DesignEditor.tsx +669 -161
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +20 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +21 -3
- package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +2 -0
- package/corpus/templates/design/app/pages/design-editor/commands/create-component.ts +44 -3
- package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +69 -47
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +157 -52
- package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +76 -1
- package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +152 -44
- package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +131 -2
- package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +5 -1
- package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +26 -1
- package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +1 -0
- package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +58 -8
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +63 -2
- package/corpus/templates/design/app/pages/design-editor/history.ts +3 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +74 -22
- package/corpus/templates/design/app/pages/design-editor/source-publication.ts +14 -2
- package/corpus/templates/design/changelog/2026-09-16-design-components-retain-linked-identities-when-edited-and-d.md +6 -0
- package/corpus/templates/design/changelog/2026-09-16-fix-design-nested-layer-dragging-and-screen-duplication-pari.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-drag-and-drop-keeps-duplicates-cross-screen-moves-and.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-design-preserves-reusable-component-identity-and-props-acros.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-open-vector-paths-no-longer-show-fills-and-interact-dimensio.md +6 -0
- package/corpus/templates/design/changelog/2026-09-17-screen-deletion-is-immediate-and-undoable-with-history-that-.md +6 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/index.html +98 -0
- package/corpus/templates/design/e2e/fixtures/design-component-react/inline-component.html +65 -0
- package/corpus/templates/design/e2e/helpers.ts +6 -1
- package/corpus/templates/design/server/lib/design-data-mutation.ts +9 -5
- package/corpus/templates/design/server/lib/design-snapshot.ts +28 -16
- package/corpus/templates/design/server/lib/design-versions.ts +208 -130
- package/corpus/templates/design/server/lib/fusion-screens.ts +46 -20
- package/corpus/templates/design/server/lib/import-design-files.ts +5 -1
- package/corpus/templates/design/server/lib/localhost-connection.ts +54 -0
- package/corpus/templates/design/server/source-workspace.ts +412 -356
- package/corpus/templates/design/shared/component-library.ts +36 -6
- package/corpus/templates/design/shared/component-model.ts +12 -2
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +231 -5
- package/corpus/templates/design/shared/source-mode.ts +22 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/client/AssistantChat.d.ts +4 -1
- package/dist/client/AssistantChat.js +26 -3
- package/dist/client/chat/agentkit-protocol.js +148 -34
- package/dist/client/chat/message-components.js +7 -5
- package/dist/client/chat/tool-call-display.js +4 -3
- package/dist/client/conversation/AgentConversation.js +4 -2
- package/dist/client/tool-display.d.ts +9 -0
- package/dist/client/tool-display.js +15 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/localization/core-messages/ar-SA.js +2 -0
- package/dist/localization/core-messages/de-DE.js +2 -0
- package/dist/localization/core-messages/en-US.d.ts +2 -0
- package/dist/localization/core-messages/en-US.js +2 -0
- package/dist/localization/core-messages/es-ES.js +2 -0
- package/dist/localization/core-messages/fr-FR.js +2 -0
- package/dist/localization/core-messages/hi-IN.js +2 -0
- package/dist/localization/core-messages/ja-JP.js +2 -0
- package/dist/localization/core-messages/ko-KR.js +2 -0
- package/dist/localization/core-messages/pt-BR.js +2 -0
- package/dist/localization/core-messages/zh-CN.js +2 -0
- package/dist/localization/core-messages/zh-TW.js +2 -0
- package/dist/localization/core-messages.d.ts +2 -0
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +1 -1
- package/package.json +3 -3
- package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +0 -66
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import {
|
|
3
|
+
agentEnterDocument,
|
|
4
|
+
agentLeaveDocument,
|
|
5
|
+
} from "@agent-native/core/collab";
|
|
6
|
+
import { getRequestUserEmail } from "@agent-native/core/server/request-context";
|
|
7
|
+
import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
|
|
8
|
+
import { and, eq } from "drizzle-orm";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
12
|
+
import { snapshotDesignBeforeAgentEdit } from "../server/lib/design-versions.js";
|
|
13
|
+
import {
|
|
14
|
+
lockDesignSourceMutation,
|
|
15
|
+
readLiveSourceFile,
|
|
16
|
+
resolveSourceWorkspace,
|
|
17
|
+
writeInlineSourceFilesBatch,
|
|
18
|
+
} from "../server/source-workspace.js";
|
|
19
|
+
import {
|
|
20
|
+
buildCodeLayerProjection,
|
|
21
|
+
patchCodeLayerNodeAttributes,
|
|
22
|
+
} from "../shared/code-layer.js";
|
|
23
|
+
import {
|
|
24
|
+
COMPONENT_ID_ATTR,
|
|
25
|
+
COMPONENT_NAME_ATTR,
|
|
26
|
+
COMPONENT_REF_ATTR,
|
|
27
|
+
componentIndexId,
|
|
28
|
+
componentNameFor,
|
|
29
|
+
} from "../shared/component-model.js";
|
|
30
|
+
import { designSourceTypeFromData } from "../shared/source-mode.js";
|
|
31
|
+
|
|
32
|
+
export class ComponentRenameAmbiguousError extends Error {
|
|
33
|
+
readonly statusCode = 409;
|
|
34
|
+
|
|
35
|
+
constructor(
|
|
36
|
+
message = "The component is a legacy same-name-only component and cannot be renamed safely.",
|
|
37
|
+
) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "ComponentRenameAmbiguousError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class ComponentRenameConflictError extends Error {
|
|
44
|
+
readonly statusCode = 409;
|
|
45
|
+
|
|
46
|
+
constructor(message = "A component with this name already exists.") {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "ComponentRenameConflictError";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function renameLinkedComponentHtml(
|
|
53
|
+
html: string,
|
|
54
|
+
componentId: string,
|
|
55
|
+
newName: string,
|
|
56
|
+
): { content: string; changed: boolean } {
|
|
57
|
+
const projection = buildCodeLayerProjection(html);
|
|
58
|
+
const updates = projection.nodes
|
|
59
|
+
.filter(
|
|
60
|
+
(node) =>
|
|
61
|
+
node.dataAttributes[COMPONENT_ID_ATTR]?.trim() === componentId ||
|
|
62
|
+
node.dataAttributes[COMPONENT_REF_ATTR]?.trim() === componentId,
|
|
63
|
+
)
|
|
64
|
+
.map((node) => ({
|
|
65
|
+
node,
|
|
66
|
+
attributes: { [COMPONENT_NAME_ATTR]: newName },
|
|
67
|
+
}));
|
|
68
|
+
const content =
|
|
69
|
+
updates.length > 0
|
|
70
|
+
? (patchCodeLayerNodeAttributes(html, updates) ?? html)
|
|
71
|
+
: html;
|
|
72
|
+
return { content, changed: content !== html };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default defineAction({
|
|
76
|
+
description:
|
|
77
|
+
"Rename a canonical linked Design component across every inline HTML file.",
|
|
78
|
+
schema: z.object({
|
|
79
|
+
designId: z.string().trim().min(1),
|
|
80
|
+
componentId: z
|
|
81
|
+
.string()
|
|
82
|
+
.trim()
|
|
83
|
+
.min(1)
|
|
84
|
+
.describe("Persisted canonical component id"),
|
|
85
|
+
newName: z.string().trim().min(1).max(255),
|
|
86
|
+
}),
|
|
87
|
+
run: async ({ designId, componentId, newName }, context) => {
|
|
88
|
+
const access = await resolveAccess("design", designId);
|
|
89
|
+
if (!access) throw new Error("Design not found");
|
|
90
|
+
if (
|
|
91
|
+
designSourceTypeFromData((access.resource as { data?: unknown }).data) !==
|
|
92
|
+
"inline"
|
|
93
|
+
) {
|
|
94
|
+
return { designId, componentId, renamed: false, ctaRequired: true };
|
|
95
|
+
}
|
|
96
|
+
await assertAccess("design", designId, "editor");
|
|
97
|
+
const workspace = await resolveSourceWorkspace(designId, {
|
|
98
|
+
includeContent: true,
|
|
99
|
+
includeBoard: true,
|
|
100
|
+
});
|
|
101
|
+
const htmlFiles = workspace.files.filter(
|
|
102
|
+
(file) => file.fileType === "html",
|
|
103
|
+
);
|
|
104
|
+
const liveFiles = await Promise.all(
|
|
105
|
+
htmlFiles.map(async (file) => ({
|
|
106
|
+
file,
|
|
107
|
+
live: await readLiveSourceFile(file),
|
|
108
|
+
})),
|
|
109
|
+
);
|
|
110
|
+
const projectedNodes = liveFiles.flatMap(
|
|
111
|
+
({ live }) => buildCodeLayerProjection(live.content).nodes,
|
|
112
|
+
);
|
|
113
|
+
const canonical = projectedNodes.find(
|
|
114
|
+
(node) => node.dataAttributes[COMPONENT_ID_ATTR]?.trim() === componentId,
|
|
115
|
+
);
|
|
116
|
+
const oldName = canonical ? componentNameFor(canonical) : null;
|
|
117
|
+
if (!oldName) {
|
|
118
|
+
throw new ComponentRenameAmbiguousError();
|
|
119
|
+
}
|
|
120
|
+
if (newName === oldName) {
|
|
121
|
+
return { designId, componentId, renamed: false };
|
|
122
|
+
}
|
|
123
|
+
const oldIndexId = componentIndexId(designId, oldName);
|
|
124
|
+
const newIndexId = componentIndexId(designId, newName);
|
|
125
|
+
if (newIndexId !== oldIndexId) {
|
|
126
|
+
const [existing] = await getDb()
|
|
127
|
+
.select({ id: schema.componentIndex.id })
|
|
128
|
+
.from(schema.componentIndex)
|
|
129
|
+
.where(
|
|
130
|
+
and(
|
|
131
|
+
eq(schema.componentIndex.id, newIndexId),
|
|
132
|
+
eq(schema.componentIndex.designId, designId),
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
.limit(1);
|
|
136
|
+
if (existing) throw new ComponentRenameConflictError();
|
|
137
|
+
}
|
|
138
|
+
const conflictingMarkup = projectedNodes.some((node) => {
|
|
139
|
+
const name = componentNameFor(node);
|
|
140
|
+
if (!name || componentIndexId(designId, name) !== newIndexId) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
return (
|
|
144
|
+
node.dataAttributes[COMPONENT_ID_ATTR]?.trim() !== componentId &&
|
|
145
|
+
node.dataAttributes[COMPONENT_REF_ATTR]?.trim() !== componentId
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
if (conflictingMarkup) throw new ComponentRenameConflictError();
|
|
149
|
+
|
|
150
|
+
const linkedNodes = projectedNodes.filter(
|
|
151
|
+
(node) =>
|
|
152
|
+
node.dataAttributes[COMPONENT_ID_ATTR]?.trim() === componentId ||
|
|
153
|
+
node.dataAttributes[COMPONENT_REF_ATTR]?.trim() === componentId,
|
|
154
|
+
);
|
|
155
|
+
const componentSelectors = Array.from(
|
|
156
|
+
new Set(
|
|
157
|
+
linkedNodes.map(
|
|
158
|
+
(node) =>
|
|
159
|
+
`[data-agent-native-node-id="${node.dataAttributes["data-agent-native-node-id"] ?? node.id}"]`,
|
|
160
|
+
),
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
const legacySelectors = Array.from(
|
|
164
|
+
new Set(
|
|
165
|
+
projectedNodes
|
|
166
|
+
.filter(
|
|
167
|
+
(node) =>
|
|
168
|
+
componentNameFor(node) === oldName &&
|
|
169
|
+
!node.dataAttributes[COMPONENT_ID_ATTR]?.trim() &&
|
|
170
|
+
!node.dataAttributes[COMPONENT_REF_ATTR]?.trim(),
|
|
171
|
+
)
|
|
172
|
+
.map(
|
|
173
|
+
(node) =>
|
|
174
|
+
`[data-agent-native-node-id="${node.dataAttributes["data-agent-native-node-id"] ?? node.id}"]`,
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
);
|
|
178
|
+
const batches = liveFiles.map(({ file, live }) => ({
|
|
179
|
+
file: { ...file, content: live.content },
|
|
180
|
+
content: renameLinkedComponentHtml(live.content, componentId, newName)
|
|
181
|
+
.content,
|
|
182
|
+
expectedVersionHash: live.versionHash,
|
|
183
|
+
}));
|
|
184
|
+
const changed = batches.filter(
|
|
185
|
+
({ file, content }) => file.content !== content,
|
|
186
|
+
);
|
|
187
|
+
if (changed.length === 0) return { designId, componentId, renamed: false };
|
|
188
|
+
|
|
189
|
+
await snapshotDesignBeforeAgentEdit(designId, context);
|
|
190
|
+
const entered = htmlFiles.map((file) => file.id).sort();
|
|
191
|
+
entered.forEach((id) => agentEnterDocument(id));
|
|
192
|
+
try {
|
|
193
|
+
const result = await writeInlineSourceFilesBatch({
|
|
194
|
+
designId,
|
|
195
|
+
files: batches,
|
|
196
|
+
expectedHtmlFileIds: htmlFiles.map((file) => file.id),
|
|
197
|
+
afterFilesPersist: async (tx, updatedAt) => {
|
|
198
|
+
await lockDesignSourceMutation(tx, designId);
|
|
199
|
+
if (newIndexId !== oldIndexId) {
|
|
200
|
+
const destination = await tx.execute({
|
|
201
|
+
sql: "SELECT id FROM component_index WHERE id = ? AND design_id = ? FOR UPDATE",
|
|
202
|
+
args: [newIndexId, designId],
|
|
203
|
+
});
|
|
204
|
+
if (destination.rows.length > 0) {
|
|
205
|
+
throw new ComponentRenameConflictError();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const moved = await tx.execute({
|
|
209
|
+
sql: "UPDATE component_index SET id = ?, name = ?, runtime_selectors = ?, updated_at = ? WHERE id = ? AND design_id = ? RETURNING id",
|
|
210
|
+
args: [
|
|
211
|
+
newIndexId,
|
|
212
|
+
newName,
|
|
213
|
+
JSON.stringify(componentSelectors),
|
|
214
|
+
updatedAt,
|
|
215
|
+
oldIndexId,
|
|
216
|
+
designId,
|
|
217
|
+
],
|
|
218
|
+
});
|
|
219
|
+
if (moved.rows.length === 1) {
|
|
220
|
+
if (legacySelectors.length > 0 && newIndexId !== oldIndexId) {
|
|
221
|
+
await tx.execute({
|
|
222
|
+
sql: `INSERT INTO component_index (
|
|
223
|
+
id, design_id, source_ref, name, file_path, export_name,
|
|
224
|
+
props, variants, stories, runtime_selectors, created_at,
|
|
225
|
+
updated_at, owner_email, org_id, visibility
|
|
226
|
+
)
|
|
227
|
+
SELECT ?, design_id, source_ref, ?, file_path, export_name,
|
|
228
|
+
props, variants, stories, ?, created_at, ?, owner_email,
|
|
229
|
+
org_id, visibility
|
|
230
|
+
FROM component_index WHERE id = ? AND design_id = ?`,
|
|
231
|
+
args: [
|
|
232
|
+
oldIndexId,
|
|
233
|
+
oldName,
|
|
234
|
+
JSON.stringify(legacySelectors),
|
|
235
|
+
updatedAt,
|
|
236
|
+
newIndexId,
|
|
237
|
+
designId,
|
|
238
|
+
],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
const designOwner = (access.resource as { ownerEmail?: unknown })
|
|
243
|
+
.ownerEmail;
|
|
244
|
+
const ownerEmail =
|
|
245
|
+
getRequestUserEmail() ??
|
|
246
|
+
(typeof designOwner === "string" && designOwner
|
|
247
|
+
? designOwner
|
|
248
|
+
: null);
|
|
249
|
+
if (!ownerEmail) throw new Error("no authenticated user");
|
|
250
|
+
await tx.execute({
|
|
251
|
+
sql: `INSERT INTO component_index
|
|
252
|
+
(id, design_id, name, runtime_selectors, owner_email, created_at, updated_at)
|
|
253
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
254
|
+
args: [
|
|
255
|
+
newIndexId,
|
|
256
|
+
designId,
|
|
257
|
+
newName,
|
|
258
|
+
JSON.stringify(componentSelectors),
|
|
259
|
+
ownerEmail,
|
|
260
|
+
updatedAt,
|
|
261
|
+
updatedAt,
|
|
262
|
+
],
|
|
263
|
+
});
|
|
264
|
+
if (legacySelectors.length > 0 && newIndexId !== oldIndexId) {
|
|
265
|
+
await tx.execute({
|
|
266
|
+
sql: `INSERT INTO component_index
|
|
267
|
+
(id, design_id, name, runtime_selectors, owner_email, created_at, updated_at)
|
|
268
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
269
|
+
args: [
|
|
270
|
+
oldIndexId,
|
|
271
|
+
designId,
|
|
272
|
+
oldName,
|
|
273
|
+
JSON.stringify(legacySelectors),
|
|
274
|
+
ownerEmail,
|
|
275
|
+
updatedAt,
|
|
276
|
+
updatedAt,
|
|
277
|
+
],
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
designId,
|
|
285
|
+
componentId,
|
|
286
|
+
newName,
|
|
287
|
+
renamed: true,
|
|
288
|
+
files: result.files,
|
|
289
|
+
};
|
|
290
|
+
} finally {
|
|
291
|
+
entered.forEach((id) => agentLeaveDocument(id));
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
});
|
|
@@ -9,6 +9,10 @@ import { and, eq, isNull, sql } from "drizzle-orm";
|
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
|
|
11
11
|
import { getDb, schema } from "../server/db/index.js";
|
|
12
|
+
import {
|
|
13
|
+
designSourceMutationLockKey,
|
|
14
|
+
lockDesignFilesTable,
|
|
15
|
+
} from "../server/source-workspace.js";
|
|
12
16
|
import { isProbablyHtmlDocumentContent } from "../shared/html-content.js";
|
|
13
17
|
import {
|
|
14
18
|
renameFilenamePreservingExtension,
|
|
@@ -180,15 +184,10 @@ export default defineAction({
|
|
|
180
184
|
for (let attempt = 0; attempt < MAX_RENAME_ATTEMPTS; attempt += 1) {
|
|
181
185
|
try {
|
|
182
186
|
return await db.transaction(async (tx) => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
).execute(
|
|
189
|
-
sql`LOCK TABLE design_files IN SHARE ROW EXCLUSIVE MODE`,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
187
|
+
await tx.execute(
|
|
188
|
+
sql`SELECT pg_advisory_xact_lock(hashtextextended(${designSourceMutationLockKey(scopedFile.designId)}, 0::bigint))`,
|
|
189
|
+
);
|
|
190
|
+
await lockDesignFilesTable(tx);
|
|
192
191
|
|
|
193
192
|
const [design] = await tx
|
|
194
193
|
.select({ updatedAt: schema.designs.updatedAt })
|
|
@@ -209,7 +208,8 @@ export default defineAction({
|
|
|
209
208
|
updatedAt: schema.designFiles.updatedAt,
|
|
210
209
|
})
|
|
211
210
|
.from(schema.designFiles)
|
|
212
|
-
.where(eq(schema.designFiles.designId, scopedFile.designId))
|
|
211
|
+
.where(eq(schema.designFiles.designId, scopedFile.designId))
|
|
212
|
+
.for("update");
|
|
213
213
|
const target = currentFiles.find((file) => file.id === id);
|
|
214
214
|
if (!target) throw new Error(`Screen not found: ${id}`);
|
|
215
215
|
if (target.fileType !== "html") {
|