micro-models-agent 2.13.1 → 2.14.0
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/CHANGELOG.md +6 -0
- package/dist/assets/js/feed.js +70 -35
- package/dist/assets/styles.css +19 -27
- package/dist/i18n/en.json +3 -4
- package/dist/i18n/ru.json +3 -4
- package/dist/main.js +19 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.14.0] - 2026-10-01
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Web UI**: file actions now render as quiet terminal-style lines instead of boxed cards (`src/modules/webui/assets/js/feed.js`, `styles.css`). Each file tool shows `◇ <label> · <path-link> [copy-icon] <status>` in the tool-category color — no more Open/Download/Path pill buttons (path click opens the viewer, copy is an icon-only hover action). Consecutive file rows collapse into an expandable `│ N files` group with a chevron toggle. File-type badges are gone (format detection stays silent for highlighting only; draw.io hint is verbose-only). Dead strings `web.card.open/download/copy_path/download_title` removed, count via new `web.card.files_*` (en+ru). Tests: `tests/webui-assets.test.ts`, `tests/webui-smoke.test.ts`, new `tests/e2e/web-filecard.e2e-test.ts`.
|
|
14
|
+
|
|
9
15
|
## [2.13.1] - 2026-09-30
|
|
10
16
|
|
|
11
17
|
### Fixed
|
package/dist/assets/js/feed.js
CHANGED
|
@@ -444,56 +444,91 @@ function safeUrl(url) {
|
|
|
444
444
|
return null;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
-
function
|
|
447
|
+
function maybeGroupFilerow(feed, row) {
|
|
448
|
+
const prev = row.previousElementSibling;
|
|
449
|
+
const group = prev && prev.classList && prev.classList.contains("filegroup") ? prev : null;
|
|
450
|
+
const prevRow = prev && prev.classList && prev.classList.contains("filerow") ? prev : null;
|
|
451
|
+
if (group && group.dataset.open !== "1") {
|
|
452
|
+
group.querySelector(".items").appendChild(row);
|
|
453
|
+
if (row.dataset.fail === "1") group.dataset.fail = "1";
|
|
454
|
+
const n = group.querySelectorAll(".items > .filerow").length;
|
|
455
|
+
group.querySelector(".count").textContent = pluralCount("web.card.files", n);
|
|
456
|
+
group.querySelector(".ok").textContent = group.dataset.fail === "1" ? " ✗" : " ✓";
|
|
457
|
+
return group;
|
|
458
|
+
}
|
|
459
|
+
if (prevRow) {
|
|
460
|
+
const wrap = el("div", "filegroup");
|
|
461
|
+
const head = el("button", "head");
|
|
462
|
+
head.setAttribute("aria-expanded", "false");
|
|
463
|
+
head.appendChild(el("span", "g", "│ "));
|
|
464
|
+
head.appendChild(el("span", "count", pluralCount("web.card.files", 2)));
|
|
465
|
+
const fail = prevRow.dataset.fail === "1" || row.dataset.fail === "1";
|
|
466
|
+
if (fail) wrap.dataset.fail = "1";
|
|
467
|
+
head.appendChild(el("span", "ok", fail ? " ✗" : " ✓"));
|
|
468
|
+
const chev = icon("chevron-down");
|
|
469
|
+
head.appendChild(chev);
|
|
470
|
+
const items = el("div", "items hidden");
|
|
471
|
+
feed.replaceChild(wrap, prevRow);
|
|
472
|
+
items.appendChild(prevRow);
|
|
473
|
+
items.appendChild(row);
|
|
474
|
+
wrap.appendChild(head);
|
|
475
|
+
wrap.appendChild(items);
|
|
476
|
+
head.addEventListener("click", () => {
|
|
477
|
+
const open = wrap.dataset.open === "1";
|
|
478
|
+
wrap.dataset.open = open ? "" : "1";
|
|
479
|
+
head.setAttribute("aria-expanded", open ? "false" : "true");
|
|
480
|
+
items.classList.toggle("hidden", open);
|
|
481
|
+
});
|
|
482
|
+
return wrap;
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function cardFor(name, args, sid, ok) {
|
|
448
488
|
const feed = feedForEvent(sid);
|
|
449
489
|
if (!feed) return null;
|
|
450
490
|
if (FILE_TOOLS.indexOf(name) !== -1) {
|
|
451
491
|
const path = args.path || args.file || args.dir;
|
|
452
492
|
if (!path) return null;
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
nameEl.title = path;
|
|
458
|
-
top.appendChild(nameEl);
|
|
459
|
-
card.appendChild(top);
|
|
460
|
-
const bottom = el("div", "fc-bottom");
|
|
461
|
-
bottom.appendChild(el("span", "lab", labelOf(name)));
|
|
462
|
-
const actions = el("span", "fc-actions");
|
|
463
|
-
const baseName = String(path.split(/[\\/]/).pop() || path);
|
|
493
|
+
const row = el("div", "filerow " + categoryOf(name));
|
|
494
|
+
row.appendChild(el("span", "g", "◇"));
|
|
495
|
+
row.appendChild(el("span", "lab", labelOf(name)));
|
|
496
|
+
row.appendChild(el("span", "sep", " · "));
|
|
464
497
|
if (OPENABLE_TOOLS.indexOf(name) !== -1) {
|
|
465
|
-
const openLink = el("a", "
|
|
498
|
+
const openLink = el("a", "path", String(path).split(/[\\/]/).pop() || path);
|
|
466
499
|
openLink.href = "/api/file?path=" + encodeURIComponent(path);
|
|
467
500
|
openLink.target = "_blank";
|
|
468
501
|
openLink.rel = "noopener noreferrer";
|
|
469
|
-
openLink.title = t("web.card.open_title");
|
|
470
|
-
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
502
|
+
openLink.title = t("web.card.open_title") + " · " + path;
|
|
503
|
+
row.appendChild(openLink);
|
|
504
|
+
const tail = String(path).split(/[\\/]/).slice(0, -1).join("/");
|
|
505
|
+
if (tail) row.appendChild(el("span", "tail", " · " + tail));
|
|
506
|
+
} else {
|
|
507
|
+
const s = el("span", "tail", path);
|
|
508
|
+
s.title = path;
|
|
509
|
+
row.appendChild(s);
|
|
476
510
|
}
|
|
477
|
-
const copyBtn = el("button", "
|
|
511
|
+
const copyBtn = el("button", "copy-btn");
|
|
478
512
|
copyBtn.type = "button";
|
|
479
513
|
copyBtn.title = t("web.card.copy_title");
|
|
514
|
+
copyBtn.setAttribute("aria-label", t("web.card.copy_title"));
|
|
515
|
+
copyBtn.appendChild(icon("copy"));
|
|
480
516
|
copyBtn.addEventListener("click", () => {
|
|
481
517
|
copyText(path).then((ok) => {
|
|
482
|
-
copyBtn.
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}, 1400);
|
|
518
|
+
copyBtn.replaceChildren(icon(ok ? "check" : "copy"));
|
|
519
|
+
if (!ok) { const st = row.querySelector(".st"); if (st) st.textContent = " ⚠"; }
|
|
520
|
+
window.setTimeout(() => copyBtn.replaceChildren(icon("copy")), 1400);
|
|
486
521
|
});
|
|
487
522
|
});
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
if (/\.drawio(\.xml)?$/i.test(path)) {
|
|
492
|
-
|
|
493
|
-
card.appendChild(hint);
|
|
523
|
+
row.appendChild(copyBtn);
|
|
524
|
+
row.appendChild(el("span", "st", ok === false ? " ✗" : ok === true ? " ✓" : ""));
|
|
525
|
+
if (ok === false) row.dataset.fail = "1";
|
|
526
|
+
if (/\.drawio(\.xml)?$/i.test(path) && verbosity() === "verbose") {
|
|
527
|
+
row.appendChild(el("span", "detail", "↳ " + t("web.card.drawio_hint")));
|
|
494
528
|
}
|
|
495
|
-
appendTo(feed,
|
|
496
|
-
|
|
529
|
+
appendTo(feed, row);
|
|
530
|
+
try { maybeGroupFilerow(feed, row); } catch (err) { /* fail-open: plain row stays */ }
|
|
531
|
+
return row;
|
|
497
532
|
}
|
|
498
533
|
if (LINK_TOOLS.indexOf(name) !== -1 && args.url) {
|
|
499
534
|
const line = el("div", "linkline");
|
|
@@ -549,7 +584,7 @@ export function onToolEnd(data) {
|
|
|
549
584
|
// a single `↳` cause instead of the whole model-facing body.
|
|
550
585
|
if (data.success) {
|
|
551
586
|
if (line) S.runNodes.push(line);
|
|
552
|
-
const card = cardFor(data.name, args, data.sessionId);
|
|
587
|
+
const card = cardFor(data.name, args, data.sessionId, data.success);
|
|
553
588
|
if (card) S.runNodes.push(card);
|
|
554
589
|
S.runDuration += typeof data.durationMs === "number" ? data.durationMs : 0;
|
|
555
590
|
} else {
|
|
@@ -558,7 +593,7 @@ export function onToolEnd(data) {
|
|
|
558
593
|
}
|
|
559
594
|
return;
|
|
560
595
|
}
|
|
561
|
-
if (data.success) cardFor(data.name, args, data.sessionId);
|
|
596
|
+
if (data.success) cardFor(data.name, args, data.sessionId, data.success);
|
|
562
597
|
renderToolOutput(line, data.output);
|
|
563
598
|
}
|
|
564
599
|
|
package/dist/assets/styles.css
CHANGED
|
@@ -377,31 +377,23 @@ body.light .codeblock { border: 1px solid var(--border); }
|
|
|
377
377
|
}
|
|
378
378
|
body.light .imgcard .ph { background: var(--ph-light); }
|
|
379
379
|
.imgcard .cap { padding: 6px 10px; color: var(--dim-a); font-size: 12px; }
|
|
380
|
-
.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
.
|
|
387
|
-
.
|
|
388
|
-
.
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
.
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
.
|
|
395
|
-
.
|
|
396
|
-
|
|
397
|
-
color: var(--accent); background: transparent; border: 1px solid var(--border-2);
|
|
398
|
-
border-radius: 4px; padding: 2px 10px; cursor: pointer; text-decoration: none;
|
|
399
|
-
}
|
|
400
|
-
.filecard .fc-open:hover { border-color: var(--accent); text-decoration: none; }
|
|
401
|
-
.filecard .fc-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
402
|
-
.filecard button.fc-open { font: inherit; }
|
|
403
|
-
.filecard .fc-actions { display: flex; gap: 6px; align-items: center; margin-left: auto; flex: 0 0 auto; }
|
|
404
|
-
.filecard .fc-hint { color: var(--dim-a); font-size: 12px; }
|
|
380
|
+
.filerow { margin-left: 26px; display: flex; gap: 8px; align-items: baseline; min-width: 0; font-size: 12.5px; color: var(--muted); }
|
|
381
|
+
.filerow .g { flex: 0 0 auto; }
|
|
382
|
+
.filerow.recon .g, .filerow.recon .lab { color: var(--cyan); }
|
|
383
|
+
.filerow.mutate .g, .filerow.mutate .lab { color: var(--magenta); }
|
|
384
|
+
.filerow .sep { color: var(--dim-b); }
|
|
385
|
+
.filerow a.path { color: var(--accent); text-decoration: none; overflow-wrap: anywhere; word-break: break-word; }
|
|
386
|
+
.filerow a.path:hover { text-decoration: underline; }
|
|
387
|
+
.filerow .tail { color: var(--dim-a); overflow-wrap: anywhere; word-break: break-word; }
|
|
388
|
+
.filerow .copy-btn { background: transparent; border: 0; padding: 2px; color: var(--dim-a); cursor: pointer; opacity: 0; }
|
|
389
|
+
.filerow:hover .copy-btn, .filerow:focus-within .copy-btn { opacity: 1; }
|
|
390
|
+
@media (pointer: coarse) { .filerow .copy-btn { opacity: 1; } }
|
|
391
|
+
.filerow .copy-btn:focus-visible { opacity: 1; outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
392
|
+
.filerow .copy-btn .svg-icon { width: 14px; height: 14px; }
|
|
393
|
+
.filegroup { margin-left: 26px; font-size: 12.5px; color: var(--muted); }
|
|
394
|
+
.filegroup .head { background: transparent; border: 0; color: inherit; font: inherit; cursor: pointer; display: flex; gap: 6px; align-items: baseline; padding: 0; }
|
|
395
|
+
.filegroup .items.hidden { display: none; }
|
|
396
|
+
.filegroup .items > .filerow { margin-left: 0; }
|
|
405
397
|
.linkline { margin-left: 26px; min-width: 0; overflow-wrap: anywhere; }
|
|
406
398
|
.linkline a { color: var(--accent); text-decoration: none; overflow-wrap: anywhere; word-break: break-word; }
|
|
407
399
|
.linkline a:hover { text-decoration: underline; }
|
|
@@ -613,7 +605,7 @@ body.light .sel > button { border: 1px solid var(--border); }
|
|
|
613
605
|
|
|
614
606
|
/* new rows rise in softly (a bulk history render animates once as a whole —
|
|
615
607
|
one shared fade, still cheap: opacity/transform only) */
|
|
616
|
-
.feed > :is(.u, .md, .act, .think, .q, .
|
|
608
|
+
.feed > :is(.u, .md, .act, .think, .q, .filerow, .filegroup, .planblock, .imgcard, .linkline,
|
|
617
609
|
.toolout, .metric, .detail, .metaln, .warnline, .errline, .cmdout, .sysline,
|
|
618
610
|
.startbanner, .grouped, .msg-actions, .busy) {
|
|
619
611
|
animation: rise .18s ease-out;
|
|
@@ -640,7 +632,7 @@ body.light .sel > button { border: 1px solid var(--border); }
|
|
|
640
632
|
.overlay.open .modal { animation: rise .18s ease-out; }
|
|
641
633
|
|
|
642
634
|
/* hover/active blends */
|
|
643
|
-
.sel > button, .menu .mi, .stab, .q .qopt, .q .qsend, .
|
|
635
|
+
.sel > button, .menu .mi, .stab, .q .qopt, .q .qsend, .filerow, .filegroup, .icon-btn,
|
|
644
636
|
.btn, .linkbtn, .chip, .cb-copy, .act-btn, .copy-btn, .composer .attach-btn,
|
|
645
637
|
.composer .send-btn, .dd-menu .si, .dd-menu .act, .cmdk-item, .toggle {
|
|
646
638
|
transition: background-color .12s, color .12s, border-color .12s;
|
package/dist/i18n/en.json
CHANGED
|
@@ -1500,13 +1500,12 @@
|
|
|
1500
1500
|
"web.code.copy_failed": "copy failed",
|
|
1501
1501
|
"web.msg.copy": "copy",
|
|
1502
1502
|
"web.msg.copy_md": "copy as markdown",
|
|
1503
|
-
"web.card.open": "open ↗",
|
|
1504
|
-
"web.card.download": "download ⤓",
|
|
1505
|
-
"web.card.copy_path": "copy path",
|
|
1506
1503
|
"web.card.open_title": "Preview in a browser tab",
|
|
1507
|
-
"web.card.download_title": "Download the file",
|
|
1508
1504
|
"web.card.copy_title": "Copy the workspace path",
|
|
1509
1505
|
"web.card.drawio_hint": "Draw.io diagram is XML — preview shows code; open it in draw.io / VS Code to see the scheme.",
|
|
1506
|
+
"web.card.files_one": "{count} file",
|
|
1507
|
+
"web.card.files_few": "{count} files",
|
|
1508
|
+
"web.card.files_many": "{count} files",
|
|
1510
1509
|
"web.card.screenshot": "screenshot",
|
|
1511
1510
|
"web.card.attached_image": "attached image",
|
|
1512
1511
|
"web.card.browser_viewport": "browser · viewport",
|
package/dist/i18n/ru.json
CHANGED
|
@@ -1476,13 +1476,12 @@
|
|
|
1476
1476
|
"web.code.copy_failed": "не скопировано",
|
|
1477
1477
|
"web.msg.copy": "копировать",
|
|
1478
1478
|
"web.msg.copy_md": "копировать как markdown",
|
|
1479
|
-
"web.card.open": "открыть ↗",
|
|
1480
|
-
"web.card.download": "скачать ⤓",
|
|
1481
|
-
"web.card.copy_path": "путь",
|
|
1482
1479
|
"web.card.open_title": "Предпросмотр во вкладке браузера",
|
|
1483
|
-
"web.card.download_title": "Скачать файл",
|
|
1484
1480
|
"web.card.copy_title": "Скопировать путь в рабочей папке",
|
|
1485
1481
|
"web.card.drawio_hint": "Диаграмма draw.io — это XML: предпросмотр покажет код, схему смотри в draw.io / VS Code.",
|
|
1482
|
+
"web.card.files_one": "{count} файл",
|
|
1483
|
+
"web.card.files_few": "{count} файла",
|
|
1484
|
+
"web.card.files_many": "{count} файлов",
|
|
1486
1485
|
"web.card.screenshot": "снимок экрана",
|
|
1487
1486
|
"web.card.attached_image": "прикреплённое изображение",
|
|
1488
1487
|
"web.card.browser_viewport": "браузер · область просмотра",
|
package/dist/main.js
CHANGED
|
@@ -4025,13 +4025,12 @@ Updated: {updatedAt}`,
|
|
|
4025
4025
|
"web.code.copy_failed": "copy failed",
|
|
4026
4026
|
"web.msg.copy": "copy",
|
|
4027
4027
|
"web.msg.copy_md": "copy as markdown",
|
|
4028
|
-
"web.card.open": "open ↗",
|
|
4029
|
-
"web.card.download": "download ⤓",
|
|
4030
|
-
"web.card.copy_path": "copy path",
|
|
4031
4028
|
"web.card.open_title": "Preview in a browser tab",
|
|
4032
|
-
"web.card.download_title": "Download the file",
|
|
4033
4029
|
"web.card.copy_title": "Copy the workspace path",
|
|
4034
4030
|
"web.card.drawio_hint": "Draw.io diagram is XML — preview shows code; open it in draw.io / VS Code to see the scheme.",
|
|
4031
|
+
"web.card.files_one": "{count} file",
|
|
4032
|
+
"web.card.files_few": "{count} files",
|
|
4033
|
+
"web.card.files_many": "{count} files",
|
|
4035
4034
|
"web.card.screenshot": "screenshot",
|
|
4036
4035
|
"web.card.attached_image": "attached image",
|
|
4037
4036
|
"web.card.browser_viewport": "browser · viewport",
|
|
@@ -5905,13 +5904,12 @@ var init_ru = __esm(() => {
|
|
|
5905
5904
|
"web.code.copy_failed": "не скопировано",
|
|
5906
5905
|
"web.msg.copy": "копировать",
|
|
5907
5906
|
"web.msg.copy_md": "копировать как markdown",
|
|
5908
|
-
"web.card.open": "открыть ↗",
|
|
5909
|
-
"web.card.download": "скачать ⤓",
|
|
5910
|
-
"web.card.copy_path": "путь",
|
|
5911
5907
|
"web.card.open_title": "Предпросмотр во вкладке браузера",
|
|
5912
|
-
"web.card.download_title": "Скачать файл",
|
|
5913
5908
|
"web.card.copy_title": "Скопировать путь в рабочей папке",
|
|
5914
5909
|
"web.card.drawio_hint": "Диаграмма draw.io — это XML: предпросмотр покажет код, схему смотри в draw.io / VS Code.",
|
|
5910
|
+
"web.card.files_one": "{count} файл",
|
|
5911
|
+
"web.card.files_few": "{count} файла",
|
|
5912
|
+
"web.card.files_many": "{count} файлов",
|
|
5915
5913
|
"web.card.screenshot": "снимок экрана",
|
|
5916
5914
|
"web.card.attached_image": "прикреплённое изображение",
|
|
5917
5915
|
"web.card.browser_viewport": "браузер · область просмотра",
|
|
@@ -11908,6 +11906,18 @@ var init_walker = __esm(() => {
|
|
|
11908
11906
|
// src/modules/indexer/cache.ts
|
|
11909
11907
|
import { readFileSync as readFileSync14, writeFileSync as writeFileSync10, existsSync as existsSync18, mkdirSync as mkdirSync11, rmSync } from "fs";
|
|
11910
11908
|
import { join as join20 } from "path";
|
|
11909
|
+
function isIndexedFile(v) {
|
|
11910
|
+
if (typeof v !== "object" || v === null)
|
|
11911
|
+
return false;
|
|
11912
|
+
const f = v;
|
|
11913
|
+
return typeof f.path === "string" && typeof f.language === "string" && Array.isArray(f.exports) && typeof f.size === "number";
|
|
11914
|
+
}
|
|
11915
|
+
function isIndexResult(v) {
|
|
11916
|
+
if (typeof v !== "object" || v === null)
|
|
11917
|
+
return false;
|
|
11918
|
+
const r = v;
|
|
11919
|
+
return Array.isArray(r.files) && r.files.every(isIndexedFile) && typeof r.totalSize === "number";
|
|
11920
|
+
}
|
|
11911
11921
|
|
|
11912
11922
|
class IndexCache {
|
|
11913
11923
|
cachePath;
|
|
@@ -11927,7 +11937,7 @@ class IndexCache {
|
|
|
11927
11937
|
return null;
|
|
11928
11938
|
try {
|
|
11929
11939
|
const parsed = JSON.parse(readFileSync14(this.cachePath, "utf-8"));
|
|
11930
|
-
if (parsed.version !== CACHE_VERSION || !parsed.result)
|
|
11940
|
+
if (parsed.version !== CACHE_VERSION || !isIndexResult(parsed.result))
|
|
11931
11941
|
return null;
|
|
11932
11942
|
if (this.root !== undefined && parsed.root !== this.root)
|
|
11933
11943
|
return null;
|