@agent-native/core 0.84.10 → 0.84.13
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/core/CHANGELOG.md +48 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/durable-background.ts +30 -0
- package/corpus/core/src/agent/production-agent.ts +34 -6
- package/corpus/core/src/agent/run-manager.ts +45 -2
- package/corpus/core/src/agent/run-store.ts +29 -6
- package/corpus/core/src/chat-threads/store.ts +2 -0
- package/corpus/core/src/cli/skills.ts +10 -6
- package/corpus/core/src/client/AssistantChat.tsx +40 -9
- package/corpus/core/src/client/agent-chat-adapter.ts +23 -2
- package/corpus/core/src/client/chat/run-recovery.tsx +52 -44
- package/corpus/core/src/client/chat/tool-call-display.tsx +7 -2
- package/corpus/core/src/client/require-session.tsx +20 -1
- package/corpus/core/src/client/sharing/ShareButton.tsx +9 -3
- package/corpus/core/src/collab/storage.ts +40 -5
- package/corpus/core/src/server/agent-chat-plugin.ts +1 -0
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/shared/streaming-text-smoothing.ts +10 -5
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +7 -6
- package/corpus/templates/assets/AGENTS.md +15 -0
- package/corpus/templates/assets/actions/_helpers.ts +3 -1
- package/corpus/templates/assets/actions/create-generation-preset.ts +12 -1
- package/corpus/templates/assets/actions/generate-asset.ts +5 -1
- package/corpus/templates/assets/actions/generate-image-batch.ts +6 -1
- package/corpus/templates/assets/actions/generate-image.ts +18 -11
- package/corpus/templates/assets/actions/open-asset-picker.ts +4 -2
- package/corpus/templates/assets/actions/update-generation-preset.ts +16 -3
- package/corpus/templates/assets/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/assets/app/i18n-data.ts +30 -0
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +42 -7
- package/corpus/templates/assets/app/routes/library.tsx +4 -2
- package/corpus/templates/assets/changelog/2026-07-01-generation-presets-can-now-composite-your-brand-s-canonical-.md +6 -0
- package/corpus/templates/assets/changelog/2026-07-01-tagging-a-preset-now-briefs-the-agent-on-that-preset-s-aesth.md +6 -0
- package/corpus/templates/assets/server/lib/preset-chat-context.ts +118 -0
- package/corpus/templates/assets/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/assets/shared/api.ts +1 -0
- package/corpus/templates/clips/app/components/library/recording-card.tsx +14 -4
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +10 -12
- package/corpus/templates/clips/app/hooks/use-library.ts +7 -4
- package/corpus/templates/clips/app/lib/recording-status.ts +32 -0
- package/corpus/templates/clips/changelog/2026-07-01-dictation-keeps-listening-through-natural-pauses-and-gives-c.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-redoing-a-paused-desktop-recording-no-longer-leaves-the-recording-controls-disabled.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-stalled-clip-uploads-now-update-in-the-library-and-surface-as-failed.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +36 -18
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +99 -27
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +4 -43
- package/corpus/templates/clips/desktop/src/overlays/toolbar.tsx +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +5 -48
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +4 -5
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +15 -6
- package/corpus/templates/design/AGENTS.md +8 -4
- package/corpus/templates/design/actions/create-design.ts +1 -0
- package/corpus/templates/design/actions/duplicate-design.ts +3 -1
- package/corpus/templates/design/actions/edit-design.ts +43 -13
- package/corpus/templates/design/actions/export-html.ts +4 -2
- package/corpus/templates/design/actions/export-pdf.ts +3 -1
- package/corpus/templates/design/actions/export-svg.ts +4 -2
- package/corpus/templates/design/actions/export-zip.ts +10 -3
- package/corpus/templates/design/actions/generate-design.ts +5 -1
- package/corpus/templates/design/actions/present-design-variants.ts +17 -12
- package/corpus/templates/design/actions/update-file.ts +68 -17
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +3 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +35 -3
- package/corpus/templates/design/app/components/design/MotionDock.tsx +46 -11
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +80 -30
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +60 -10
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +2 -0
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +4 -0
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +2 -2
- package/corpus/templates/design/app/i18n-data.ts +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +493 -109
- package/corpus/templates/design/app/pages/VisualEdit.tsx +4 -16
- package/corpus/templates/design/changelog/2026-07-01-org-members-can-see-design-projects-created-in-their-workspace.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +2 -2
- package/corpus/templates/design/server/plugins/db.ts +9 -0
- package/dist/agent/durable-background.d.ts +3 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +19 -0
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +29 -6
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +40 -2
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +28 -6
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +2 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +10 -6
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +10 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +26 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -2
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +15 -10
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +1 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +3 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/require-session.d.ts +14 -0
- package/dist/client/require-session.d.ts.map +1 -1
- package/dist/client/require-session.js +19 -1
- package/dist/client/require-session.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +4 -2
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -1
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/collab/storage.d.ts.map +1 -1
- package/dist/collab/storage.js +31 -5
- package/dist/collab/storage.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/streaming-text-smoothing.d.ts.map +1 -1
- package/dist/shared/streaming-text-smoothing.js +8 -5
- package/dist/shared/streaming-text-smoothing.js.map +1 -1
- package/package.json +1 -1
|
@@ -139,20 +139,31 @@ export default defineAction({
|
|
|
139
139
|
"`replace` adds the wrapper around the original text. For broad copy-only " +
|
|
140
140
|
'changes such as translating all visible text, use `mode: "replace-file"` ' +
|
|
141
141
|
"with `replacementContent`: the complete updated file content copied from " +
|
|
142
|
-
"the snapshot with only the requested copy changed.
|
|
143
|
-
"
|
|
142
|
+
"the snapshot with only the requested copy changed. After a variant pick " +
|
|
143
|
+
"or any other selected-screen follow-up, pass the exact `fileId` from " +
|
|
144
|
+
'`get-design-snapshot` and use `mode: "replace-file"` when replacing ' +
|
|
145
|
+
"the representative placeholder with the full chosen direction. Use " +
|
|
146
|
+
"`generate-design` instead only for brand-new files.",
|
|
144
147
|
schema: z
|
|
145
148
|
.object({
|
|
146
149
|
designId: z.string().describe("Design project ID"),
|
|
150
|
+
fileId: z
|
|
151
|
+
.string()
|
|
152
|
+
.optional()
|
|
153
|
+
.describe(
|
|
154
|
+
"Optional exact design file ID to edit. Use this after a variant pick or selected-screen snapshot; when provided, it wins over filename.",
|
|
155
|
+
),
|
|
147
156
|
filename: z
|
|
148
157
|
.string()
|
|
149
|
-
.
|
|
150
|
-
.describe(
|
|
158
|
+
.optional()
|
|
159
|
+
.describe(
|
|
160
|
+
"File to edit (e.g. 'index.html'). Defaults to index.html only when fileId is omitted.",
|
|
161
|
+
),
|
|
151
162
|
mode: z
|
|
152
163
|
.enum(["search-replace", "replace-file"])
|
|
153
164
|
.optional()
|
|
154
165
|
.describe(
|
|
155
|
-
"Defaults to search-replace. Use replace-file for broad copy-only edits
|
|
166
|
+
"Defaults to search-replace. Use replace-file for selected variant expansion or broad copy-only edits after reading get-design-snapshot.",
|
|
156
167
|
),
|
|
157
168
|
edits: editBlocksSchema
|
|
158
169
|
.optional()
|
|
@@ -164,7 +175,7 @@ export default defineAction({
|
|
|
164
175
|
.min(1)
|
|
165
176
|
.optional()
|
|
166
177
|
.describe(
|
|
167
|
-
"Complete updated file content. Use only with mode=replace-file for broad copy-only changes; preserve all HTML structure, CSS, scripts, and tweaks from get-design-snapshot.",
|
|
178
|
+
"Complete updated file content. Use only with mode=replace-file for selected variant expansion or broad copy-only changes; preserve all HTML structure, CSS, scripts, and tweaks from get-design-snapshot.",
|
|
168
179
|
),
|
|
169
180
|
})
|
|
170
181
|
.superRefine((value, ctx) => {
|
|
@@ -201,16 +212,31 @@ export default defineAction({
|
|
|
201
212
|
});
|
|
202
213
|
}
|
|
203
214
|
}),
|
|
204
|
-
run: async ({
|
|
215
|
+
run: async ({
|
|
216
|
+
designId,
|
|
217
|
+
fileId,
|
|
218
|
+
filename,
|
|
219
|
+
edits,
|
|
220
|
+
mode,
|
|
221
|
+
replacementContent,
|
|
222
|
+
}) => {
|
|
205
223
|
await assertAccess("design", designId, "editor");
|
|
206
224
|
|
|
207
225
|
const db = getDb();
|
|
208
226
|
const now = new Date().toISOString();
|
|
227
|
+
const requestedFileId = fileId?.trim();
|
|
228
|
+
const targetFilename = requestedFileId
|
|
229
|
+
? undefined
|
|
230
|
+
: filename?.trim() || "index.html";
|
|
231
|
+
const targetCondition = requestedFileId
|
|
232
|
+
? eq(schema.designFiles.id, requestedFileId)
|
|
233
|
+
: eq(schema.designFiles.filename, targetFilename!);
|
|
209
234
|
|
|
210
|
-
// Resolve the target file (access-scoped) by design + filename.
|
|
235
|
+
// Resolve the target file (access-scoped) by design + fileId or filename.
|
|
211
236
|
const [file] = await db
|
|
212
237
|
.select({
|
|
213
238
|
id: schema.designFiles.id,
|
|
239
|
+
filename: schema.designFiles.filename,
|
|
214
240
|
content: schema.designFiles.content,
|
|
215
241
|
})
|
|
216
242
|
.from(schema.designFiles)
|
|
@@ -221,14 +247,18 @@ export default defineAction({
|
|
|
221
247
|
.where(
|
|
222
248
|
and(
|
|
223
249
|
eq(schema.designFiles.designId, designId),
|
|
224
|
-
|
|
250
|
+
targetCondition,
|
|
225
251
|
accessFilter(schema.designs, schema.designShares),
|
|
226
252
|
),
|
|
227
253
|
)
|
|
228
254
|
.limit(1);
|
|
229
255
|
|
|
230
256
|
if (!file) {
|
|
231
|
-
throw new Error(
|
|
257
|
+
throw new Error(
|
|
258
|
+
requestedFileId
|
|
259
|
+
? `File id "${requestedFileId}" not found in design ${designId}`
|
|
260
|
+
: `File "${targetFilename}" not found in design ${designId}`,
|
|
261
|
+
);
|
|
232
262
|
}
|
|
233
263
|
|
|
234
264
|
// Prefer live collab content so we edit in-flight changes, not a stale
|
|
@@ -265,13 +295,13 @@ export default defineAction({
|
|
|
265
295
|
selection: firstSearch
|
|
266
296
|
? `[data-edit-target="${firstSearch.slice(0, 40)}"]`
|
|
267
297
|
: null,
|
|
268
|
-
editingFile: filename,
|
|
298
|
+
editingFile: file.filename,
|
|
269
299
|
designId,
|
|
270
300
|
});
|
|
271
301
|
} else {
|
|
272
302
|
agentUpdateSelection(file.id, {
|
|
273
303
|
selection: null,
|
|
274
|
-
editingFile: filename,
|
|
304
|
+
editingFile: file.filename,
|
|
275
305
|
designId,
|
|
276
306
|
});
|
|
277
307
|
}
|
|
@@ -301,7 +331,7 @@ export default defineAction({
|
|
|
301
331
|
|
|
302
332
|
return {
|
|
303
333
|
designId,
|
|
304
|
-
filename,
|
|
334
|
+
filename: file.filename,
|
|
305
335
|
fileId: file.id,
|
|
306
336
|
mode: resolvedMode,
|
|
307
337
|
editsApplied: applied,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
exportFilename,
|
|
10
10
|
trySaveExportFile,
|
|
11
11
|
} from "../server/lib/design-export.js";
|
|
12
|
+
import { isBoardFile } from "../shared/board-file.js";
|
|
12
13
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
13
14
|
|
|
14
15
|
export default defineAction({
|
|
@@ -31,12 +32,13 @@ export default defineAction({
|
|
|
31
32
|
.select()
|
|
32
33
|
.from(schema.designFiles)
|
|
33
34
|
.where(eq(schema.designFiles.designId, id));
|
|
35
|
+
const exportFiles = files.filter((file) => !isBoardFile(file.filename));
|
|
34
36
|
|
|
35
|
-
const html = buildStandaloneHtml({ title: row.title, files });
|
|
37
|
+
const html = buildStandaloneHtml({ title: row.title, files: exportFiles });
|
|
36
38
|
|
|
37
39
|
const filename = exportFilename(row.title, "html");
|
|
38
40
|
const saveResult = await trySaveExportFile(filename, html);
|
|
39
41
|
|
|
40
|
-
return { html, filename, ...saveResult, fileCount:
|
|
42
|
+
return { html, filename, ...saveResult, fileCount: exportFiles.length };
|
|
41
43
|
},
|
|
42
44
|
});
|
|
@@ -4,6 +4,7 @@ import { eq } from "drizzle-orm";
|
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
|
|
6
6
|
import { getDb, schema } from "../server/db/index.js";
|
|
7
|
+
import { isBoardFile } from "../shared/board-file.js";
|
|
7
8
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
8
9
|
|
|
9
10
|
export default defineAction({
|
|
@@ -27,6 +28,7 @@ export default defineAction({
|
|
|
27
28
|
.select()
|
|
28
29
|
.from(schema.designFiles)
|
|
29
30
|
.where(eq(schema.designFiles.designId, id));
|
|
31
|
+
const exportFiles = files.filter((file) => !isBoardFile(file.filename));
|
|
30
32
|
|
|
31
33
|
return {
|
|
32
34
|
id: row.id,
|
|
@@ -34,7 +36,7 @@ export default defineAction({
|
|
|
34
36
|
description: row.description,
|
|
35
37
|
projectType: row.projectType,
|
|
36
38
|
data: row.data ?? null,
|
|
37
|
-
files:
|
|
39
|
+
files: exportFiles.map((f) => ({
|
|
38
40
|
id: f.id,
|
|
39
41
|
filename: f.filename,
|
|
40
42
|
fileType: f.fileType,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
exportFilename,
|
|
11
11
|
trySaveExportFile,
|
|
12
12
|
} from "../server/lib/design-export.js";
|
|
13
|
+
import { isBoardFile } from "../shared/board-file.js";
|
|
13
14
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
14
15
|
|
|
15
16
|
export default defineAction({
|
|
@@ -45,8 +46,9 @@ export default defineAction({
|
|
|
45
46
|
.select()
|
|
46
47
|
.from(schema.designFiles)
|
|
47
48
|
.where(eq(schema.designFiles.designId, id));
|
|
49
|
+
const exportFiles = files.filter((file) => !isBoardFile(file.filename));
|
|
48
50
|
|
|
49
|
-
const html = buildStandaloneHtml({ title: row.title, files });
|
|
51
|
+
const html = buildStandaloneHtml({ title: row.title, files: exportFiles });
|
|
50
52
|
const svg = buildSvgForeignObject({
|
|
51
53
|
html,
|
|
52
54
|
width,
|
|
@@ -56,6 +58,6 @@ export default defineAction({
|
|
|
56
58
|
const filename = exportFilename(row.title, "svg");
|
|
57
59
|
const saveResult = await trySaveExportFile(filename, svg);
|
|
58
60
|
|
|
59
|
-
return { svg, filename, ...saveResult, fileCount:
|
|
61
|
+
return { svg, filename, ...saveResult, fileCount: exportFiles.length };
|
|
60
62
|
},
|
|
61
63
|
});
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
exportFilename,
|
|
9
9
|
trySaveExportFile,
|
|
10
10
|
} from "../server/lib/design-export.js";
|
|
11
|
+
import { isBoardFile } from "../shared/board-file.js";
|
|
11
12
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
12
13
|
|
|
13
14
|
const METADATA_ARCHIVE_DIR = "agent-native-metadata";
|
|
@@ -40,6 +41,7 @@ export default defineAction({
|
|
|
40
41
|
.select()
|
|
41
42
|
.from(schema.designFiles)
|
|
42
43
|
.where(eq(schema.designFiles.designId, id));
|
|
44
|
+
const exportFiles = files.filter((file) => !isBoardFile(file.filename));
|
|
43
45
|
|
|
44
46
|
// Dynamic import JSZip
|
|
45
47
|
const JSZip = (await import("jszip")).default;
|
|
@@ -57,14 +59,14 @@ export default defineAction({
|
|
|
57
59
|
"",
|
|
58
60
|
"## Files",
|
|
59
61
|
"",
|
|
60
|
-
...
|
|
62
|
+
...exportFiles.map((f) => `- ${f.filename} (${f.fileType})`),
|
|
61
63
|
].join("\n");
|
|
62
64
|
|
|
63
65
|
zip.file(`${METADATA_ARCHIVE_DIR}/README.md`, readme);
|
|
64
66
|
|
|
65
67
|
// Preserve design-relative paths so exported HTML keeps working with
|
|
66
68
|
// sibling CSS/assets. Strip traversal segments defensively for legacy rows.
|
|
67
|
-
for (const [index, file] of
|
|
69
|
+
for (const [index, file] of exportFiles.entries()) {
|
|
68
70
|
const filename = safeArchivePath(
|
|
69
71
|
file.filename,
|
|
70
72
|
`design-file-${index + 1}.txt`,
|
|
@@ -84,6 +86,11 @@ export default defineAction({
|
|
|
84
86
|
const filename = exportFilename(row.title, "zip");
|
|
85
87
|
const saveResult = await trySaveExportFile(filename, zipBuffer);
|
|
86
88
|
|
|
87
|
-
return {
|
|
89
|
+
return {
|
|
90
|
+
zipBase64,
|
|
91
|
+
filename,
|
|
92
|
+
...saveResult,
|
|
93
|
+
fileCount: exportFiles.length,
|
|
94
|
+
};
|
|
88
95
|
},
|
|
89
96
|
});
|
|
@@ -81,7 +81,8 @@ const generateDesignAgentParameters = {
|
|
|
81
81
|
type: "string",
|
|
82
82
|
description:
|
|
83
83
|
"JSON array of files to save. Pass one compact, complete, renderable index.html first, e.g. " +
|
|
84
|
-
'[{"filename":"index.html","fileType":"html","content":"<!doctype html>..."}].'
|
|
84
|
+
'[{"filename":"index.html","fileType":"html","content":"<!doctype html>..."}]. ' +
|
|
85
|
+
"Do not use generate-design to replace a selected variant screen after a variant pick; snapshot that fileId and use edit-design instead.",
|
|
85
86
|
},
|
|
86
87
|
designSystemId: {
|
|
87
88
|
type: ["string", "null"],
|
|
@@ -117,6 +118,9 @@ const generateDesignAction = defineAction({
|
|
|
117
118
|
"version then refine individual files with `edit-design` (search/replace) rather " +
|
|
118
119
|
"than resending a big multi-file payload — a single oversized payload can get cut " +
|
|
119
120
|
"off mid-stream and stall the turn. " +
|
|
121
|
+
"Do not use this action to replace a selected variant screen after a " +
|
|
122
|
+
"variant pick; call `get-design-snapshot` for the selected `fileId` and " +
|
|
123
|
+
"`edit-design` that same `fileId` instead. " +
|
|
120
124
|
"Do not report a design as ready until this action succeeds. " +
|
|
121
125
|
"When adding multiple screens or states, pass canvasFrames with filenames " +
|
|
122
126
|
"and x/y/width/height so the new screens appear placed on the overview canvas.",
|
|
@@ -41,16 +41,19 @@ const FALLBACK_INSTRUCTIONS =
|
|
|
41
41
|
"overview board. The chat shows one button per screen. Ask the user to pick " +
|
|
42
42
|
"a screen by name if the inline buttons are not available; after they pick, " +
|
|
43
43
|
"delete the other variant screens, call get-design-snapshot with fileId for " +
|
|
44
|
-
"the kept screen,
|
|
44
|
+
"the kept screen, then call edit-design on that same fileId in a bounded pass. " +
|
|
45
|
+
'Use mode "replace-file" when expanding the representative placeholder. ' +
|
|
46
|
+
"Do not call generate-design after a variant pick.";
|
|
45
47
|
|
|
46
48
|
const VARIANT_PICK_SUBMIT_MESSAGE =
|
|
47
49
|
"Use this design direction. Delete the other variant screens, call " +
|
|
48
50
|
"get-design-snapshot with the selected option's fileId (or filename if " +
|
|
49
|
-
"needed) for the kept file, then
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"needed) for the kept file, then call edit-design with that same fileId in " +
|
|
52
|
+
'a bounded single-file pass. Use mode "replace-file" when replacing the ' +
|
|
53
|
+
"representative placeholder with the full chosen direction, or search/replace " +
|
|
54
|
+
"for smaller refinements. Do not call generate-design after a variant pick, " +
|
|
55
|
+
"do not create index.html, and do not resend a huge payload. Stop after the " +
|
|
56
|
+
"first successful edit-design save.";
|
|
54
57
|
|
|
55
58
|
const variantSchema = z.object({
|
|
56
59
|
id: z.string().min(1).describe("Stable variant id, e.g. 'minimal-focus'"),
|
|
@@ -421,9 +424,11 @@ export default defineAction({
|
|
|
421
424
|
"exploration before follow-up refinement. After the user's choice, keep " +
|
|
422
425
|
"the chosen screen, delete the other generated variant screens, and " +
|
|
423
426
|
"call get-design-snapshot with fileId for the kept screen before " +
|
|
424
|
-
"
|
|
425
|
-
"
|
|
426
|
-
"
|
|
427
|
+
"calling edit-design on that same fileId in a bounded pass. Use " +
|
|
428
|
+
'`mode: "replace-file"` when expanding the representative placeholder ' +
|
|
429
|
+
"into the full chosen direction. Do not call generate-design after a " +
|
|
430
|
+
"variant pick. Stop after the first successful edit-design save. For " +
|
|
431
|
+
"complex apps, " +
|
|
427
432
|
"make each variant a " +
|
|
428
433
|
"compact representative screen; pass concise labels/descriptions/features " +
|
|
429
434
|
"and omit content when full HTML would be too large. Design will render " +
|
|
@@ -577,7 +582,7 @@ export default defineAction({
|
|
|
577
582
|
await writeAppStateForCurrentTab("guided-questions", {
|
|
578
583
|
title: prompt ?? "Pick a direction",
|
|
579
584
|
description:
|
|
580
|
-
"All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and
|
|
585
|
+
"All options are on the board. Choose one to keep; I will delete the others, read only the kept screen, and edit that same file in a bounded pass.",
|
|
581
586
|
submitLabel: "Use selected direction",
|
|
582
587
|
submitMessage: VARIANT_PICK_SUBMIT_MESSAGE,
|
|
583
588
|
skipLabel: "Show another set",
|
|
@@ -605,7 +610,7 @@ export default defineAction({
|
|
|
605
610
|
value:
|
|
606
611
|
`Keep "${screen.label}" (${screen.filename}, file id ${screen.id}) ` +
|
|
607
612
|
`from variant set ${variantSetId}. Delete the other variant screens: ${otherScreens}. ` +
|
|
608
|
-
`Then call get-design-snapshot with designId ${designId} and fileId ${screen.id} (filename ${screen.filename})
|
|
613
|
+
`Then call get-design-snapshot with designId ${designId} and fileId ${screen.id} (filename ${screen.filename}), then call edit-design with fileId ${screen.id} on that same kept file in a bounded single-file pass. Use mode "replace-file" when replacing the representative placeholder with the full chosen direction, or search/replace for smaller refinements. Do not call generate-design after this variant pick, do not create index.html, and do not resend a huge payload. Stop after the first successful edit-design save.`,
|
|
609
614
|
};
|
|
610
615
|
}),
|
|
611
616
|
},
|
|
@@ -623,7 +628,7 @@ export default defineAction({
|
|
|
623
628
|
embed: true,
|
|
624
629
|
fallbackInstructions: FALLBACK_INSTRUCTIONS,
|
|
625
630
|
nextRequiredAction:
|
|
626
|
-
|
|
631
|
+
'Wait for the user to pick a screen in chat. Then delete the unchosen variant screens with delete-file, call get-design-snapshot with fileId for the chosen screen, and call edit-design with that same fileId in a bounded pass. Use mode "replace-file" when expanding the placeholder into the full chosen direction. Do not call generate-design after a variant pick. Stop after the first successful edit-design save.',
|
|
627
632
|
};
|
|
628
633
|
},
|
|
629
634
|
link: ({ result }) => {
|
|
@@ -4,12 +4,24 @@ import {
|
|
|
4
4
|
applyText,
|
|
5
5
|
seedFromText,
|
|
6
6
|
} from "@agent-native/core/collab";
|
|
7
|
+
import { isPostgres } from "@agent-native/core/db";
|
|
7
8
|
import { accessFilter, assertAccess } from "@agent-native/core/sharing";
|
|
8
|
-
import { and, eq } from "drizzle-orm";
|
|
9
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
|
|
11
12
|
import { getDb, schema } from "../server/db/index.js";
|
|
12
13
|
|
|
14
|
+
function rowsAffected(result: unknown): number | undefined {
|
|
15
|
+
const candidate = result as {
|
|
16
|
+
rowsAffected?: unknown;
|
|
17
|
+
rowCount?: unknown;
|
|
18
|
+
changes?: unknown;
|
|
19
|
+
} | null;
|
|
20
|
+
const value =
|
|
21
|
+
candidate?.rowsAffected ?? candidate?.rowCount ?? candidate?.changes;
|
|
22
|
+
return typeof value === "number" ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export default defineAction({
|
|
14
26
|
description:
|
|
15
27
|
"Update an existing file in a design project. " +
|
|
@@ -70,13 +82,19 @@ export default defineAction({
|
|
|
70
82
|
|
|
71
83
|
await assertAccess("design", file.designId, "editor");
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
const updates: Record<string, unknown> = { updatedAt: now };
|
|
86
|
+
if (content !== undefined) updates.content = content;
|
|
87
|
+
if (filename !== undefined) updates.filename = filename;
|
|
88
|
+
if (fileType !== undefined) updates.fileType = fileType;
|
|
89
|
+
|
|
90
|
+
if (filename !== undefined && isPostgres()) {
|
|
91
|
+
await db.transaction(async (tx) => {
|
|
92
|
+
// Postgres evaluates concurrent NOT EXISTS updates under MVCC, so a
|
|
93
|
+
// guarded UPDATE alone can still race. Serialize design-file renames in
|
|
94
|
+
// this rare path without using SQLite's fragile async savepoint wrapper.
|
|
95
|
+
await (
|
|
96
|
+
tx as unknown as { execute: (query: unknown) => Promise<unknown> }
|
|
97
|
+
).execute(sql`LOCK TABLE design_files IN SHARE ROW EXCLUSIVE MODE`);
|
|
80
98
|
const [collision] = await tx
|
|
81
99
|
.select({ id: schema.designFiles.id })
|
|
82
100
|
.from(schema.designFiles)
|
|
@@ -92,18 +110,51 @@ export default defineAction({
|
|
|
92
110
|
`File "${filename}" already exists in design ${file.designId}`,
|
|
93
111
|
);
|
|
94
112
|
}
|
|
95
|
-
|
|
113
|
+
await tx
|
|
114
|
+
.update(schema.designFiles)
|
|
115
|
+
.set(updates)
|
|
116
|
+
.where(eq(schema.designFiles.id, id));
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
// Reject colliding SQLite renames as part of the write. SQLite's local
|
|
120
|
+
// async transaction wrapper can fail under concurrent editor/collab writes,
|
|
121
|
+
// so keep this to one guarded UPDATE instead of a SELECT-then-UPDATE window.
|
|
122
|
+
const updateWhere =
|
|
123
|
+
filename === undefined
|
|
124
|
+
? eq(schema.designFiles.id, id)
|
|
125
|
+
: and(
|
|
126
|
+
eq(schema.designFiles.id, id),
|
|
127
|
+
sql`NOT EXISTS (
|
|
128
|
+
SELECT 1 FROM design_files AS sibling
|
|
129
|
+
WHERE sibling.design_id = ${file.designId}
|
|
130
|
+
AND sibling.filename = ${filename}
|
|
131
|
+
AND sibling.id <> ${id}
|
|
132
|
+
)`,
|
|
133
|
+
);
|
|
96
134
|
|
|
97
|
-
const
|
|
98
|
-
if (content !== undefined) updates.content = content;
|
|
99
|
-
if (filename !== undefined) updates.filename = filename;
|
|
100
|
-
if (fileType !== undefined) updates.fileType = fileType;
|
|
101
|
-
|
|
102
|
-
await tx
|
|
135
|
+
const updateResult = await db
|
|
103
136
|
.update(schema.designFiles)
|
|
104
137
|
.set(updates)
|
|
105
|
-
.where(
|
|
106
|
-
|
|
138
|
+
.where(updateWhere);
|
|
139
|
+
|
|
140
|
+
if (filename !== undefined && rowsAffected(updateResult) === 0) {
|
|
141
|
+
const [collision] = await db
|
|
142
|
+
.select({ id: schema.designFiles.id })
|
|
143
|
+
.from(schema.designFiles)
|
|
144
|
+
.where(
|
|
145
|
+
and(
|
|
146
|
+
eq(schema.designFiles.designId, file.designId),
|
|
147
|
+
eq(schema.designFiles.filename, filename),
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
.limit(1);
|
|
151
|
+
if (collision && collision.id !== id) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
`File "${filename}" already exists in design ${file.designId}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
107
158
|
|
|
108
159
|
// Push content through the collab layer so live editors see the change
|
|
109
160
|
if (content !== undefined && syncCollab) {
|
|
@@ -1811,14 +1811,15 @@ export function DesignCanvas({
|
|
|
1811
1811
|
: "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-same-origin"
|
|
1812
1812
|
}
|
|
1813
1813
|
data-design-preview-iframe
|
|
1814
|
-
data-screen-iframe-id={
|
|
1814
|
+
data-screen-iframe-id={
|
|
1815
|
+
boardSurface ? undefined : (screenId ?? undefined)
|
|
1816
|
+
}
|
|
1815
1817
|
data-design-source-type={
|
|
1816
1818
|
sourceType ??
|
|
1817
1819
|
(externalPreviewUrl
|
|
1818
1820
|
? "localhost" // inferred — content is a URL
|
|
1819
1821
|
: "inline")
|
|
1820
1822
|
}
|
|
1821
|
-
allowTransparency={transparentBackground || undefined}
|
|
1822
1823
|
className="block h-full w-full border-0 bg-transparent"
|
|
1823
1824
|
style={{
|
|
1824
1825
|
background: iframeBackgroundColor,
|
|
@@ -138,7 +138,7 @@ import type { StatesPanelProps } from "./StatesPanel";
|
|
|
138
138
|
import { TweaksPanelContent } from "./TweaksPanel";
|
|
139
139
|
import type { ElementInfo } from "./types";
|
|
140
140
|
|
|
141
|
-
export type InspectorTab = "design" | "tweaks";
|
|
141
|
+
export type InspectorTab = "design" | "tweaks" | "extensions";
|
|
142
142
|
|
|
143
143
|
const MIXED_VALUE = "Mixed";
|
|
144
144
|
|
|
@@ -216,6 +216,7 @@ interface EditPanelProps {
|
|
|
216
216
|
tweakValues?: Record<string, string | number | boolean>;
|
|
217
217
|
onTweakChange?: (id: string, value: string | number | boolean) => void;
|
|
218
218
|
onRequestTweaks?: (anchor: HTMLElement) => void;
|
|
219
|
+
extensionsPanel?: ReactNode;
|
|
219
220
|
onStyleChange: (property: string, value: string) => void;
|
|
220
221
|
onStylesChange?: (styles: Record<string, string>) => void;
|
|
221
222
|
onExport?: (settings: ExportSettingsValue[]) => void;
|
|
@@ -283,6 +284,8 @@ interface EditPanelProps {
|
|
|
283
284
|
defaultComponentName?: string;
|
|
284
285
|
/** Code-inspection data for the "Inspect code" popover. */
|
|
285
286
|
inspectCode?: InspectCodeData;
|
|
287
|
+
/** Optional compact AI edit controls for selected/local source elements. */
|
|
288
|
+
aiActions?: ReactNode;
|
|
286
289
|
}
|
|
287
290
|
|
|
288
291
|
/**
|
|
@@ -2960,10 +2963,12 @@ function InspectorTabsHeader({
|
|
|
2960
2963
|
activeTab,
|
|
2961
2964
|
onActiveTabChange,
|
|
2962
2965
|
trailing,
|
|
2966
|
+
showExtensions,
|
|
2963
2967
|
}: {
|
|
2964
2968
|
activeTab: InspectorTab;
|
|
2965
2969
|
onActiveTabChange: (tab: InspectorTab) => void;
|
|
2966
2970
|
trailing?: ReactNode;
|
|
2971
|
+
showExtensions?: boolean;
|
|
2967
2972
|
}) {
|
|
2968
2973
|
const t = useT();
|
|
2969
2974
|
|
|
@@ -2986,6 +2991,14 @@ function InspectorTabsHeader({
|
|
|
2986
2991
|
>
|
|
2987
2992
|
{t("designEditor.tweaks")}
|
|
2988
2993
|
</TabsTrigger>
|
|
2994
|
+
{showExtensions ? (
|
|
2995
|
+
<TabsTrigger
|
|
2996
|
+
value="extensions"
|
|
2997
|
+
className="h-6 rounded-md px-1.5 !text-[11px] font-semibold text-muted-foreground shadow-none transition-colors hover:text-foreground data-[state=active]:bg-[var(--design-editor-panel-raised-bg)] data-[state=active]:text-foreground data-[state=active]:shadow-none"
|
|
2998
|
+
>
|
|
2999
|
+
{"Extensions" /* i18n-ignore design inspector tab */}
|
|
3000
|
+
</TabsTrigger>
|
|
3001
|
+
) : null}
|
|
2989
3002
|
</TabsList>
|
|
2990
3003
|
</Tabs>
|
|
2991
3004
|
{trailing ? <div className="shrink-0">{trailing}</div> : null}
|
|
@@ -3282,7 +3295,7 @@ function CornerRadiusControl({
|
|
|
3282
3295
|
value={radius}
|
|
3283
3296
|
onChange={commitRadius}
|
|
3284
3297
|
min={0}
|
|
3285
|
-
precision={
|
|
3298
|
+
precision={0}
|
|
3286
3299
|
/>
|
|
3287
3300
|
<Tooltip>
|
|
3288
3301
|
<TooltipTrigger asChild>
|
|
@@ -3308,6 +3321,7 @@ function CornerRadiusControl({
|
|
|
3308
3321
|
<>
|
|
3309
3322
|
<AppearanceScrubField
|
|
3310
3323
|
label={t("editPanel.labels.topLeft")}
|
|
3324
|
+
ariaLabel="Top left"
|
|
3311
3325
|
icon={IconRadiusTopLeft}
|
|
3312
3326
|
value={corners.topLeft}
|
|
3313
3327
|
onChange={(value) =>
|
|
@@ -3321,6 +3335,7 @@ function CornerRadiusControl({
|
|
|
3321
3335
|
/>
|
|
3322
3336
|
<AppearanceScrubField
|
|
3323
3337
|
label={t("editPanel.labels.topRight")}
|
|
3338
|
+
ariaLabel="Top right"
|
|
3324
3339
|
icon={IconRadiusTopRight}
|
|
3325
3340
|
value={corners.topRight}
|
|
3326
3341
|
onChange={(value) =>
|
|
@@ -3335,6 +3350,7 @@ function CornerRadiusControl({
|
|
|
3335
3350
|
<span aria-hidden="true" />
|
|
3336
3351
|
<AppearanceScrubField
|
|
3337
3352
|
label={t("editPanel.labels.bottomLeft")}
|
|
3353
|
+
ariaLabel="Bottom left"
|
|
3338
3354
|
icon={IconRadiusBottomLeft}
|
|
3339
3355
|
value={corners.bottomLeft}
|
|
3340
3356
|
onChange={(value) =>
|
|
@@ -3348,6 +3364,7 @@ function CornerRadiusControl({
|
|
|
3348
3364
|
/>
|
|
3349
3365
|
<AppearanceScrubField
|
|
3350
3366
|
label={t("editPanel.labels.bottomRight")}
|
|
3367
|
+
ariaLabel="Bottom right"
|
|
3351
3368
|
icon={IconRadiusBottomRight}
|
|
3352
3369
|
value={corners.bottomRight}
|
|
3353
3370
|
onChange={(value) =>
|
|
@@ -3368,6 +3385,7 @@ function CornerRadiusControl({
|
|
|
3368
3385
|
|
|
3369
3386
|
function AppearanceScrubField({
|
|
3370
3387
|
label,
|
|
3388
|
+
ariaLabel,
|
|
3371
3389
|
icon,
|
|
3372
3390
|
value,
|
|
3373
3391
|
onChange,
|
|
@@ -3379,6 +3397,7 @@ function AppearanceScrubField({
|
|
|
3379
3397
|
precision,
|
|
3380
3398
|
}: {
|
|
3381
3399
|
label: string;
|
|
3400
|
+
ariaLabel?: string;
|
|
3382
3401
|
icon: (props: { className?: string }) => ReactNode;
|
|
3383
3402
|
value: number;
|
|
3384
3403
|
onChange: (value: number) => void;
|
|
@@ -3392,7 +3411,7 @@ function AppearanceScrubField({
|
|
|
3392
3411
|
return (
|
|
3393
3412
|
<ScrubInput
|
|
3394
3413
|
label={label}
|
|
3395
|
-
ariaLabel={label}
|
|
3414
|
+
ariaLabel={ariaLabel ?? label}
|
|
3396
3415
|
icon={icon}
|
|
3397
3416
|
value={value}
|
|
3398
3417
|
onChange={onChange}
|
|
@@ -6842,6 +6861,7 @@ export function EditPanel({
|
|
|
6842
6861
|
tweakValues = {},
|
|
6843
6862
|
onTweakChange,
|
|
6844
6863
|
onRequestTweaks,
|
|
6864
|
+
extensionsPanel,
|
|
6845
6865
|
onStyleChange,
|
|
6846
6866
|
onStylesChange,
|
|
6847
6867
|
onExport,
|
|
@@ -6858,6 +6878,7 @@ export function EditPanel({
|
|
|
6858
6878
|
selectedElementAlreadyComponent = false,
|
|
6859
6879
|
defaultComponentName = "Component",
|
|
6860
6880
|
inspectCode,
|
|
6881
|
+
aiActions,
|
|
6861
6882
|
}: EditPanelProps) {
|
|
6862
6883
|
const t = useT();
|
|
6863
6884
|
const [createComponentOpen, setCreateComponentOpen] = useState(false);
|
|
@@ -6950,6 +6971,7 @@ export function EditPanel({
|
|
|
6950
6971
|
activeTab={activeTab}
|
|
6951
6972
|
onActiveTabChange={handleActiveTabChange}
|
|
6952
6973
|
trailing={headerTrailing}
|
|
6974
|
+
showExtensions={!!extensionsPanel}
|
|
6953
6975
|
/>
|
|
6954
6976
|
|
|
6955
6977
|
{activeTab === "design" ? (
|
|
@@ -7058,6 +7080,12 @@ export function EditPanel({
|
|
|
7058
7080
|
/>
|
|
7059
7081
|
)}
|
|
7060
7082
|
|
|
7083
|
+
{aiActions ? (
|
|
7084
|
+
<div className="border-b border-[var(--design-editor-control-border)] px-2 py-1.5">
|
|
7085
|
+
{aiActions}
|
|
7086
|
+
</div>
|
|
7087
|
+
) : null}
|
|
7088
|
+
|
|
7061
7089
|
{!inspectorElement && (
|
|
7062
7090
|
<PageProperties
|
|
7063
7091
|
styles={pageStyles}
|
|
@@ -7228,6 +7256,10 @@ export function EditPanel({
|
|
|
7228
7256
|
/>
|
|
7229
7257
|
</div>
|
|
7230
7258
|
</div>
|
|
7259
|
+
) : activeTab === "extensions" && extensionsPanel ? (
|
|
7260
|
+
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
|
7261
|
+
{extensionsPanel}
|
|
7262
|
+
</div>
|
|
7231
7263
|
) : null}
|
|
7232
7264
|
</div>
|
|
7233
7265
|
);
|