@agent-native/core 0.84.30 → 0.84.32
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 +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/session-replay.ts +5 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +13 -1
- package/corpus/templates/analytics/changelog/2026-07-01-session-replays-keep-inlined-css-without-live-resource-loa.md +6 -0
- package/corpus/templates/design/AGENTS.md +18 -0
- package/corpus/templates/design/actions/apply-source-edit.ts +87 -0
- package/corpus/templates/design/actions/list-source-files.ts +52 -0
- package/corpus/templates/design/actions/navigate.ts +3 -2
- package/corpus/templates/design/actions/preview-source-edit.ts +85 -0
- package/corpus/templates/design/actions/read-source-file.ts +55 -0
- package/corpus/templates/design/actions/resolve-selection-source.ts +101 -0
- package/corpus/templates/design/actions/view-screen.ts +43 -1
- package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +630 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +9 -6
- package/corpus/templates/design/app/pages/DesignEditor.tsx +144 -9
- package/corpus/templates/design/changelog/2026-07-01-design-code-workspace.md +6 -0
- package/corpus/templates/design/server/source-workspace.ts +215 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +5 -5
- package/corpus/templates/design/shared/source-workspace.ts +149 -0
- package/dist/client/session-replay.d.ts +1 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +2 -0
- package/dist/client/session-replay.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.32
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- da8ac0a: Keep tail-resume reconnect content display-only, recover zero-byte action-prep stalls, and polish Design screen tool labels.
|
|
8
|
+
|
|
9
|
+
## 0.84.31
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 3190dea: Inline rrweb stylesheet snapshots by default so Analytics session replay captures can play back styled pages without live CSS fetches.
|
|
14
|
+
|
|
3
15
|
## 0.84.30
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.32",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -19,6 +19,7 @@ interface RrwebRecordOptions {
|
|
|
19
19
|
emit: (event: ReplayEvent) => void;
|
|
20
20
|
checkoutEveryNth?: number;
|
|
21
21
|
checkoutEveryNms?: number;
|
|
22
|
+
inlineStylesheet?: boolean;
|
|
22
23
|
blockClass?: string | RegExp;
|
|
23
24
|
blockSelector?: string;
|
|
24
25
|
ignoreClass?: string | RegExp;
|
|
@@ -82,6 +83,7 @@ export interface SessionReplayOptions {
|
|
|
82
83
|
maxBatchBytes?: number;
|
|
83
84
|
checkoutEveryNth?: number;
|
|
84
85
|
checkoutEveryNms?: number;
|
|
86
|
+
inlineStylesheet?: boolean;
|
|
85
87
|
blockSelector?: string;
|
|
86
88
|
ignoreSelector?: string;
|
|
87
89
|
maskTextClass?: string | RegExp;
|
|
@@ -135,6 +137,7 @@ interface NormalizedSessionReplayOptions {
|
|
|
135
137
|
maxBatchBytes: number;
|
|
136
138
|
checkoutEveryNth?: number;
|
|
137
139
|
checkoutEveryNms?: number;
|
|
140
|
+
inlineStylesheet: boolean;
|
|
138
141
|
blockSelector: string;
|
|
139
142
|
ignoreSelector: string;
|
|
140
143
|
maskTextClass: string | RegExp;
|
|
@@ -509,6 +512,7 @@ function normalizeOptions(
|
|
|
509
512
|
),
|
|
510
513
|
checkoutEveryNth: options.checkoutEveryNth,
|
|
511
514
|
checkoutEveryNms: options.checkoutEveryNms,
|
|
515
|
+
inlineStylesheet: options.inlineStylesheet ?? true,
|
|
512
516
|
blockSelector: options.blockSelector || DEFAULT_BLOCK_SELECTOR,
|
|
513
517
|
ignoreSelector: options.ignoreSelector || DEFAULT_IGNORE_SELECTOR,
|
|
514
518
|
maskTextClass: options.maskTextClass || DEFAULT_MASK_TEXT_CLASS,
|
|
@@ -1010,6 +1014,7 @@ async function startSessionReplayRecorder(
|
|
|
1010
1014
|
sampling: normalized.eventSampling,
|
|
1011
1015
|
checkoutEveryNth: normalized.checkoutEveryNth,
|
|
1012
1016
|
checkoutEveryNms: normalized.checkoutEveryNms,
|
|
1017
|
+
inlineStylesheet: normalized.inlineStylesheet,
|
|
1013
1018
|
blockSelector: normalized.blockSelector,
|
|
1014
1019
|
ignoreSelector: normalized.ignoreSelector,
|
|
1015
1020
|
maskTextClass: normalized.maskTextClass,
|
|
@@ -1243,7 +1243,14 @@ function sanitizeCssText(value: string): string {
|
|
|
1243
1243
|
if (!containsStylesheetNetworkLoad(value)) return value;
|
|
1244
1244
|
return value
|
|
1245
1245
|
.replace(/@import\s+(?:url\s*\()?[^;{}]+;?/gi, "")
|
|
1246
|
-
.replace(/\burl\s*\((?:\\.|[^\\)])
|
|
1246
|
+
.replace(/\burl\s*\(\s*((?:\\.|[^\\)])*)\)/gi, sanitizeCssUrlToken);
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function sanitizeCssUrlToken(match: string, rawValue: string): string {
|
|
1250
|
+
const urlValue = rawValue.trim();
|
|
1251
|
+
const unquoted = urlValue.replace(/^(['"])(.*)\1$/, "$2").trim();
|
|
1252
|
+
if (/^(?:data:|blob:|#)/i.test(unquoted)) return match;
|
|
1253
|
+
return "none";
|
|
1247
1254
|
}
|
|
1248
1255
|
|
|
1249
1256
|
function sanitizeAttributes(attributes: AnyRecord): AnyRecord {
|
|
@@ -1258,6 +1265,11 @@ function sanitizeAttributes(attributes: AnyRecord): AnyRecord {
|
|
|
1258
1265
|
if (style.trim()) next[key] = style;
|
|
1259
1266
|
continue;
|
|
1260
1267
|
}
|
|
1268
|
+
if (normalized === "_csstext") {
|
|
1269
|
+
const cssText = sanitizeCssText(String(value));
|
|
1270
|
+
if (cssText.trim()) next[key] = cssText;
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1261
1273
|
next[key] = value;
|
|
1262
1274
|
}
|
|
1263
1275
|
return next;
|
|
@@ -191,6 +191,24 @@ patterns live in `.agents/skills/`.
|
|
|
191
191
|
mode can list and resolve local routes now, but file reads/writes remain a
|
|
192
192
|
bridge contract until explicit permission controls are hardened.
|
|
193
193
|
|
|
194
|
+
## Code Workspace
|
|
195
|
+
|
|
196
|
+
- The editor left rail has a wide `code` workspace panel. Open it with
|
|
197
|
+
`navigate --view editor --designId <id> --leftPanel code` and optionally pass
|
|
198
|
+
`fileId`, `filename`, or `screen` to focus a file.
|
|
199
|
+
- Use `list-source-files` to inspect the source workspace. For the MVP the
|
|
200
|
+
backend is `virtual-inline` over SQL-backed `design_files`, exposed as
|
|
201
|
+
`designfs://<designId>/`.
|
|
202
|
+
- Use `read-source-file` for file contents and preserve its `versionHash` before
|
|
203
|
+
writing. Do not return full file content from `view-screen`; it reports only
|
|
204
|
+
active code file metadata and dirty state.
|
|
205
|
+
- Use `preview-source-edit` to show a diff without saving, then
|
|
206
|
+
`apply-source-edit` with the prior `versionHash` to save either a full replace
|
|
207
|
+
or exact replace. These actions update the same inline file state as the UI.
|
|
208
|
+
- Use `resolve-selection-source` when the user has a canvas element selected and
|
|
209
|
+
you need the best matching inline file location/snippet. Localhost/container
|
|
210
|
+
source reads and writes remain future backend work.
|
|
211
|
+
|
|
194
212
|
## Localhost Source Actions
|
|
195
213
|
|
|
196
214
|
- `connect-localhost`: registers or refreshes a localhost source connection
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
findSourceWorkspaceFile,
|
|
6
|
+
readLiveSourceFile,
|
|
7
|
+
resolveSourceWorkspace,
|
|
8
|
+
writeInlineSourceFile,
|
|
9
|
+
} from "../server/source-workspace.js";
|
|
10
|
+
import {
|
|
11
|
+
applySourceEdit,
|
|
12
|
+
previewSourceDiff,
|
|
13
|
+
} from "../shared/source-workspace.js";
|
|
14
|
+
|
|
15
|
+
const sourceEditSchema = z.discriminatedUnion("kind", [
|
|
16
|
+
z.object({
|
|
17
|
+
kind: z.literal("full-replace"),
|
|
18
|
+
content: z.string().describe("Complete replacement file content"),
|
|
19
|
+
}),
|
|
20
|
+
z.object({
|
|
21
|
+
kind: z.literal("exact-replace"),
|
|
22
|
+
search: z.string().min(1).describe("Unique exact text to replace"),
|
|
23
|
+
replace: z.string().describe("Replacement text"),
|
|
24
|
+
}),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export default defineAction({
|
|
28
|
+
description:
|
|
29
|
+
"Apply a source-file edit through the shared Design source surface. In the " +
|
|
30
|
+
"MVP this writes inline design_files only and rejects stale version hashes.",
|
|
31
|
+
schema: z
|
|
32
|
+
.object({
|
|
33
|
+
designId: z.string().describe("Design project ID"),
|
|
34
|
+
path: z
|
|
35
|
+
.string()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("Source path/filename, such as index.html"),
|
|
38
|
+
fileId: z.string().optional().describe("Design file ID"),
|
|
39
|
+
edit: sourceEditSchema,
|
|
40
|
+
expectedVersionHash: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Hash returned by read-source-file or preview-source-edit"),
|
|
44
|
+
})
|
|
45
|
+
.refine((args) => args.path || args.fileId, {
|
|
46
|
+
message: "Provide either path or fileId.",
|
|
47
|
+
path: ["path"],
|
|
48
|
+
}),
|
|
49
|
+
run: async ({ designId, path, fileId, edit, expectedVersionHash }) => {
|
|
50
|
+
const workspace = await resolveSourceWorkspace(designId, {
|
|
51
|
+
includeContent: true,
|
|
52
|
+
});
|
|
53
|
+
if (workspace.sourceType !== "inline") {
|
|
54
|
+
throw new Error("Only inline Design files are editable in this MVP.");
|
|
55
|
+
}
|
|
56
|
+
const file = findSourceWorkspaceFile(workspace.files, { fileId, path });
|
|
57
|
+
const live = await readLiveSourceFile(file);
|
|
58
|
+
if (
|
|
59
|
+
expectedVersionHash !== undefined &&
|
|
60
|
+
expectedVersionHash !== live.versionHash
|
|
61
|
+
) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
"Source file changed since it was read. Re-read the file and retry.",
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const next = applySourceEdit(live.content, edit);
|
|
68
|
+
const write = await writeInlineSourceFile({
|
|
69
|
+
designId,
|
|
70
|
+
file,
|
|
71
|
+
content: next.content,
|
|
72
|
+
expectedVersionHash: expectedVersionHash ?? live.versionHash,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
designId,
|
|
77
|
+
path: file.filename,
|
|
78
|
+
fileId: file.id,
|
|
79
|
+
backendKind: "virtual-inline",
|
|
80
|
+
changed: write.changed,
|
|
81
|
+
editsApplied: next.editsApplied,
|
|
82
|
+
versionHash: write.versionHash,
|
|
83
|
+
updatedAt: write.updatedAt,
|
|
84
|
+
diff: previewSourceDiff(live.content, next.content),
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import { resolveSourceWorkspace } from "../server/source-workspace.js";
|
|
5
|
+
|
|
6
|
+
export default defineAction({
|
|
7
|
+
description:
|
|
8
|
+
"List source files for a Design code workspace. In the current MVP this " +
|
|
9
|
+
"returns inline SQL-backed design_files by filename; future localhost and " +
|
|
10
|
+
"container source backends will use the same shape.",
|
|
11
|
+
schema: z.object({
|
|
12
|
+
designId: z.string().describe("Design project ID"),
|
|
13
|
+
}),
|
|
14
|
+
readOnly: true,
|
|
15
|
+
http: { method: "GET" },
|
|
16
|
+
run: async ({ designId }) => {
|
|
17
|
+
const workspace = await resolveSourceWorkspace(designId);
|
|
18
|
+
const readonly = !workspace.canEdit || workspace.sourceType !== "inline";
|
|
19
|
+
return {
|
|
20
|
+
designId,
|
|
21
|
+
backend: {
|
|
22
|
+
kind: "virtual-inline" as const,
|
|
23
|
+
workspaceUri: `designfs://${designId}/`,
|
|
24
|
+
designId,
|
|
25
|
+
capabilities: {
|
|
26
|
+
readFile: true,
|
|
27
|
+
writeFile: workspace.canEdit && workspace.sourceType === "inline",
|
|
28
|
+
diff: true,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
sourceType: workspace.sourceType,
|
|
32
|
+
files: workspace.files.map((file) => ({
|
|
33
|
+
path: file.filename,
|
|
34
|
+
displayName: file.filename,
|
|
35
|
+
kind: "file" as const,
|
|
36
|
+
sourceType: workspace.sourceType,
|
|
37
|
+
fileId: file.id,
|
|
38
|
+
readonly,
|
|
39
|
+
reason: readonly
|
|
40
|
+
? workspace.canEdit
|
|
41
|
+
? "Only inline Design files are editable in this MVP."
|
|
42
|
+
: "You need editor access to change this file."
|
|
43
|
+
: undefined,
|
|
44
|
+
language:
|
|
45
|
+
file.fileType === "html" || file.fileType === "css"
|
|
46
|
+
? file.fileType
|
|
47
|
+
: undefined,
|
|
48
|
+
updatedAt: file.updatedAt,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
});
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* --designId Design ID (for editor/present views)
|
|
19
19
|
* --editorView Editor mode for designs: single or overview
|
|
20
20
|
* --inspectorTab Inspector tab for designs: design or tweaks (extensions opens Tools for compatibility)
|
|
21
|
-
* --leftPanel Left editor panel: file, agent, assets, tools, or
|
|
21
|
+
* --leftPanel Left editor panel: file, agent, assets, tools, tokens, or code
|
|
22
22
|
* --fileId Screen/file id to focus in the design editor
|
|
23
23
|
* --filename Screen filename to focus in the design editor
|
|
24
24
|
* --tool Design editor tool to activate
|
|
@@ -53,11 +53,12 @@ const designLeftPanelSchema = z.enum([
|
|
|
53
53
|
"assets",
|
|
54
54
|
"tools",
|
|
55
55
|
"tokens",
|
|
56
|
+
"code",
|
|
56
57
|
]);
|
|
57
58
|
|
|
58
59
|
export default defineAction({
|
|
59
60
|
description:
|
|
60
|
-
"Navigate the UI to a specific view or path. Views: list, editor, design-systems, present, settings. Use --designId with editor/present views and --designSystemId with design-systems. For designs, use editorView=overview to show the infinite screens canvas, or editorView=single with fileId/filename/screen to focus a screen. Use leftPanel=file|agent|assets|tools|tokens to focus the
|
|
61
|
+
"Navigate the UI to a specific view or path. Views: list, editor, design-systems, present, settings. Use --designId with editor/present views and --designSystemId with design-systems. For designs, use editorView=overview to show the infinite screens canvas, or editorView=single with fileId/filename/screen to focus a screen. Use leftPanel=file|agent|assets|tools|tokens|code to focus the left rail, including the wide Code workspace. Legacy inspectorTab=extensions opens Tools. Use tool to activate a design editor tool.",
|
|
61
62
|
schema: z
|
|
62
63
|
.object({
|
|
63
64
|
view: z
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
findSourceWorkspaceFile,
|
|
6
|
+
readLiveSourceFile,
|
|
7
|
+
resolveSourceWorkspace,
|
|
8
|
+
} from "../server/source-workspace.js";
|
|
9
|
+
import {
|
|
10
|
+
applySourceEdit,
|
|
11
|
+
previewSourceDiff,
|
|
12
|
+
} from "../shared/source-workspace.js";
|
|
13
|
+
|
|
14
|
+
const sourceEditSchema = z.discriminatedUnion("kind", [
|
|
15
|
+
z.object({
|
|
16
|
+
kind: z.literal("full-replace"),
|
|
17
|
+
content: z.string().describe("Complete replacement file content"),
|
|
18
|
+
}),
|
|
19
|
+
z.object({
|
|
20
|
+
kind: z.literal("exact-replace"),
|
|
21
|
+
search: z.string().min(1).describe("Unique exact text to replace"),
|
|
22
|
+
replace: z.string().describe("Replacement text"),
|
|
23
|
+
}),
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
export default defineAction({
|
|
27
|
+
description:
|
|
28
|
+
"Preview a source-file edit without saving it. Returns changed byte counts, " +
|
|
29
|
+
"line range, and compact before/after excerpts for Design code workspace diffs.",
|
|
30
|
+
schema: z
|
|
31
|
+
.object({
|
|
32
|
+
designId: z.string().describe("Design project ID"),
|
|
33
|
+
path: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Source path/filename, such as index.html"),
|
|
37
|
+
fileId: z.string().optional().describe("Design file ID"),
|
|
38
|
+
edit: sourceEditSchema,
|
|
39
|
+
expectedVersionHash: z
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Optional hash from read-source-file to detect stale edits"),
|
|
43
|
+
})
|
|
44
|
+
.refine((args) => args.path || args.fileId, {
|
|
45
|
+
message: "Provide either path or fileId.",
|
|
46
|
+
path: ["path"],
|
|
47
|
+
}),
|
|
48
|
+
readOnly: true,
|
|
49
|
+
run: async ({ designId, path, fileId, edit, expectedVersionHash }) => {
|
|
50
|
+
const workspace = await resolveSourceWorkspace(designId, {
|
|
51
|
+
includeContent: true,
|
|
52
|
+
});
|
|
53
|
+
const file = findSourceWorkspaceFile(workspace.files, { fileId, path });
|
|
54
|
+
const live = await readLiveSourceFile(file);
|
|
55
|
+
const stale =
|
|
56
|
+
expectedVersionHash !== undefined &&
|
|
57
|
+
expectedVersionHash !== live.versionHash;
|
|
58
|
+
if (stale) {
|
|
59
|
+
return {
|
|
60
|
+
designId,
|
|
61
|
+
path: file.filename,
|
|
62
|
+
fileId: file.id,
|
|
63
|
+
okToApply: false,
|
|
64
|
+
conflict: "stale-version",
|
|
65
|
+
currentVersionHash: live.versionHash,
|
|
66
|
+
message:
|
|
67
|
+
"Source file changed since it was read. Re-read before saving.",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const next = applySourceEdit(live.content, edit);
|
|
72
|
+
return {
|
|
73
|
+
designId,
|
|
74
|
+
path: file.filename,
|
|
75
|
+
fileId: file.id,
|
|
76
|
+
okToApply: workspace.canEdit && workspace.sourceType === "inline",
|
|
77
|
+
conflict:
|
|
78
|
+
workspace.sourceType === "inline" ? null : "unsupported-source-backend",
|
|
79
|
+
currentVersionHash: live.versionHash,
|
|
80
|
+
nextVersionHash: next.changed ? undefined : live.versionHash,
|
|
81
|
+
editsApplied: next.editsApplied,
|
|
82
|
+
diff: previewSourceDiff(live.content, next.content),
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
findSourceWorkspaceFile,
|
|
6
|
+
readLiveSourceFile,
|
|
7
|
+
resolveSourceWorkspace,
|
|
8
|
+
} from "../server/source-workspace.js";
|
|
9
|
+
|
|
10
|
+
export default defineAction({
|
|
11
|
+
description:
|
|
12
|
+
"Read one Design source file. For inline designs this returns live " +
|
|
13
|
+
"design_files content with a version hash for safe follow-up writes.",
|
|
14
|
+
schema: z
|
|
15
|
+
.object({
|
|
16
|
+
designId: z.string().describe("Design project ID"),
|
|
17
|
+
path: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Source path/filename, such as index.html"),
|
|
21
|
+
fileId: z.string().optional().describe("Design file ID"),
|
|
22
|
+
})
|
|
23
|
+
.refine((args) => args.path || args.fileId, {
|
|
24
|
+
message: "Provide either path or fileId.",
|
|
25
|
+
path: ["path"],
|
|
26
|
+
}),
|
|
27
|
+
readOnly: true,
|
|
28
|
+
http: { method: "GET" },
|
|
29
|
+
run: async ({ designId, path, fileId }) => {
|
|
30
|
+
const workspace = await resolveSourceWorkspace(designId, {
|
|
31
|
+
includeContent: true,
|
|
32
|
+
});
|
|
33
|
+
const file = findSourceWorkspaceFile(workspace.files, { fileId, path });
|
|
34
|
+
const live = await readLiveSourceFile(file);
|
|
35
|
+
return {
|
|
36
|
+
designId,
|
|
37
|
+
path: file.filename,
|
|
38
|
+
displayName: file.filename,
|
|
39
|
+
fileId: file.id,
|
|
40
|
+
sourceType: workspace.sourceType,
|
|
41
|
+
backendKind: "virtual-inline",
|
|
42
|
+
readonly: !workspace.canEdit || workspace.sourceType !== "inline",
|
|
43
|
+
language: live.language,
|
|
44
|
+
content: live.content,
|
|
45
|
+
versionHash: live.versionHash,
|
|
46
|
+
updatedAt: file.updatedAt,
|
|
47
|
+
provenance: {
|
|
48
|
+
kind: "design-file",
|
|
49
|
+
designId,
|
|
50
|
+
fileId: file.id,
|
|
51
|
+
filename: file.filename,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
findSourceWorkspaceFile,
|
|
6
|
+
readLiveSourceFile,
|
|
7
|
+
resolveSourceWorkspace,
|
|
8
|
+
} from "../server/source-workspace.js";
|
|
9
|
+
import { buildCodeLayerProjection } from "../shared/code-layer.js";
|
|
10
|
+
|
|
11
|
+
function offsetToLineColumn(content: string, offset: number) {
|
|
12
|
+
let line = 1;
|
|
13
|
+
let column = 1;
|
|
14
|
+
for (let index = 0; index < offset; index += 1) {
|
|
15
|
+
if (content.charCodeAt(index) === 10) {
|
|
16
|
+
line += 1;
|
|
17
|
+
column = 1;
|
|
18
|
+
} else {
|
|
19
|
+
column += 1;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { line, column };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default defineAction({
|
|
26
|
+
description:
|
|
27
|
+
"Resolve a selected Design canvas node to the best source file location. " +
|
|
28
|
+
"Inline designs resolve to the containing design file and, when possible, " +
|
|
29
|
+
"a line/column/snippet from the code-layer projection.",
|
|
30
|
+
schema: z
|
|
31
|
+
.object({
|
|
32
|
+
designId: z.string().describe("Design project ID"),
|
|
33
|
+
path: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Source path/filename, such as index.html"),
|
|
37
|
+
fileId: z.string().optional().describe("Design file ID"),
|
|
38
|
+
nodeId: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("data-agent-native-node-id or code-layer node id"),
|
|
42
|
+
selector: z.string().optional().describe("CSS selector fallback"),
|
|
43
|
+
})
|
|
44
|
+
.refine((args) => args.path || args.fileId, {
|
|
45
|
+
message: "Provide either path or fileId.",
|
|
46
|
+
path: ["path"],
|
|
47
|
+
}),
|
|
48
|
+
readOnly: true,
|
|
49
|
+
http: { method: "GET" },
|
|
50
|
+
run: async ({ designId, path, fileId, nodeId, selector }) => {
|
|
51
|
+
const workspace = await resolveSourceWorkspace(designId, {
|
|
52
|
+
includeContent: true,
|
|
53
|
+
});
|
|
54
|
+
const file = findSourceWorkspaceFile(workspace.files, { fileId, path });
|
|
55
|
+
const live = await readLiveSourceFile(file);
|
|
56
|
+
const projection = buildCodeLayerProjection(live.content, {
|
|
57
|
+
source: {
|
|
58
|
+
kind: "design-file",
|
|
59
|
+
designId,
|
|
60
|
+
fileId: file.id,
|
|
61
|
+
filename: file.filename,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
const node =
|
|
65
|
+
projection.nodes.find(
|
|
66
|
+
(candidate) =>
|
|
67
|
+
candidate.id === nodeId ||
|
|
68
|
+
candidate.dataAttributes["data-agent-native-node-id"] === nodeId ||
|
|
69
|
+
candidate.dataAttributes["data-code-layer-id"] === nodeId,
|
|
70
|
+
) ??
|
|
71
|
+
projection.nodes.find((candidate) =>
|
|
72
|
+
selector
|
|
73
|
+
? candidate.selector === selector ||
|
|
74
|
+
candidate.path === selector ||
|
|
75
|
+
candidate.selectors.includes(selector)
|
|
76
|
+
: false,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const start = node?.source?.openStart ?? node?.source?.start;
|
|
80
|
+
const location =
|
|
81
|
+
typeof start === "number"
|
|
82
|
+
? offsetToLineColumn(live.content, start)
|
|
83
|
+
: null;
|
|
84
|
+
const snippet =
|
|
85
|
+
node?.source && typeof node.source.start === "number"
|
|
86
|
+
? live.content.slice(node.source.start, node.source.end).slice(0, 1200)
|
|
87
|
+
: undefined;
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
designId,
|
|
91
|
+
sourceType: workspace.sourceType,
|
|
92
|
+
backendKind: "virtual-inline",
|
|
93
|
+
path: file.filename,
|
|
94
|
+
fileId: file.id,
|
|
95
|
+
line: location?.line ?? null,
|
|
96
|
+
column: location?.column ?? null,
|
|
97
|
+
snippet,
|
|
98
|
+
resolved: Boolean(node),
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
});
|
|
@@ -34,6 +34,20 @@ function stringArrayProp(value: unknown, key: string): string[] {
|
|
|
34
34
|
: [];
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function boolProp(value: unknown, key: string): boolean | undefined {
|
|
38
|
+
if (!value || typeof value !== "object") return undefined;
|
|
39
|
+
const candidate = (value as Record<string, unknown>)[key];
|
|
40
|
+
return typeof candidate === "boolean" ? candidate : undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function objectProp(value: unknown, key: string): Record<string, unknown> {
|
|
44
|
+
if (!value || typeof value !== "object") return {};
|
|
45
|
+
const candidate = (value as Record<string, unknown>)[key];
|
|
46
|
+
return candidate && typeof candidate === "object" && !Array.isArray(candidate)
|
|
47
|
+
? (candidate as Record<string, unknown>)
|
|
48
|
+
: {};
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
function resolveActiveScreen(
|
|
38
52
|
files: Array<{
|
|
39
53
|
id: string;
|
|
@@ -93,9 +107,36 @@ function resolveActiveScreen(
|
|
|
93
107
|
return null;
|
|
94
108
|
}
|
|
95
109
|
|
|
110
|
+
function resolveActiveCodeFile(
|
|
111
|
+
files: Array<{
|
|
112
|
+
id: string;
|
|
113
|
+
filename: string;
|
|
114
|
+
fileType: string | null;
|
|
115
|
+
updatedAt: string | null;
|
|
116
|
+
}>,
|
|
117
|
+
designSelection: unknown,
|
|
118
|
+
) {
|
|
119
|
+
const codeWorkspace = objectProp(designSelection, "codeWorkspace");
|
|
120
|
+
if (Object.keys(codeWorkspace).length === 0) return null;
|
|
121
|
+
const fileId = stringProp(codeWorkspace, "activeFileId");
|
|
122
|
+
const path = stringProp(codeWorkspace, "activePath");
|
|
123
|
+
const file = files.find(
|
|
124
|
+
(candidate) => candidate.id === fileId || candidate.filename === path,
|
|
125
|
+
);
|
|
126
|
+
return {
|
|
127
|
+
open: boolProp(codeWorkspace, "open") ?? false,
|
|
128
|
+
backendKind: stringProp(codeWorkspace, "backendKind") ?? "virtual-inline",
|
|
129
|
+
path: path ?? file?.filename ?? null,
|
|
130
|
+
fileId: fileId ?? file?.id ?? null,
|
|
131
|
+
dirty: boolProp(codeWorkspace, "dirty") ?? false,
|
|
132
|
+
versionHash: stringProp(codeWorkspace, "versionHash") ?? null,
|
|
133
|
+
file: file ?? null,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
96
137
|
export default defineAction({
|
|
97
138
|
description:
|
|
98
|
-
"See what the user is currently looking at on screen. Returns the current navigation state including which design is open, which view they are on (list, editor, design-systems, present, settings), active/focused design screen, selected element, active inspector tab (design or tweaks), active left rail panel (file, agent, assets, tools, or
|
|
139
|
+
"See what the user is currently looking at on screen. Returns the current navigation state including which design is open, which view they are on (list, editor, design-systems, present, settings), active/focused design screen, selected element, active inspector tab (design or tweaks), active left rail panel (file, agent, assets, tools, tokens, or code), active code file metadata, overview canvas state, plus any pending question overlay. Always call this first before taking any action.",
|
|
99
140
|
schema: z.object({}),
|
|
100
141
|
http: false,
|
|
101
142
|
run: async () => {
|
|
@@ -154,6 +195,7 @@ export default defineAction({
|
|
|
154
195
|
title: (access.resource as { title?: unknown }).title ?? null,
|
|
155
196
|
screens: files,
|
|
156
197
|
activeScreen: resolveActiveScreen(files, navigation, designSelection),
|
|
198
|
+
activeCodeFile: resolveActiveCodeFile(files, designSelection),
|
|
157
199
|
canvasFrames: parseCanvasFrameGeometryById(data.canvasFrames),
|
|
158
200
|
};
|
|
159
201
|
}
|