dsh-agy-link 0.4.67 → 0.4.70
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 +124 -0
- package/dist/client.js +217 -43
- package/dist/index.js +820 -117
- package/docs/KNOWN-GAPS.md +40 -5
- package/package.json +2 -1
package/dist/client.js
CHANGED
|
@@ -604,6 +604,58 @@ window.__ModuleLoader__.load({
|
|
|
604
604
|
"status.disabled": "Deshabilitado"
|
|
605
605
|
};
|
|
606
606
|
//#endregion
|
|
607
|
+
//#region src/client/scroll-guard.ts
|
|
608
|
+
let isScrolling = false;
|
|
609
|
+
let scrollTimer = null;
|
|
610
|
+
let listenerAttached = false;
|
|
611
|
+
const settleListeners = /* @__PURE__ */ new Set();
|
|
612
|
+
function onScrollActivity() {
|
|
613
|
+
isScrolling = true;
|
|
614
|
+
if (scrollTimer !== null) clearTimeout(scrollTimer);
|
|
615
|
+
scrollTimer = setTimeout(() => {
|
|
616
|
+
isScrolling = false;
|
|
617
|
+
scrollTimer = null;
|
|
618
|
+
for (const fn of settleListeners) try {
|
|
619
|
+
fn();
|
|
620
|
+
} catch {}
|
|
621
|
+
}, 150);
|
|
622
|
+
}
|
|
623
|
+
function isScrollActive() {
|
|
624
|
+
return isScrolling;
|
|
625
|
+
}
|
|
626
|
+
/** Register a callback to run once scrolling has settled. Returns an unregister function. */
|
|
627
|
+
function onScrollSettle(fn) {
|
|
628
|
+
ensureScrollListener();
|
|
629
|
+
settleListeners.add(fn);
|
|
630
|
+
return () => {
|
|
631
|
+
settleListeners.delete(fn);
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
function ensureScrollListener() {
|
|
635
|
+
if (typeof window === "undefined" || typeof window.addEventListener !== "function" || listenerAttached) return;
|
|
636
|
+
listenerAttached = true;
|
|
637
|
+
window.addEventListener("scroll", onScrollActivity, {
|
|
638
|
+
passive: true,
|
|
639
|
+
capture: true
|
|
640
|
+
});
|
|
641
|
+
window.addEventListener("wheel", onScrollActivity, {
|
|
642
|
+
passive: true,
|
|
643
|
+
capture: true
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
function disposeScrollListener() {
|
|
647
|
+
if (typeof window === "undefined" || typeof window.removeEventListener !== "function" || !listenerAttached) return;
|
|
648
|
+
window.removeEventListener("scroll", onScrollActivity, true);
|
|
649
|
+
window.removeEventListener("wheel", onScrollActivity, true);
|
|
650
|
+
listenerAttached = false;
|
|
651
|
+
if (scrollTimer !== null) {
|
|
652
|
+
clearTimeout(scrollTimer);
|
|
653
|
+
scrollTimer = null;
|
|
654
|
+
}
|
|
655
|
+
settleListeners.clear();
|
|
656
|
+
isScrolling = false;
|
|
657
|
+
}
|
|
658
|
+
//#endregion
|
|
607
659
|
//#region src/client/decisions.ts
|
|
608
660
|
const DECISION_CSS = `
|
|
609
661
|
.agy-decision-container {
|
|
@@ -740,14 +792,34 @@ window.__ModuleLoader__.load({
|
|
|
740
792
|
return options;
|
|
741
793
|
}
|
|
742
794
|
/**
|
|
743
|
-
* Scan DOM for decision prompts and mount interactive choice chips
|
|
795
|
+
* Scan DOM for decision prompts and mount interactive choice chips.
|
|
796
|
+
*
|
|
797
|
+
* IDEMPOTENCE (incident 2026-10-23 — "scroll back and forth = insane flashing"):
|
|
798
|
+
* DSH renders these containers inside a TanStack-Virtual list with dynamic
|
|
799
|
+
* measurement (`measureElement` + `anchorTo:"start"`). Injecting a toolbar
|
|
800
|
+
* changes the measured item height, which makes the virtualizer rewrite
|
|
801
|
+
* scrollTop; the resulting re-render rebuilds the markdown/heading nodes
|
|
802
|
+
* (dropping the per-HEADING guard) and this scanner injected the toolbar
|
|
803
|
+
* again — growth on every scroll pass, i.e. a flashing flywheel.
|
|
804
|
+
*
|
|
805
|
+
* Two independent guards now make injection strictly once-per-container:
|
|
806
|
+
* 1. `data-agy-decisions-scanned` on the container, written on EVERY pass
|
|
807
|
+
* (it used to be read here but never assigned anywhere in the codebase),
|
|
808
|
+
* 2. "a toolbar already exists in this container" — survives React
|
|
809
|
+
* rebuilding the heading nodes during a virtualizer remount.
|
|
744
810
|
*/
|
|
745
811
|
function scanAndEnhanceDecisions() {
|
|
746
812
|
if (typeof document === "undefined") return;
|
|
813
|
+
if (isScrollActive()) return;
|
|
747
814
|
const markdownContainers = document.querySelectorAll("div[class*=\"_markdown_\"], [data-markdown-variant]");
|
|
748
815
|
for (let i = 0; i < markdownContainers.length; i++) {
|
|
749
816
|
const container = markdownContainers[i];
|
|
750
817
|
if (!container || container.dataset.agyDecisionsScanned === "true") continue;
|
|
818
|
+
if (container.querySelector(".agy-decision-container")) {
|
|
819
|
+
container.dataset.agyDecisionsScanned = "true";
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
container.dataset.agyDecisionsScanned = "true";
|
|
751
823
|
const headings = container.querySelectorAll("h2, h3, h4, p > strong");
|
|
752
824
|
for (let j = 0; j < headings.length; j++) {
|
|
753
825
|
const heading = headings[j];
|
|
@@ -804,12 +876,26 @@ window.__ModuleLoader__.load({
|
|
|
804
876
|
}
|
|
805
877
|
}
|
|
806
878
|
let decisionChipsDispose = null;
|
|
879
|
+
let decisionScanTimer = null;
|
|
880
|
+
let unbindDecisionSettle = null;
|
|
881
|
+
function scheduleDecisionScan() {
|
|
882
|
+
if (isScrollActive()) return;
|
|
883
|
+
if (decisionScanTimer !== null) return;
|
|
884
|
+
decisionScanTimer = setTimeout(() => {
|
|
885
|
+
decisionScanTimer = null;
|
|
886
|
+
if (isScrollActive()) return;
|
|
887
|
+
scanAndEnhanceDecisions();
|
|
888
|
+
}, 100);
|
|
889
|
+
}
|
|
807
890
|
function installDecisionChips() {
|
|
808
891
|
if (typeof window === "undefined" || typeof document === "undefined") return () => {};
|
|
809
892
|
decisionChipsDispose?.();
|
|
810
893
|
injectStyles$3();
|
|
811
894
|
const observer = new MutationObserver(() => {
|
|
812
|
-
|
|
895
|
+
scheduleDecisionScan();
|
|
896
|
+
});
|
|
897
|
+
unbindDecisionSettle = onScrollSettle(() => {
|
|
898
|
+
scheduleDecisionScan();
|
|
813
899
|
});
|
|
814
900
|
const setup = () => {
|
|
815
901
|
if (document.body) {
|
|
@@ -824,6 +910,14 @@ window.__ModuleLoader__.load({
|
|
|
824
910
|
else setup();
|
|
825
911
|
const dispose = () => {
|
|
826
912
|
observer.disconnect();
|
|
913
|
+
if (unbindDecisionSettle !== null) {
|
|
914
|
+
unbindDecisionSettle();
|
|
915
|
+
unbindDecisionSettle = null;
|
|
916
|
+
}
|
|
917
|
+
if (decisionScanTimer !== null) {
|
|
918
|
+
clearTimeout(decisionScanTimer);
|
|
919
|
+
decisionScanTimer = null;
|
|
920
|
+
}
|
|
827
921
|
window.removeEventListener("DOMContentLoaded", setup);
|
|
828
922
|
if (decisionChipsDispose === dispose) decisionChipsDispose = null;
|
|
829
923
|
};
|
|
@@ -1194,6 +1288,7 @@ window.__ModuleLoader__.load({
|
|
|
1194
1288
|
*/
|
|
1195
1289
|
function scanAndEnhanceImages() {
|
|
1196
1290
|
if (typeof document === "undefined") return;
|
|
1291
|
+
if (isScrollActive()) return;
|
|
1197
1292
|
const origin = getOrigin();
|
|
1198
1293
|
if (origin && !origin.includes("127.0.0.1") && !origin.includes("localhost")) {
|
|
1199
1294
|
const images = document.querySelectorAll("img[src*=\"/plugins/agy-link/media\"]");
|
|
@@ -1283,12 +1378,26 @@ window.__ModuleLoader__.load({
|
|
|
1283
1378
|
}
|
|
1284
1379
|
}
|
|
1285
1380
|
let imageEnhancerDispose = null;
|
|
1381
|
+
let imageScanTimer = null;
|
|
1382
|
+
let unbindImageSettle = null;
|
|
1383
|
+
function scheduleImageScan() {
|
|
1384
|
+
if (isScrollActive()) return;
|
|
1385
|
+
if (imageScanTimer !== null) return;
|
|
1386
|
+
imageScanTimer = setTimeout(() => {
|
|
1387
|
+
imageScanTimer = null;
|
|
1388
|
+
if (isScrollActive()) return;
|
|
1389
|
+
scanAndEnhanceImages();
|
|
1390
|
+
}, 100);
|
|
1391
|
+
}
|
|
1286
1392
|
function installImageEnhancer() {
|
|
1287
1393
|
if (typeof window === "undefined" || typeof document === "undefined") return () => {};
|
|
1288
1394
|
imageEnhancerDispose?.();
|
|
1289
1395
|
injectStyles$2();
|
|
1290
1396
|
const observer = new MutationObserver(() => {
|
|
1291
|
-
|
|
1397
|
+
scheduleImageScan();
|
|
1398
|
+
});
|
|
1399
|
+
unbindImageSettle = onScrollSettle(() => {
|
|
1400
|
+
scheduleImageScan();
|
|
1292
1401
|
});
|
|
1293
1402
|
const setup = () => {
|
|
1294
1403
|
if (document.body) {
|
|
@@ -1303,6 +1412,14 @@ window.__ModuleLoader__.load({
|
|
|
1303
1412
|
else setup();
|
|
1304
1413
|
const dispose = () => {
|
|
1305
1414
|
observer.disconnect();
|
|
1415
|
+
if (unbindImageSettle !== null) {
|
|
1416
|
+
unbindImageSettle();
|
|
1417
|
+
unbindImageSettle = null;
|
|
1418
|
+
}
|
|
1419
|
+
if (imageScanTimer !== null) {
|
|
1420
|
+
clearTimeout(imageScanTimer);
|
|
1421
|
+
imageScanTimer = null;
|
|
1422
|
+
}
|
|
1306
1423
|
window.removeEventListener("DOMContentLoaded", setup);
|
|
1307
1424
|
if (imageEnhancerDispose === dispose) imageEnhancerDispose = null;
|
|
1308
1425
|
};
|
|
@@ -1316,12 +1433,23 @@ window.__ModuleLoader__.load({
|
|
|
1316
1433
|
if (mod === void 0 || mod === null) throw new Error("dsh-agy-link toolview requires react at render time (browser host)");
|
|
1317
1434
|
return mod;
|
|
1318
1435
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1436
|
+
/** Cache user-expanded tool cards across TanStack Virtualizer remount cycles */
|
|
1437
|
+
const expandedToolBlockKeys = /* @__PURE__ */ new Set();
|
|
1438
|
+
function useToggle(initial = false, key) {
|
|
1439
|
+
const R2 = react();
|
|
1440
|
+
const init = key ? expandedToolBlockKeys.has(key) : initial;
|
|
1441
|
+
const [value, setValue] = R2.useState(init);
|
|
1442
|
+
const toggle = () => {
|
|
1443
|
+
setValue((prev) => {
|
|
1444
|
+
const next = !prev;
|
|
1445
|
+
if (key) {
|
|
1446
|
+
if (next) expandedToolBlockKeys.add(key);
|
|
1447
|
+
else expandedToolBlockKeys.delete(key);
|
|
1448
|
+
}
|
|
1449
|
+
return next;
|
|
1450
|
+
});
|
|
1451
|
+
};
|
|
1452
|
+
return [value, toggle];
|
|
1325
1453
|
}
|
|
1326
1454
|
function hx(type, props, ...children) {
|
|
1327
1455
|
return react().createElement(type, props, ...children);
|
|
@@ -2096,12 +2224,12 @@ window.__ModuleLoader__.load({
|
|
|
2096
2224
|
}
|
|
2097
2225
|
}
|
|
2098
2226
|
function AgyMirrorToolView(props) {
|
|
2099
|
-
const [open, setOpen] = useToggle(false);
|
|
2100
2227
|
const h = hx;
|
|
2101
|
-
const { block, cwd, home, openFile, inspect, t } = props;
|
|
2228
|
+
const { block, cwd, home, openFile, inspect, t, callId } = props;
|
|
2102
2229
|
const settle = isSettled(block);
|
|
2103
2230
|
const err = isError(block);
|
|
2104
2231
|
const model = mirrorCardModel(block, cwd, home);
|
|
2232
|
+
const [open, setOpen] = useToggle(false, callId ? `call:${callId}` : `${model.kind}:${model.tool ?? ""}:${model.title}:${model.path ?? model.command ?? ""}`);
|
|
2105
2233
|
const state = !settle ? "run" : err ? "err" : "ok";
|
|
2106
2234
|
const title = displayTitle(model, t);
|
|
2107
2235
|
const summary = displaySummary(model, cwd, home);
|
|
@@ -2217,10 +2345,10 @@ window.__ModuleLoader__.load({
|
|
|
2217
2345
|
* friends) so an ordinary Code Mode call reads exactly like a native tool row.
|
|
2218
2346
|
*/
|
|
2219
2347
|
function AgyCodeToolView(props) {
|
|
2220
|
-
const [open, setOpen] = useToggle(false);
|
|
2221
2348
|
const h = hx;
|
|
2222
|
-
const { block, inspect, t } = props ?? {};
|
|
2349
|
+
const { block, inspect, t, callId } = props ?? {};
|
|
2223
2350
|
const model = runCodeModel(block);
|
|
2351
|
+
const [open, setOpen] = useToggle(false, callId ? `call:${callId}` : `code:${model.title}`);
|
|
2224
2352
|
const settle = block !== void 0 && isSettled(block);
|
|
2225
2353
|
const err = block !== void 0 && isError(block);
|
|
2226
2354
|
const state = !settle ? "run" : err ? "err" : "ok";
|
|
@@ -3207,14 +3335,17 @@ body > [id^="iagy-mmd"] .error-icon {
|
|
|
3207
3335
|
}
|
|
3208
3336
|
let scanTimer$1 = null;
|
|
3209
3337
|
function scheduleScan$1() {
|
|
3338
|
+
if (isScrollActive()) return;
|
|
3210
3339
|
if (scanTimer$1 !== null) return;
|
|
3211
3340
|
scanTimer$1 = setTimeout(() => {
|
|
3212
3341
|
scanTimer$1 = null;
|
|
3342
|
+
if (isScrollActive()) return;
|
|
3213
3343
|
scan();
|
|
3214
3344
|
}, 150);
|
|
3215
3345
|
}
|
|
3216
3346
|
function scan() {
|
|
3217
3347
|
if (typeof document === "undefined") return;
|
|
3348
|
+
if (isScrollActive()) return;
|
|
3218
3349
|
cleanupTempElements();
|
|
3219
3350
|
const blocks = document.querySelectorAll(".md-code-block");
|
|
3220
3351
|
for (let i = 0; i < blocks.length; i++) {
|
|
@@ -3232,6 +3363,7 @@ body > [id^="iagy-mmd"] .error-icon {
|
|
|
3232
3363
|
}
|
|
3233
3364
|
let activeObserver$1 = null;
|
|
3234
3365
|
let domContentLoadedHandler$1 = null;
|
|
3366
|
+
let unbindMermaidSettle = null;
|
|
3235
3367
|
/**
|
|
3236
3368
|
* Install the mermaid overlay. Returns a disposer that disconnects the
|
|
3237
3369
|
* MutationObserver and cancels pending setup — wrap it in ctx.effect so a
|
|
@@ -3246,6 +3378,9 @@ body > [id^="iagy-mmd"] .error-icon {
|
|
|
3246
3378
|
scheduleScan$1();
|
|
3247
3379
|
});
|
|
3248
3380
|
activeObserver$1 = observer;
|
|
3381
|
+
unbindMermaidSettle = onScrollSettle(() => {
|
|
3382
|
+
scheduleScan$1();
|
|
3383
|
+
});
|
|
3249
3384
|
const setup = () => {
|
|
3250
3385
|
if (document.body) {
|
|
3251
3386
|
cleanupTempElements();
|
|
@@ -3268,6 +3403,10 @@ body > [id^="iagy-mmd"] .error-icon {
|
|
|
3268
3403
|
activeObserver$1.disconnect();
|
|
3269
3404
|
activeObserver$1 = null;
|
|
3270
3405
|
}
|
|
3406
|
+
if (unbindMermaidSettle !== null) {
|
|
3407
|
+
unbindMermaidSettle();
|
|
3408
|
+
unbindMermaidSettle = null;
|
|
3409
|
+
}
|
|
3271
3410
|
if (domContentLoadedHandler$1 !== null) {
|
|
3272
3411
|
window.removeEventListener("DOMContentLoaded", domContentLoadedHandler$1);
|
|
3273
3412
|
domContentLoadedHandler$1 = null;
|
|
@@ -3334,42 +3473,57 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3334
3473
|
}
|
|
3335
3474
|
}
|
|
3336
3475
|
/**
|
|
3476
|
+
* Persistent cache of handled thinking turns (keyed by turn fingerprint).
|
|
3477
|
+
* Survived across TanStack Virtualizer unmount/remount cycles.
|
|
3478
|
+
*/
|
|
3479
|
+
const handledTurns = /* @__PURE__ */ new Set();
|
|
3480
|
+
function getTurnKey(root, text) {
|
|
3481
|
+
const parent = root.closest("[data-message-id], [data-item-index], [data-index], [data-role=\"message-content\"]");
|
|
3482
|
+
return `${parent?.getAttribute("data-message-id") ?? parent?.getAttribute("data-index") ?? parent?.getAttribute("data-item-index") ?? ""}:${text.slice(0, 100)}`;
|
|
3483
|
+
}
|
|
3484
|
+
/**
|
|
3337
3485
|
* Try to expand a thinking row once when its body contains real prose
|
|
3338
3486
|
* (not a bare [agy thinking turn · N tokens] chip). Uses the host's own
|
|
3339
3487
|
* disclosure control when present — never mutates thinkBody children.
|
|
3340
|
-
*
|
|
3341
|
-
* A row the user manually COLLAPSED is recorded in a WeakSet keyed by the
|
|
3342
|
-
* row element; React keeps the same element identity across its own
|
|
3343
|
-
* re-renders, so the scanner stops re-expanding it (the old data attribute
|
|
3344
|
-
* survived only until React rebuilt the node).
|
|
3345
3488
|
*/
|
|
3346
|
-
const userCollapsed = typeof WeakSet !== "undefined" ? /* @__PURE__ */ new WeakSet() : null;
|
|
3347
3489
|
function tryExpandThinkRow(root) {
|
|
3348
3490
|
if (root.dataset.agyExpanded === "true") return;
|
|
3349
|
-
if (userCollapsed?.has(root) === true) return;
|
|
3350
3491
|
const body = root.querySelector("[class*=\"thinkBody\"]");
|
|
3351
3492
|
if (!body) return;
|
|
3352
3493
|
const text = (body.textContent ?? "").trim();
|
|
3353
3494
|
if (text === "") return;
|
|
3354
3495
|
if (/^\[agy(?: thinking turn)?(?: · \d+ (?:thinking )?tokens)?\]\s*$/.test(text)) return;
|
|
3355
|
-
const
|
|
3356
|
-
if (
|
|
3496
|
+
const turnKey = getTurnKey(root, text);
|
|
3497
|
+
if (handledTurns.has(turnKey)) {
|
|
3357
3498
|
root.dataset.agyExpanded = "true";
|
|
3358
3499
|
return;
|
|
3359
3500
|
}
|
|
3360
3501
|
const toggle = root.querySelector("[data-disclosure-row]") ?? root.querySelector("[class*=\"disclosure\"]") ?? root.querySelector("button");
|
|
3361
|
-
if (toggle)
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3502
|
+
if (!toggle) return;
|
|
3503
|
+
if (!toggle.dataset.agyClickBound) {
|
|
3504
|
+
toggle.dataset.agyClickBound = "true";
|
|
3505
|
+
toggle.addEventListener("click", () => {
|
|
3506
|
+
handledTurns.add(turnKey);
|
|
3507
|
+
}, {
|
|
3508
|
+
capture: true,
|
|
3509
|
+
passive: true
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
if (isScrollActive()) return;
|
|
3513
|
+
if (body.clientHeight > 8) {
|
|
3514
|
+
handledTurns.add(turnKey);
|
|
3515
|
+
root.dataset.agyExpanded = "true";
|
|
3516
|
+
return;
|
|
3369
3517
|
}
|
|
3518
|
+
handledTurns.add(turnKey);
|
|
3519
|
+
root.dataset.agyExpanded = "true";
|
|
3520
|
+
try {
|
|
3521
|
+
toggle.click();
|
|
3522
|
+
} catch {}
|
|
3370
3523
|
}
|
|
3371
3524
|
function scanThinkingRows() {
|
|
3372
3525
|
if (typeof document === "undefined") return;
|
|
3526
|
+
if (isScrollActive()) return;
|
|
3373
3527
|
const rows = document.querySelectorAll("div[data-variant=\"think\"]");
|
|
3374
3528
|
for (let i = 0; i < rows.length; i++) {
|
|
3375
3529
|
const row = rows[i];
|
|
@@ -3378,14 +3532,17 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3378
3532
|
}
|
|
3379
3533
|
let scanTimer = null;
|
|
3380
3534
|
function scheduleScan() {
|
|
3535
|
+
if (isScrollActive()) return;
|
|
3381
3536
|
if (scanTimer !== null) return;
|
|
3382
3537
|
scanTimer = setTimeout(() => {
|
|
3383
3538
|
scanTimer = null;
|
|
3539
|
+
if (isScrollActive()) return;
|
|
3384
3540
|
scanThinkingRows();
|
|
3385
|
-
},
|
|
3541
|
+
}, 150);
|
|
3386
3542
|
}
|
|
3387
3543
|
let activeObserver = null;
|
|
3388
3544
|
let domContentLoadedHandler = null;
|
|
3545
|
+
let unbindSettle = null;
|
|
3389
3546
|
/**
|
|
3390
3547
|
* Install the auto-expand reasoning overlay. Returns a disposer that
|
|
3391
3548
|
* disconnects the MutationObserver — wrap it in ctx.effect so a plugin
|
|
@@ -3399,6 +3556,9 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3399
3556
|
scheduleScan();
|
|
3400
3557
|
});
|
|
3401
3558
|
activeObserver = observer;
|
|
3559
|
+
unbindSettle = onScrollSettle(() => {
|
|
3560
|
+
scheduleScan();
|
|
3561
|
+
});
|
|
3402
3562
|
const setupObserver = () => {
|
|
3403
3563
|
if (document.body) {
|
|
3404
3564
|
observer.observe(document.body, {
|
|
@@ -3420,6 +3580,10 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3420
3580
|
activeObserver.disconnect();
|
|
3421
3581
|
activeObserver = null;
|
|
3422
3582
|
}
|
|
3583
|
+
if (unbindSettle !== null) {
|
|
3584
|
+
unbindSettle();
|
|
3585
|
+
unbindSettle = null;
|
|
3586
|
+
}
|
|
3423
3587
|
if (domContentLoadedHandler !== null) {
|
|
3424
3588
|
window.removeEventListener("DOMContentLoaded", domContentLoadedHandler);
|
|
3425
3589
|
domContentLoadedHandler = null;
|
|
@@ -3859,8 +4023,12 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3859
4023
|
scheduled = false;
|
|
3860
4024
|
if (disposed) return;
|
|
3861
4025
|
const wanted = resolveLabel();
|
|
3862
|
-
for (const row of Array.from(document.querySelectorAll(NAV_ROW_SELECTOR)))
|
|
3863
|
-
|
|
4026
|
+
for (const row of Array.from(document.querySelectorAll(NAV_ROW_SELECTOR))) {
|
|
4027
|
+
const shouldMark = isOwnNavRow(row.textContent, wanted);
|
|
4028
|
+
if (shouldMark === row.hasAttribute(NAV_ICON_MARKER)) continue;
|
|
4029
|
+
if (shouldMark) row.setAttribute(NAV_ICON_MARKER, "");
|
|
4030
|
+
else row.removeAttribute(NAV_ICON_MARKER);
|
|
4031
|
+
}
|
|
3864
4032
|
};
|
|
3865
4033
|
const schedule = () => {
|
|
3866
4034
|
if (scheduled || disposed) return;
|
|
@@ -3872,7 +4040,7 @@ div[data-variant="think"] [class*="thinkBody"] {
|
|
|
3872
4040
|
observer.observe(document.body, {
|
|
3873
4041
|
childList: true,
|
|
3874
4042
|
subtree: true,
|
|
3875
|
-
|
|
4043
|
+
attributeFilter: [NAV_ICON_MARKER]
|
|
3876
4044
|
});
|
|
3877
4045
|
return () => {
|
|
3878
4046
|
disposed = true;
|
|
@@ -4871,6 +5039,9 @@ body.dark,
|
|
|
4871
5039
|
const accounts = pool?.accounts ?? [];
|
|
4872
5040
|
const isAuthed = authPhase === "ok" || accounts.length > 0;
|
|
4873
5041
|
const isBusy = loadingAction !== null;
|
|
5042
|
+
/** Busy for a per-account action; also blocks while a global action runs. */
|
|
5043
|
+
const accountActionBusy = (acc, action) => loadingAction === `${action}:${acc.id}`;
|
|
5044
|
+
isBusy && loadingAction?.includes(":");
|
|
4874
5045
|
const renderSpinner = () => h("span", { className: "agy-spinner" });
|
|
4875
5046
|
const renderToastBanner = () => {
|
|
4876
5047
|
if (!toast) return null;
|
|
@@ -4920,8 +5091,7 @@ body.dark,
|
|
|
4920
5091
|
"aria-label": t("aria.closeToast")
|
|
4921
5092
|
}, uiIcon("x", 12)));
|
|
4922
5093
|
};
|
|
4923
|
-
const
|
|
4924
|
-
const renderQuotaBar = (label, familyKey, acc) => renderFamilyQuotaBar(t, label, familyKey, acc, isRefreshingQuota);
|
|
5094
|
+
const renderQuotaBar = (label, familyKey, acc) => renderFamilyQuotaBar(t, label, familyKey, acc, loadingAction === "refresh-quota" || loadingAction === `refresh:${acc.id}`);
|
|
4925
5095
|
const authoritativeActiveId = pool?.activeAccountId ?? pool?.lastActiveAccountId ?? pool?.activeAccountIds?.google ?? pool?.primaryAccountId;
|
|
4926
5096
|
const activeAccountId = optimisticActiveId ?? authoritativeActiveId;
|
|
4927
5097
|
const renderedAccountCards = accounts.map((acc) => {
|
|
@@ -5132,7 +5302,7 @@ body.dark,
|
|
|
5132
5302
|
padding: "2px 6px"
|
|
5133
5303
|
},
|
|
5134
5304
|
title: acc.enabled ? t("account.disableTitle") : t("account.enableTitle"),
|
|
5135
|
-
disabled:
|
|
5305
|
+
disabled: accountActionBusy(acc, "enable"),
|
|
5136
5306
|
onClick: () => void toggleAccountEnabled(acc)
|
|
5137
5307
|
}, loadingAction === `enable:${acc.id}` ? [renderSpinner(), acc.enabled ? t("account.disable") : t("account.enable")] : [uiIcon("power", 10, acc.enabled ? "var(--agy-text-secondary)" : "#ffffff"), ` ${acc.enabled ? t("account.disable") : t("account.enable")}`]), h("button", {
|
|
5138
5308
|
type: "button",
|
|
@@ -5144,7 +5314,7 @@ body.dark,
|
|
|
5144
5314
|
padding: "2px 6px"
|
|
5145
5315
|
},
|
|
5146
5316
|
title: t("account.syncTitle"),
|
|
5147
|
-
disabled:
|
|
5317
|
+
disabled: accountActionBusy(acc, "refresh"),
|
|
5148
5318
|
onClick: () => void refreshQuota(acc.id)
|
|
5149
5319
|
}, loadingAction === `refresh:${acc.id}` ? [renderSpinner(), t("account.syncing")] : [uiIcon("refresh", 10), ` ${t("account.sync")}`]), acc.enabled && !isInUse ? h("button", {
|
|
5150
5320
|
type: "button",
|
|
@@ -5156,7 +5326,7 @@ body.dark,
|
|
|
5156
5326
|
padding: "2px 6px"
|
|
5157
5327
|
},
|
|
5158
5328
|
title: t("account.setActive"),
|
|
5159
|
-
disabled:
|
|
5329
|
+
disabled: accountActionBusy(acc, "active"),
|
|
5160
5330
|
onClick: () => void setActiveAccount(acc.id)
|
|
5161
5331
|
}, loadingAction === `active:${acc.id}` ? [renderSpinner(), t("account.settingActive")] : [uiIcon("zap", 10), ` ${t("account.setActive")}`]) : null, h("button", {
|
|
5162
5332
|
type: "button",
|
|
@@ -5172,7 +5342,7 @@ body.dark,
|
|
|
5172
5342
|
fontSize: "11px",
|
|
5173
5343
|
padding: "2px 6px"
|
|
5174
5344
|
},
|
|
5175
|
-
disabled:
|
|
5345
|
+
disabled: accountActionBusy(acc, "proxy"),
|
|
5176
5346
|
onClick: () => setEditingProxyId(isEditingProxy ? null : acc.id)
|
|
5177
5347
|
}, [uiIcon("globe", 10), ` ${t("proxy.button")}`]), accounts.length > 1 ? h("button", {
|
|
5178
5348
|
type: "button",
|
|
@@ -5184,7 +5354,7 @@ body.dark,
|
|
|
5184
5354
|
},
|
|
5185
5355
|
title: t("account.removeTitle"),
|
|
5186
5356
|
"aria-label": t("account.removeTitle"),
|
|
5187
|
-
disabled:
|
|
5357
|
+
disabled: accountActionBusy(acc, "remove"),
|
|
5188
5358
|
onClick: () => void removeAccount(acc.id, displayAlias)
|
|
5189
5359
|
}, loadingAction === `remove:${acc.id}` ? renderSpinner() : uiIcon("trash", 11, "var(--agy-danger-text)")) : null)), h("div", {
|
|
5190
5360
|
key: "agy-body",
|
|
@@ -5273,7 +5443,7 @@ body.dark,
|
|
|
5273
5443
|
background: "var(--agy-warn-btn-bg)",
|
|
5274
5444
|
gap: "4px"
|
|
5275
5445
|
},
|
|
5276
|
-
disabled:
|
|
5446
|
+
disabled: accountActionBusy(acc, "clearCooldown"),
|
|
5277
5447
|
onClick: () => void clearCooldown(acc.id)
|
|
5278
5448
|
}, loadingAction === `clearCooldown:${acc.id}` ? [renderSpinner(), ""] : [uiIcon("zap", 11, "var(--agy-warn-btn-text)"), ` ${t("account.clearCooldown")}`])) : null, isEditingProxy ? h("div", {
|
|
5279
5449
|
key: "proxy-body",
|
|
@@ -5300,7 +5470,7 @@ body.dark,
|
|
|
5300
5470
|
type: "button",
|
|
5301
5471
|
className: "agy-btn",
|
|
5302
5472
|
style: S.btnPrimary,
|
|
5303
|
-
disabled:
|
|
5473
|
+
disabled: accountActionBusy(acc, "proxy"),
|
|
5304
5474
|
onClick: () => void saveProxy(acc.id)
|
|
5305
5475
|
}, loadingAction === `proxy:${acc.id}` ? [renderSpinner(), t("proxy.save")] : t("proxy.save")), h("button", {
|
|
5306
5476
|
type: "button",
|
|
@@ -5745,6 +5915,10 @@ body.dark,
|
|
|
5745
5915
|
return h(AgySettingsSection, { isModal: false });
|
|
5746
5916
|
});
|
|
5747
5917
|
});
|
|
5918
|
+
ctx.effect(() => {
|
|
5919
|
+
ensureScrollListener();
|
|
5920
|
+
return disposeScrollListener;
|
|
5921
|
+
});
|
|
5748
5922
|
installAgyToolView(ctx);
|
|
5749
5923
|
ctx.effect(() => installAutoExpandReasoning());
|
|
5750
5924
|
ctx.effect(() => installMermaidRendering());
|