@lazyingart/agintiflow 0.20.183 → 0.20.185
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/docs/auxiliary-image-generation.md +32 -0
- package/package.json +1 -1
- package/public/app.js +394 -0
- package/public/index.html +33 -0
- package/public/styles.css +197 -2
- package/references/agintiflow-npm-publication-memory.md +12 -0
- package/references/agintiflow-paper-ui-generalization-plan-2026-05-31.md +283 -0
- package/scripts/smoke-auxiliary-tools.js +22 -0
- package/scripts/smoke-web-api.js +90 -1
- package/scripts/smoke-web-ui.js +55 -1
- package/src/agent-runner.js +1 -1
- package/src/auxiliary-tools.js +61 -6
- package/src/model-client.js +7 -2
- package/src/task-profiles.js +1 -1
- package/web.js +332 -0
|
@@ -7,6 +7,10 @@ AgInTiFlow separates **skills** from **tools**:
|
|
|
7
7
|
|
|
8
8
|
The optional image-generation skills are `image_generation` and `venice_image_generation`. Both use the deterministic `generate_image` tool.
|
|
9
9
|
|
|
10
|
+
`generate_image` is a raster-image tool. It does not produce true SVG/vector output. If a caller requests `svg`, the tool records
|
|
11
|
+
`requestedFormat: "svg"`, generates a PNG fallback with `actualFormat: "png"`, and returns a `formatNotice`. If the task truly requires
|
|
12
|
+
editable vectors, use AgInTiFlow file tools to write deterministic SVG/LaTeX/HTML instead of calling `generate_image`.
|
|
13
|
+
|
|
10
14
|
## Setup
|
|
11
15
|
|
|
12
16
|
Store a GRS AI or Venice key project-locally:
|
|
@@ -38,11 +42,39 @@ For image, cover, poster, illustration, photo, or logo-concept requests, the mod
|
|
|
38
42
|
"model": "nano-banana-2",
|
|
39
43
|
"outputDir": "artifacts/images/robot-cover",
|
|
40
44
|
"outputStem": "robot-cover",
|
|
45
|
+
"format": "png",
|
|
41
46
|
"aspectRatio": "1:1",
|
|
42
47
|
"imageSize": "2K"
|
|
43
48
|
}
|
|
44
49
|
```
|
|
45
50
|
|
|
51
|
+
For trusted local apps that need image generation without routing through a language-model run, the web server exposes a deterministic endpoint:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
curl -sS http://127.0.0.1:3210/api/auxiliary/generate-image \
|
|
55
|
+
-H 'Content-Type: application/json' \
|
|
56
|
+
-d '{
|
|
57
|
+
"prompt": "A polished cyan robot painting a circuit-board river",
|
|
58
|
+
"provider": "venice",
|
|
59
|
+
"format": "svg",
|
|
60
|
+
"outputDir": "artifacts/images/robot-cover",
|
|
61
|
+
"outputStem": "robot-cover"
|
|
62
|
+
}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This endpoint is intended for trusted local/server-side callers. Browser apps on another origin should use an explicit adapter/proxy
|
|
66
|
+
rather than relying on permissive CORS, because generation can consume API keys and write workspace artifacts.
|
|
67
|
+
|
|
68
|
+
That SVG request returns a PNG fallback contract rather than pretending SVG was generated:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"requestedFormat": "svg",
|
|
73
|
+
"actualFormat": "png",
|
|
74
|
+
"formatNotice": "SVG/vector output is not supported by generate_image. A raster PNG is generated instead."
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
46
78
|
With GRS AI, the tool uses the Nano Banana API:
|
|
47
79
|
|
|
48
80
|
- `POST https://grsaiapi.com/v1/draw/nano-banana`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyingart/agintiflow",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.185",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgInTiFlow is a project-aware agent workspace for hybrid wet-dry R&D, hardware-aware intelligence, software automation, and industrial workflows.",
|
|
6
6
|
"license": "Apache-2.0",
|
package/public/app.js
CHANGED
|
@@ -145,6 +145,14 @@ const translations = {
|
|
|
145
145
|
wrappersOffLabel: "wrapper tools off",
|
|
146
146
|
wrapperCapabilityTitle: "Agent wrappers",
|
|
147
147
|
workspaceCapabilityTitle: "Workspace files",
|
|
148
|
+
workspaceExplorerTitle: "Workspace explorer",
|
|
149
|
+
workspaceExplorerHelp: "Browse, classify, drag into chat, and open project files.",
|
|
150
|
+
workspaceRefreshButton: "Refresh",
|
|
151
|
+
workspaceAutomapButton: "Auto map",
|
|
152
|
+
workspaceWorkbenchTitle: "Workspace workbench",
|
|
153
|
+
workspaceNoFileSelected: "Select a file from the explorer.",
|
|
154
|
+
workspaceSaveButton: "Save file",
|
|
155
|
+
workspaceRevertButton: "Revert",
|
|
148
156
|
mcpCapabilityTitle: "MCP servers",
|
|
149
157
|
mcpEmpty: "No MCP servers configured.",
|
|
150
158
|
workspaceToolsLabel: "File tools",
|
|
@@ -746,6 +754,19 @@ const wrapperGridEl = document.querySelector("#wrapper-grid");
|
|
|
746
754
|
const workspaceStatusEl = document.querySelector("#workspace-status");
|
|
747
755
|
const workspaceToolsEl = document.querySelector("#workspace-tools");
|
|
748
756
|
const workspaceChangesEl = document.querySelector("#workspace-changes");
|
|
757
|
+
const workspaceExplorerEl = document.querySelector("#workspace-explorer");
|
|
758
|
+
const workspaceBrowserStatusEl = document.querySelector("#workspace-browser-status");
|
|
759
|
+
const workspaceLanesEl = document.querySelector("#workspace-lanes");
|
|
760
|
+
const workspaceTreeEl = document.querySelector("#workspace-tree");
|
|
761
|
+
const workspaceRefreshButton = document.querySelector("#workspace-refresh");
|
|
762
|
+
const workspaceAutomapButton = document.querySelector("#workspace-automap");
|
|
763
|
+
const workspaceActiveTitleEl = document.querySelector("#workspace-active-title");
|
|
764
|
+
const workspaceActiveMetaEl = document.querySelector("#workspace-active-meta");
|
|
765
|
+
const workspacePreviewEl = document.querySelector("#workspace-preview");
|
|
766
|
+
const workspaceEditorEl = document.querySelector("#workspace-editor");
|
|
767
|
+
const workspaceSaveButton = document.querySelector("#workspace-save");
|
|
768
|
+
const workspaceRevertButton = document.querySelector("#workspace-revert");
|
|
769
|
+
const workspaceEditorStatusEl = document.querySelector("#workspace-editor-status");
|
|
749
770
|
const mcpStatusEl = document.querySelector("#mcp-status");
|
|
750
771
|
const mcpGridEl = document.querySelector("#mcp-grid");
|
|
751
772
|
const mcpWarningsEl = document.querySelector("#mcp-warnings");
|
|
@@ -835,6 +856,31 @@ let currentArtifactTab = "canvas";
|
|
|
835
856
|
let artifactUnreadCount = 0;
|
|
836
857
|
let lastAnnouncedRunKey = "";
|
|
837
858
|
let quickVeniceModeActive = false;
|
|
859
|
+
const WORKSPACE_LANES = [
|
|
860
|
+
{ id: "all", label: "All" },
|
|
861
|
+
{ id: "source", label: "Source" },
|
|
862
|
+
{ id: "scripts", label: "Scripts" },
|
|
863
|
+
{ id: "docs", label: "Docs" },
|
|
864
|
+
{ id: "data", label: "Data" },
|
|
865
|
+
{ id: "media", label: "Media" },
|
|
866
|
+
{ id: "references", label: "Refs" },
|
|
867
|
+
{ id: "artifacts", label: "Artifacts" },
|
|
868
|
+
{ id: "logs", label: "Logs" },
|
|
869
|
+
{ id: "other", label: "Other" },
|
|
870
|
+
];
|
|
871
|
+
let workspaceBrowser = {
|
|
872
|
+
root: "",
|
|
873
|
+
files: [],
|
|
874
|
+
warnings: [],
|
|
875
|
+
activeLane: "all",
|
|
876
|
+
activePath: "",
|
|
877
|
+
activeContent: "",
|
|
878
|
+
savedContent: "",
|
|
879
|
+
overrides: {},
|
|
880
|
+
loading: false,
|
|
881
|
+
error: "",
|
|
882
|
+
initialized: false,
|
|
883
|
+
};
|
|
838
884
|
|
|
839
885
|
function normalizeLanguage(language) {
|
|
840
886
|
const lower = String(language || "").toLowerCase();
|
|
@@ -1156,6 +1202,278 @@ function renderWorkspacePanel(workspace = lastWorkspace, activity = lastWorkspac
|
|
|
1156
1202
|
.join("");
|
|
1157
1203
|
}
|
|
1158
1204
|
|
|
1205
|
+
function workspaceRoot() {
|
|
1206
|
+
return commandCwdField?.value.trim() || projectInfo?.commandCwd || projectInfo?.root || "";
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function workspaceExt(filePath = "") {
|
|
1210
|
+
const name = String(filePath || "").toLowerCase();
|
|
1211
|
+
const index = name.lastIndexOf(".");
|
|
1212
|
+
return index >= 0 ? name.slice(index) : "";
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
function workspaceDir(filePath = "") {
|
|
1216
|
+
const normalized = String(filePath || "").replace(/\\/g, "/");
|
|
1217
|
+
const index = normalized.lastIndexOf("/");
|
|
1218
|
+
return index >= 0 ? normalized.slice(0, index) : "";
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function workspaceBaseName(filePath = "") {
|
|
1222
|
+
return String(filePath || "").replace(/\\/g, "/").split("/").filter(Boolean).pop() || filePath;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function inferWorkspaceLane(file = {}) {
|
|
1226
|
+
const filePath = String(file.path || "").toLowerCase();
|
|
1227
|
+
const first = filePath.split("/")[0] || "";
|
|
1228
|
+
const ext = workspaceExt(filePath);
|
|
1229
|
+
if (workspaceBrowser.overrides[file.path]) return workspaceBrowser.overrides[file.path];
|
|
1230
|
+
if (["artifacts", "runs", "outputs", "reports"].includes(first)) return "artifacts";
|
|
1231
|
+
if (["logs", "log", "events"].includes(first) || ext === ".log") return "logs";
|
|
1232
|
+
if (["data", "datasets", "tables"].includes(first) || [".csv", ".tsv", ".jsonl", ".xlsx", ".parquet"].includes(ext)) return "data";
|
|
1233
|
+
if (["docs", "doc", "references", "reference"].includes(first) || [".md", ".txt", ".tex", ".rst"].includes(ext)) return "docs";
|
|
1234
|
+
if (["refs", "bibliography"].includes(first) || [".bib", ".ris", ".enw"].includes(ext)) return "references";
|
|
1235
|
+
if (["scripts", "bin"].includes(first) || [".py", ".r", ".sh", ".m", ".pl"].includes(ext)) return "scripts";
|
|
1236
|
+
if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".pdf", ".mp4", ".mov", ".wav", ".mp3"].includes(ext)) return "media";
|
|
1237
|
+
if ([".js", ".mjs", ".ts", ".tsx", ".jsx", ".css", ".html", ".go", ".rs", ".c", ".cpp", ".h", ".hpp", ".java", ".rb", ".php", ".lua"].includes(ext)) {
|
|
1238
|
+
return "source";
|
|
1239
|
+
}
|
|
1240
|
+
return "other";
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
function workspaceFilesByLane() {
|
|
1244
|
+
const groups = Object.fromEntries(WORKSPACE_LANES.map((lane) => [lane.id, []]));
|
|
1245
|
+
for (const file of workspaceBrowser.files) {
|
|
1246
|
+
groups.all.push(file);
|
|
1247
|
+
const lane = inferWorkspaceLane(file);
|
|
1248
|
+
if (!groups[lane]) groups.other.push(file);
|
|
1249
|
+
else groups[lane].push(file);
|
|
1250
|
+
}
|
|
1251
|
+
return groups;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
function workspaceActiveFile() {
|
|
1255
|
+
return workspaceBrowser.files.find((file) => file.path === workspaceBrowser.activePath) || null;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function formatBytes(size = 0) {
|
|
1259
|
+
const value = Number(size || 0);
|
|
1260
|
+
if (value < 1024) return `${value} B`;
|
|
1261
|
+
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
1262
|
+
return `${(value / 1024 / 1024).toFixed(1)} MB`;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function workspaceRawUrl(filePath = "") {
|
|
1266
|
+
const params = new URLSearchParams({ commandCwd: workspaceBrowser.root || workspaceRoot(), path: filePath });
|
|
1267
|
+
return `/api/workspace/raw?${params.toString()}`;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
function setWorkspaceEditorStatus(message = "", kind = "info") {
|
|
1271
|
+
if (!workspaceEditorStatusEl) return;
|
|
1272
|
+
workspaceEditorStatusEl.textContent = message;
|
|
1273
|
+
workspaceEditorStatusEl.dataset.kind = kind;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function renderWorkspaceExplorer() {
|
|
1277
|
+
if (!workspaceExplorerEl || !workspaceLanesEl || !workspaceTreeEl || !workspaceBrowserStatusEl) return;
|
|
1278
|
+
const groups = workspaceFilesByLane();
|
|
1279
|
+
const activeLane = workspaceBrowser.activeLane || "all";
|
|
1280
|
+
const warnings = workspaceBrowser.warnings?.length ? ` · ${workspaceBrowser.warnings.length} warning(s)` : "";
|
|
1281
|
+
const truncated = workspaceBrowser.truncated ? " · truncated" : "";
|
|
1282
|
+
const status = workspaceBrowser.loading
|
|
1283
|
+
? "Loading workspace..."
|
|
1284
|
+
: workspaceBrowser.error
|
|
1285
|
+
? `Workspace unavailable: ${workspaceBrowser.error}`
|
|
1286
|
+
: `${workspaceBrowser.files.length} files · ${workspaceBrowser.root || workspaceRoot()}${warnings}${truncated}`;
|
|
1287
|
+
workspaceBrowserStatusEl.textContent = status;
|
|
1288
|
+
|
|
1289
|
+
workspaceLanesEl.innerHTML = WORKSPACE_LANES.map((lane) => {
|
|
1290
|
+
const count = groups[lane.id]?.length || 0;
|
|
1291
|
+
return `
|
|
1292
|
+
<button
|
|
1293
|
+
type="button"
|
|
1294
|
+
class="workspace-lane"
|
|
1295
|
+
data-workspace-lane="${escapeHtml(lane.id)}"
|
|
1296
|
+
data-selected="${lane.id === activeLane}"
|
|
1297
|
+
title="Drop a file here to classify it as ${escapeHtml(lane.label)}"
|
|
1298
|
+
>
|
|
1299
|
+
<span>${escapeHtml(lane.label)}</span>
|
|
1300
|
+
<strong>${count}</strong>
|
|
1301
|
+
</button>
|
|
1302
|
+
`;
|
|
1303
|
+
}).join("");
|
|
1304
|
+
|
|
1305
|
+
const visibleFiles = (groups[activeLane] || []).slice().sort((left, right) => left.path.localeCompare(right.path));
|
|
1306
|
+
if (!visibleFiles.length) {
|
|
1307
|
+
workspaceTreeEl.innerHTML = `<p class="subtle workspace-empty">No files in this category.</p>`;
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
workspaceTreeEl.innerHTML = visibleFiles
|
|
1312
|
+
.map((file) => {
|
|
1313
|
+
const lane = inferWorkspaceLane(file);
|
|
1314
|
+
const active = file.path === workspaceBrowser.activePath;
|
|
1315
|
+
const meta = [lane, formatBytes(file.size), file.binary ? "binary" : "text"].join(" · ");
|
|
1316
|
+
return `
|
|
1317
|
+
<button
|
|
1318
|
+
type="button"
|
|
1319
|
+
class="workspace-file-row"
|
|
1320
|
+
data-workspace-file="${escapeHtml(file.path)}"
|
|
1321
|
+
data-selected="${active}"
|
|
1322
|
+
draggable="true"
|
|
1323
|
+
>
|
|
1324
|
+
<span class="workspace-file-main">
|
|
1325
|
+
<strong>${escapeHtml(workspaceBaseName(file.path))}</strong>
|
|
1326
|
+
<small>${escapeHtml(workspaceDir(file.path) || ".")}</small>
|
|
1327
|
+
</span>
|
|
1328
|
+
<span class="workspace-file-meta">${escapeHtml(meta)}</span>
|
|
1329
|
+
</button>
|
|
1330
|
+
`;
|
|
1331
|
+
})
|
|
1332
|
+
.join("");
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function renderWorkspaceWorkbench() {
|
|
1336
|
+
if (!workspaceActiveTitleEl || !workspaceActiveMetaEl || !workspacePreviewEl || !workspaceEditorEl) return;
|
|
1337
|
+
const file = workspaceActiveFile();
|
|
1338
|
+
const dirty = workspaceBrowser.activeContent !== workspaceBrowser.savedContent;
|
|
1339
|
+
|
|
1340
|
+
if (!file) {
|
|
1341
|
+
workspaceActiveTitleEl.textContent = t("workspaceNoFileSelected");
|
|
1342
|
+
workspaceActiveMetaEl.textContent = t("workspaceNoFileSelected");
|
|
1343
|
+
workspacePreviewEl.innerHTML = `<p class="subtle">${t("workspaceNoFileSelected")}</p>`;
|
|
1344
|
+
workspaceEditorEl.hidden = true;
|
|
1345
|
+
if (workspaceSaveButton) workspaceSaveButton.disabled = true;
|
|
1346
|
+
if (workspaceRevertButton) workspaceRevertButton.disabled = true;
|
|
1347
|
+
setWorkspaceEditorStatus("");
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
workspaceActiveTitleEl.textContent = file.path;
|
|
1352
|
+
workspaceActiveMetaEl.textContent = `${inferWorkspaceLane(file)} · ${formatBytes(file.size)} · ${
|
|
1353
|
+
file.binary ? "preview-only" : dirty ? "edited" : "saved"
|
|
1354
|
+
}`;
|
|
1355
|
+
|
|
1356
|
+
const ext = workspaceExt(file.path);
|
|
1357
|
+
const rawUrl = workspaceRawUrl(file.path);
|
|
1358
|
+
if (file.binary) {
|
|
1359
|
+
workspaceEditorEl.hidden = true;
|
|
1360
|
+
if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
|
|
1361
|
+
workspacePreviewEl.innerHTML = `<img class="workspace-media-preview" src="${escapeHtml(rawUrl)}" alt="${escapeHtml(file.path)}" />`;
|
|
1362
|
+
} else if (ext === ".pdf") {
|
|
1363
|
+
workspacePreviewEl.innerHTML = `<iframe class="workspace-pdf-preview" src="${escapeHtml(rawUrl)}" title="${escapeHtml(file.path)}"></iframe>`;
|
|
1364
|
+
} else {
|
|
1365
|
+
workspacePreviewEl.innerHTML = `<p class="subtle">Binary file preview is unavailable. Use the raw file path or artifact canvas for specialized previews.</p>`;
|
|
1366
|
+
}
|
|
1367
|
+
if (workspaceSaveButton) workspaceSaveButton.disabled = true;
|
|
1368
|
+
if (workspaceRevertButton) workspaceRevertButton.disabled = true;
|
|
1369
|
+
setWorkspaceEditorStatus("Preview-only binary file.");
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
workspacePreviewEl.innerHTML = `<pre class="workspace-code-preview">${escapeHtml(workspaceBrowser.activeContent).slice(
|
|
1374
|
+
0,
|
|
1375
|
+
12000
|
|
1376
|
+
)}</pre>`;
|
|
1377
|
+
workspaceEditorEl.hidden = false;
|
|
1378
|
+
if (document.activeElement !== workspaceEditorEl && workspaceEditorEl.value !== workspaceBrowser.activeContent) {
|
|
1379
|
+
workspaceEditorEl.value = workspaceBrowser.activeContent;
|
|
1380
|
+
}
|
|
1381
|
+
if (workspaceSaveButton) workspaceSaveButton.disabled = !dirty;
|
|
1382
|
+
if (workspaceRevertButton) workspaceRevertButton.disabled = !dirty;
|
|
1383
|
+
setWorkspaceEditorStatus(dirty ? "Unsaved editor changes." : "File loaded.");
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
function renderWorkspaceBrowser() {
|
|
1387
|
+
renderWorkspaceExplorer();
|
|
1388
|
+
renderWorkspaceWorkbench();
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
async function refreshWorkspaceSnapshot({ preserveActive = true } = {}) {
|
|
1392
|
+
if (!workspaceExplorerEl) return;
|
|
1393
|
+
workspaceBrowser.loading = true;
|
|
1394
|
+
workspaceBrowser.error = "";
|
|
1395
|
+
renderWorkspaceExplorer();
|
|
1396
|
+
try {
|
|
1397
|
+
const response = await fetch("/api/workspace/snapshot", {
|
|
1398
|
+
method: "POST",
|
|
1399
|
+
headers: { "Content-Type": "application/json" },
|
|
1400
|
+
body: JSON.stringify({ commandCwd: workspaceRoot() }),
|
|
1401
|
+
});
|
|
1402
|
+
const data = await response.json().catch(() => ({}));
|
|
1403
|
+
if (!response.ok) throw new Error(data.error || "Workspace snapshot failed.");
|
|
1404
|
+
workspaceBrowser = {
|
|
1405
|
+
...workspaceBrowser,
|
|
1406
|
+
root: data.root || workspaceRoot(),
|
|
1407
|
+
files: data.files || [],
|
|
1408
|
+
warnings: data.warnings || [],
|
|
1409
|
+
truncated: Boolean(data.truncated),
|
|
1410
|
+
loading: false,
|
|
1411
|
+
error: "",
|
|
1412
|
+
initialized: true,
|
|
1413
|
+
};
|
|
1414
|
+
if (!preserveActive || !workspaceBrowser.files.some((file) => file.path === workspaceBrowser.activePath)) {
|
|
1415
|
+
workspaceBrowser.activePath = workspaceBrowser.files.find((file) => !file.binary)?.path || workspaceBrowser.files[0]?.path || "";
|
|
1416
|
+
const active = workspaceActiveFile();
|
|
1417
|
+
workspaceBrowser.activeContent = active?.content || "";
|
|
1418
|
+
workspaceBrowser.savedContent = active?.content || "";
|
|
1419
|
+
} else {
|
|
1420
|
+
const active = workspaceActiveFile();
|
|
1421
|
+
if (!active?.binary && workspaceBrowser.activeContent === workspaceBrowser.savedContent) {
|
|
1422
|
+
workspaceBrowser.activeContent = active?.content || "";
|
|
1423
|
+
workspaceBrowser.savedContent = active?.content || "";
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
workspaceBrowser.loading = false;
|
|
1428
|
+
workspaceBrowser.error = error instanceof Error ? error.message : String(error);
|
|
1429
|
+
}
|
|
1430
|
+
renderWorkspaceBrowser();
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function selectWorkspaceFile(filePath = "") {
|
|
1434
|
+
const file = workspaceBrowser.files.find((item) => item.path === filePath);
|
|
1435
|
+
if (!file) return;
|
|
1436
|
+
workspaceBrowser.activePath = file.path;
|
|
1437
|
+
workspaceBrowser.activeContent = file.content || "";
|
|
1438
|
+
workspaceBrowser.savedContent = file.content || "";
|
|
1439
|
+
renderWorkspaceBrowser();
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
async function saveWorkspaceEditor() {
|
|
1443
|
+
const file = workspaceActiveFile();
|
|
1444
|
+
if (!file || file.binary || !workspaceEditorEl) return;
|
|
1445
|
+
const content = workspaceEditorEl.value;
|
|
1446
|
+
setWorkspaceEditorStatus("Saving...");
|
|
1447
|
+
const response = await fetch("/api/workspace/write", {
|
|
1448
|
+
method: "POST",
|
|
1449
|
+
headers: { "Content-Type": "application/json" },
|
|
1450
|
+
body: JSON.stringify({ commandCwd: workspaceBrowser.root || workspaceRoot(), path: file.path, content }),
|
|
1451
|
+
});
|
|
1452
|
+
const data = await response.json().catch(() => ({}));
|
|
1453
|
+
if (!response.ok) {
|
|
1454
|
+
setWorkspaceEditorStatus(data.error || "Save failed.", "error");
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
workspaceBrowser.activeContent = content;
|
|
1458
|
+
workspaceBrowser.savedContent = content;
|
|
1459
|
+
showToast(`Saved ${file.path}.`);
|
|
1460
|
+
await refreshWorkspaceSnapshot({ preserveActive: true });
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
function revertWorkspaceEditor() {
|
|
1464
|
+
workspaceBrowser.activeContent = workspaceBrowser.savedContent;
|
|
1465
|
+
if (workspaceEditorEl) workspaceEditorEl.value = workspaceBrowser.savedContent;
|
|
1466
|
+
renderWorkspaceWorkbench();
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
function insertWorkspaceReference(filePath = "") {
|
|
1470
|
+
if (!chatInputEl || !filePath) return;
|
|
1471
|
+
const reference = `@file ${filePath}`;
|
|
1472
|
+
const current = chatInputEl.value.trimEnd();
|
|
1473
|
+
chatInputEl.value = current ? `${current}\n${reference}` : reference;
|
|
1474
|
+
chatInputEl.focus();
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1159
1477
|
function renderMcpPanel(mcp = lastMcp) {
|
|
1160
1478
|
lastMcp = mcp || null;
|
|
1161
1479
|
if (!mcpStatusEl || !mcpGridEl || !mcpWarningsEl) return;
|
|
@@ -1663,6 +1981,7 @@ function applyLanguage(language, { persist = true } = {}) {
|
|
|
1663
1981
|
renderProjectStatus();
|
|
1664
1982
|
renderWrapperStatus();
|
|
1665
1983
|
renderWorkspacePanel();
|
|
1984
|
+
renderWorkspaceBrowser();
|
|
1666
1985
|
renderSandboxStatus();
|
|
1667
1986
|
updateRoutingHint();
|
|
1668
1987
|
updatePermissionHint();
|
|
@@ -3085,6 +3404,7 @@ async function refreshRun() {
|
|
|
3085
3404
|
await refreshChat();
|
|
3086
3405
|
await refreshSessions();
|
|
3087
3406
|
await refreshWorkspaceChanges();
|
|
3407
|
+
await refreshWorkspaceSnapshot({ preserveActive: true });
|
|
3088
3408
|
await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
|
|
3089
3409
|
if (run.status === "finished") await flushAfterFinishQueue();
|
|
3090
3410
|
}
|
|
@@ -3335,6 +3655,7 @@ async function startRunFromGoal(goal, { clearComposer = false } = {}) {
|
|
|
3335
3655
|
sessionSelectEl.value = currentSessionId;
|
|
3336
3656
|
await refreshChat();
|
|
3337
3657
|
await refreshWorkspaceChanges();
|
|
3658
|
+
await refreshWorkspaceSnapshot({ preserveActive: true });
|
|
3338
3659
|
await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
|
|
3339
3660
|
|
|
3340
3661
|
if (pollTimer) clearInterval(pollTimer);
|
|
@@ -3589,6 +3910,77 @@ commandCwdField?.addEventListener("input", () => {
|
|
|
3589
3910
|
commandCwdField?.addEventListener("change", () => {
|
|
3590
3911
|
schedulePreferenceSave();
|
|
3591
3912
|
refreshCommandCwdSuggestions(commandCwdField.value).catch(() => {});
|
|
3913
|
+
refreshWorkspaceSnapshot({ preserveActive: false }).catch(() => {});
|
|
3914
|
+
});
|
|
3915
|
+
|
|
3916
|
+
workspaceRefreshButton?.addEventListener("click", () => {
|
|
3917
|
+
refreshWorkspaceSnapshot({ preserveActive: true }).catch((error) => {
|
|
3918
|
+
setWorkspaceEditorStatus(String(error), "error");
|
|
3919
|
+
});
|
|
3920
|
+
});
|
|
3921
|
+
|
|
3922
|
+
workspaceAutomapButton?.addEventListener("click", () => {
|
|
3923
|
+
workspaceBrowser.overrides = {};
|
|
3924
|
+
renderWorkspaceBrowser();
|
|
3925
|
+
showToast("Workspace files auto-mapped by general category.");
|
|
3926
|
+
});
|
|
3927
|
+
|
|
3928
|
+
workspaceLanesEl?.addEventListener("click", (event) => {
|
|
3929
|
+
const button = event.target.closest("[data-workspace-lane]");
|
|
3930
|
+
if (!button) return;
|
|
3931
|
+
workspaceBrowser.activeLane = button.dataset.workspaceLane || "all";
|
|
3932
|
+
renderWorkspaceBrowser();
|
|
3933
|
+
});
|
|
3934
|
+
|
|
3935
|
+
workspaceLanesEl?.addEventListener("dragover", (event) => {
|
|
3936
|
+
if (event.target.closest("[data-workspace-lane]")) event.preventDefault();
|
|
3937
|
+
});
|
|
3938
|
+
|
|
3939
|
+
workspaceLanesEl?.addEventListener("drop", (event) => {
|
|
3940
|
+
const button = event.target.closest("[data-workspace-lane]");
|
|
3941
|
+
const filePath = event.dataTransfer?.getData("text/agintiflow-workspace-file") || event.dataTransfer?.getData("text/plain");
|
|
3942
|
+
const lane = button?.dataset.workspaceLane;
|
|
3943
|
+
if (!button || !filePath || !lane || lane === "all") return;
|
|
3944
|
+
event.preventDefault();
|
|
3945
|
+
workspaceBrowser.overrides[filePath] = lane;
|
|
3946
|
+
workspaceBrowser.activeLane = lane;
|
|
3947
|
+
renderWorkspaceBrowser();
|
|
3948
|
+
});
|
|
3949
|
+
|
|
3950
|
+
workspaceTreeEl?.addEventListener("click", (event) => {
|
|
3951
|
+
const row = event.target.closest("[data-workspace-file]");
|
|
3952
|
+
if (!row) return;
|
|
3953
|
+
selectWorkspaceFile(row.dataset.workspaceFile || "");
|
|
3954
|
+
});
|
|
3955
|
+
|
|
3956
|
+
workspaceTreeEl?.addEventListener("dragstart", (event) => {
|
|
3957
|
+
const row = event.target.closest("[data-workspace-file]");
|
|
3958
|
+
if (!row || !event.dataTransfer) return;
|
|
3959
|
+
event.dataTransfer.setData("text/agintiflow-workspace-file", row.dataset.workspaceFile || "");
|
|
3960
|
+
event.dataTransfer.setData("text/plain", row.dataset.workspaceFile || "");
|
|
3961
|
+
event.dataTransfer.effectAllowed = "copyMove";
|
|
3962
|
+
});
|
|
3963
|
+
|
|
3964
|
+
workspaceEditorEl?.addEventListener("input", () => {
|
|
3965
|
+
workspaceBrowser.activeContent = workspaceEditorEl.value;
|
|
3966
|
+
renderWorkspaceWorkbench();
|
|
3967
|
+
});
|
|
3968
|
+
|
|
3969
|
+
workspaceSaveButton?.addEventListener("click", () => {
|
|
3970
|
+
saveWorkspaceEditor().catch((error) => setWorkspaceEditorStatus(String(error), "error"));
|
|
3971
|
+
});
|
|
3972
|
+
|
|
3973
|
+
workspaceRevertButton?.addEventListener("click", revertWorkspaceEditor);
|
|
3974
|
+
|
|
3975
|
+
chatInputEl?.addEventListener("dragover", (event) => {
|
|
3976
|
+
if (event.dataTransfer?.types?.includes("text/agintiflow-workspace-file")) event.preventDefault();
|
|
3977
|
+
});
|
|
3978
|
+
|
|
3979
|
+
chatInputEl?.addEventListener("drop", (event) => {
|
|
3980
|
+
const filePath = event.dataTransfer?.getData("text/agintiflow-workspace-file");
|
|
3981
|
+
if (!filePath) return;
|
|
3982
|
+
event.preventDefault();
|
|
3983
|
+
insertWorkspaceReference(filePath);
|
|
3592
3984
|
});
|
|
3593
3985
|
|
|
3594
3986
|
chatInputEl.addEventListener("keydown", (event) => {
|
|
@@ -3724,6 +4116,7 @@ chatFormEl.addEventListener("submit", async (event) => {
|
|
|
3724
4116
|
await refreshSessions();
|
|
3725
4117
|
await refreshChat();
|
|
3726
4118
|
await refreshWorkspaceChanges();
|
|
4119
|
+
await refreshWorkspaceSnapshot({ preserveActive: true });
|
|
3727
4120
|
await refreshArtifacts({ loadSelected: artifactTunnelDialog?.open });
|
|
3728
4121
|
|
|
3729
4122
|
if (pollTimer) clearInterval(pollTimer);
|
|
@@ -3835,6 +4228,7 @@ async function loadConfig() {
|
|
|
3835
4228
|
renderWorkspacePanel(data.workspace, []);
|
|
3836
4229
|
renderMcpPanel(data.mcp || null);
|
|
3837
4230
|
await refreshWorkspaceChanges();
|
|
4231
|
+
await refreshWorkspaceSnapshot({ preserveActive: false });
|
|
3838
4232
|
renderSandboxLogs(data.sandbox?.logs || []);
|
|
3839
4233
|
if (quickVeniceModeActive) applyVeniceMode(true);
|
|
3840
4234
|
updateRoutingHint();
|
package/public/index.html
CHANGED
|
@@ -223,6 +223,22 @@
|
|
|
223
223
|
<button id="open-settings" type="button" class="secondary settings-button">Advanced settings</button>
|
|
224
224
|
</form>
|
|
225
225
|
</details>
|
|
226
|
+
|
|
227
|
+
<section id="workspace-explorer" class="workspace-explorer-card" aria-labelledby="workspace-explorer-title">
|
|
228
|
+
<div class="workspace-explorer-head">
|
|
229
|
+
<div>
|
|
230
|
+
<h2 id="workspace-explorer-title" data-i18n="workspaceExplorerTitle">Workspace explorer</h2>
|
|
231
|
+
<p class="subtle" data-i18n="workspaceExplorerHelp">Browse, classify, drag into chat, and open project files.</p>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="workspace-explorer-actions">
|
|
234
|
+
<button id="workspace-refresh" type="button" class="secondary" data-i18n="workspaceRefreshButton">Refresh</button>
|
|
235
|
+
<button id="workspace-automap" type="button" class="secondary" data-i18n="workspaceAutomapButton">Auto map</button>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
<div id="workspace-browser-status" class="subtle workspace-browser-status" aria-live="polite">Loading workspace...</div>
|
|
239
|
+
<div id="workspace-lanes" class="workspace-lanes" aria-label="Workspace file categories"></div>
|
|
240
|
+
<div id="workspace-tree" class="workspace-tree" aria-live="polite"></div>
|
|
241
|
+
</section>
|
|
226
242
|
</section>
|
|
227
243
|
|
|
228
244
|
<section class="stack">
|
|
@@ -306,6 +322,23 @@
|
|
|
306
322
|
<div id="chat-pending" class="chat-pending" hidden></div>
|
|
307
323
|
</section>
|
|
308
324
|
|
|
325
|
+
<section id="workspace-workbench" class="panel workspace-workbench">
|
|
326
|
+
<div class="header-row">
|
|
327
|
+
<div>
|
|
328
|
+
<h2 data-i18n="workspaceWorkbenchTitle">Workspace workbench</h2>
|
|
329
|
+
<p id="workspace-active-meta" class="subtle" data-i18n="workspaceNoFileSelected">Select a file from the explorer.</p>
|
|
330
|
+
</div>
|
|
331
|
+
<div class="workspace-editor-actions">
|
|
332
|
+
<button id="workspace-revert" type="button" class="secondary" disabled data-i18n="workspaceRevertButton">Revert</button>
|
|
333
|
+
<button id="workspace-save" type="button" disabled data-i18n="workspaceSaveButton">Save file</button>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
<div id="workspace-active-title" class="workspace-active-title" data-i18n="workspaceNoFileSelected">Select a file from the explorer.</div>
|
|
337
|
+
<div id="workspace-preview" class="workspace-preview"></div>
|
|
338
|
+
<textarea id="workspace-editor" class="workspace-editor" spellcheck="false" hidden></textarea>
|
|
339
|
+
<div id="workspace-editor-status" class="subtle workspace-editor-status"></div>
|
|
340
|
+
</section>
|
|
341
|
+
|
|
309
342
|
<section class="panel output-panel">
|
|
310
343
|
<div class="header-row">
|
|
311
344
|
<h2 data-i18n="runOutputTitle">Run output</h2>
|