agent-yes 1.98.0 → 1.100.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/dist/SUPPORTED_CLIS-C-cenkTG.js +8 -0
- package/dist/{SUPPORTED_CLIS-C0a9K6I5.js → SUPPORTED_CLIS-DIHMEdRx.js} +2 -2
- package/dist/cli.js +3 -3
- package/dist/index.js +2 -2
- package/dist/{serve-DPY37v0u.js → serve-C4fZSjh9.js} +22 -14
- package/dist/{share-D-r6y3xD.js → share-BsCeIfQM.js} +19 -2
- package/dist/{subcommands-D4Muugfr.js → subcommands-BKY3nQV4.js} +2 -2
- package/dist/{subcommands-fCkYXyTe.js → subcommands-dnjUZ9nY.js} +1 -1
- package/dist/{ts-BuFWTNL9.js → ts-CUn393DD.js} +2 -2
- package/dist/{versionChecker-CpNUvHBx.js → versionChecker-BWdncsn6.js} +2 -2
- package/lab/ui/index.html +221 -98
- package/lab/ui/room-client.js +328 -239
- package/package.json +1 -1
- package/ts/serve.ts +38 -10
- package/ts/share.ts +29 -1
- package/dist/SUPPORTED_CLIS-BtLklR5y.js +0 -8
package/lab/ui/index.html
CHANGED
|
@@ -52,14 +52,34 @@
|
|
|
52
52
|
flex children actually shrink — without it overflow:auto never engages). */
|
|
53
53
|
.app {
|
|
54
54
|
display: grid;
|
|
55
|
-
grid-template-columns:
|
|
55
|
+
grid-template-columns: var(--leftw, 42%) 1px 1fr;
|
|
56
56
|
grid-template-rows: minmax(0, 1fr);
|
|
57
57
|
height: 100vh;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/* VSCode-style splitter: the visible divider stays 1px, but a ::before
|
|
61
|
+
overlay widens the pointer hit area to ~11px so it's easy to grab. */
|
|
62
|
+
.splitter {
|
|
63
|
+
position: relative;
|
|
64
|
+
background: var(--line);
|
|
65
|
+
cursor: col-resize;
|
|
66
|
+
z-index: 5;
|
|
67
|
+
}
|
|
68
|
+
.splitter::before {
|
|
69
|
+
content: "";
|
|
70
|
+
position: absolute;
|
|
71
|
+
top: 0;
|
|
72
|
+
bottom: 0;
|
|
73
|
+
left: -5px;
|
|
74
|
+
right: -5px;
|
|
75
|
+
}
|
|
76
|
+
.splitter:hover,
|
|
77
|
+
.splitter.drag {
|
|
78
|
+
background: var(--accent);
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
/* ---- left: list ---- */
|
|
61
82
|
.left {
|
|
62
|
-
border-right: 1px solid var(--line);
|
|
63
83
|
display: flex;
|
|
64
84
|
flex-direction: column;
|
|
65
85
|
min-width: 0;
|
|
@@ -316,6 +336,24 @@
|
|
|
316
336
|
.newbtn:hover {
|
|
317
337
|
filter: brightness(1.12);
|
|
318
338
|
}
|
|
339
|
+
.viewbtn {
|
|
340
|
+
background: transparent;
|
|
341
|
+
border: 1px solid var(--line);
|
|
342
|
+
border-radius: 7px;
|
|
343
|
+
color: var(--muted);
|
|
344
|
+
font-size: 11.5px;
|
|
345
|
+
padding: 3px 9px;
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
line-height: 1.6;
|
|
348
|
+
}
|
|
349
|
+
.viewbtn:hover {
|
|
350
|
+
color: var(--fg);
|
|
351
|
+
border-color: var(--accent);
|
|
352
|
+
}
|
|
353
|
+
.viewbtn.on {
|
|
354
|
+
color: var(--accent);
|
|
355
|
+
border-color: var(--accent);
|
|
356
|
+
}
|
|
319
357
|
.lcard .nfield {
|
|
320
358
|
margin-top: 12px;
|
|
321
359
|
}
|
|
@@ -428,6 +466,39 @@
|
|
|
428
466
|
text-overflow: ellipsis;
|
|
429
467
|
white-space: nowrap;
|
|
430
468
|
}
|
|
469
|
+
/* compact view: one line per agent — dot + cli + live title (or prompt), age */
|
|
470
|
+
.row.crow {
|
|
471
|
+
display: flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
gap: 8px;
|
|
474
|
+
padding: 6px 18px;
|
|
475
|
+
}
|
|
476
|
+
.crow .cident {
|
|
477
|
+
font-family: var(--mono);
|
|
478
|
+
font-size: 11.5px;
|
|
479
|
+
color: var(--green);
|
|
480
|
+
flex: none;
|
|
481
|
+
}
|
|
482
|
+
.crow .cname {
|
|
483
|
+
font-weight: 600;
|
|
484
|
+
color: var(--purple);
|
|
485
|
+
flex: none;
|
|
486
|
+
}
|
|
487
|
+
.crow .ctitle {
|
|
488
|
+
flex: 1;
|
|
489
|
+
min-width: 0;
|
|
490
|
+
font-size: 12.5px;
|
|
491
|
+
overflow: hidden;
|
|
492
|
+
text-overflow: ellipsis;
|
|
493
|
+
white-space: nowrap;
|
|
494
|
+
}
|
|
495
|
+
.crow .ctitle.dim {
|
|
496
|
+
color: var(--muted);
|
|
497
|
+
}
|
|
498
|
+
.crow .age {
|
|
499
|
+
margin-left: 0;
|
|
500
|
+
flex: none;
|
|
501
|
+
}
|
|
431
502
|
.rowtags {
|
|
432
503
|
display: flex;
|
|
433
504
|
flex-wrap: wrap;
|
|
@@ -509,50 +580,6 @@
|
|
|
509
580
|
color: var(--muted);
|
|
510
581
|
font-size: 14px;
|
|
511
582
|
}
|
|
512
|
-
.composer {
|
|
513
|
-
border-top: 1px solid var(--line);
|
|
514
|
-
padding: 12px 16px;
|
|
515
|
-
display: flex;
|
|
516
|
-
gap: 8px;
|
|
517
|
-
align-items: flex-end;
|
|
518
|
-
}
|
|
519
|
-
.composer textarea {
|
|
520
|
-
flex: 1;
|
|
521
|
-
resize: none;
|
|
522
|
-
background: var(--panel);
|
|
523
|
-
border: 1px solid var(--line);
|
|
524
|
-
border-radius: 9px;
|
|
525
|
-
color: var(--fg);
|
|
526
|
-
font: 13px var(--mono);
|
|
527
|
-
padding: 9px 12px;
|
|
528
|
-
outline: 0;
|
|
529
|
-
min-height: 40px;
|
|
530
|
-
max-height: 160px;
|
|
531
|
-
}
|
|
532
|
-
.composer textarea:focus {
|
|
533
|
-
border-color: var(--accent);
|
|
534
|
-
}
|
|
535
|
-
.send {
|
|
536
|
-
background: var(--accent);
|
|
537
|
-
color: var(--bg);
|
|
538
|
-
border: 0;
|
|
539
|
-
border-radius: 9px;
|
|
540
|
-
font-weight: 600;
|
|
541
|
-
padding: 10px 18px;
|
|
542
|
-
cursor: pointer;
|
|
543
|
-
font-size: 13px;
|
|
544
|
-
}
|
|
545
|
-
.send:disabled {
|
|
546
|
-
opacity: 0.45;
|
|
547
|
-
cursor: not-allowed;
|
|
548
|
-
}
|
|
549
|
-
.hint {
|
|
550
|
-
color: var(--muted);
|
|
551
|
-
font-size: 11px;
|
|
552
|
-
font-family: var(--mono);
|
|
553
|
-
margin-top: 6px;
|
|
554
|
-
padding: 0 16px 10px;
|
|
555
|
-
}
|
|
556
583
|
</style>
|
|
557
584
|
<link
|
|
558
585
|
rel="stylesheet"
|
|
@@ -589,6 +616,7 @@
|
|
|
589
616
|
<div class="meta">
|
|
590
617
|
<span id="count"></span>
|
|
591
618
|
<span class="metaright">
|
|
619
|
+
<button id="viewbtn" class="viewbtn" title="toggle compact list">☰</button>
|
|
592
620
|
<button id="newbtn" class="newbtn" title="spawn a new agent on this fleet">
|
|
593
621
|
+ New agent
|
|
594
622
|
</button>
|
|
@@ -600,6 +628,8 @@
|
|
|
600
628
|
<div class="list" id="list"></div>
|
|
601
629
|
</div>
|
|
602
630
|
|
|
631
|
+
<div class="splitter" id="splitter" title="drag to resize"></div>
|
|
632
|
+
|
|
603
633
|
<div class="right">
|
|
604
634
|
<div class="rhead" id="rhead" style="display: none">
|
|
605
635
|
<span class="dot" id="rdot"></span>
|
|
@@ -610,18 +640,9 @@
|
|
|
610
640
|
>
|
|
611
641
|
</div>
|
|
612
642
|
<div class="log" id="log">
|
|
613
|
-
<div class="placeholder"
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
<textarea
|
|
617
|
-
id="msg"
|
|
618
|
-
rows="1"
|
|
619
|
-
placeholder="message to send to the agent… (⌘/Ctrl+Enter)"
|
|
620
|
-
></textarea>
|
|
621
|
-
<button class="send" id="send">Send ⏎</button>
|
|
622
|
-
</div>
|
|
623
|
-
<div class="hint" id="hint" style="display: none">
|
|
624
|
-
POST /api/send → writes to the agent's stdin fifo, then Enter.
|
|
643
|
+
<div class="placeholder">
|
|
644
|
+
← pick an agent to tail its log; type directly into the terminal
|
|
645
|
+
</div>
|
|
625
646
|
</div>
|
|
626
647
|
</div>
|
|
627
648
|
</div>
|
|
@@ -963,14 +984,32 @@
|
|
|
963
984
|
},
|
|
964
985
|
};
|
|
965
986
|
|
|
987
|
+
// claude is the default CLI — show the cli name only when it differs, so the
|
|
988
|
+
// common case stays uncluttered and the identity (repo/branch) leads instead.
|
|
989
|
+
const cliLabel = (e) => (e.cli && e.cli !== "claude" ? e.cli : "");
|
|
990
|
+
// Parse owner/repo/branch from a cwd like .../ws/<owner>/<repo>/tree/<branch>.
|
|
991
|
+
function repoBranch(e) {
|
|
992
|
+
const m = /\/([^/]+)\/([^/]+)\/tree\/([^/]+)/.exec(e.cwd || "");
|
|
993
|
+
return m ? { owner: m[1], repo: m[2], branch: m[3] } : null;
|
|
994
|
+
}
|
|
995
|
+
// Identity string for the left panel. cap=true → repo/branch each clipped to
|
|
996
|
+
// 3 chars for the compact one-line view (e.g. "age/mai").
|
|
997
|
+
function ident(e, cap) {
|
|
998
|
+
const rb = repoBranch(e);
|
|
999
|
+
if (!rb) return "";
|
|
1000
|
+
const c = (s) => (cap && s.length > 3 ? s.slice(0, 3) : s);
|
|
1001
|
+
return `${c(rb.repo)}/${c(rb.branch)}`;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
966
1004
|
// Derive codehost-style mnemonic tags from a cwd like .../ws/<owner>/<repo>/tree/<wt>
|
|
967
1005
|
function tagsFor(e) {
|
|
968
1006
|
const t = [];
|
|
969
|
-
const
|
|
970
|
-
if (
|
|
971
|
-
t.push(["repo", `${
|
|
1007
|
+
const rb = repoBranch(e);
|
|
1008
|
+
if (rb) {
|
|
1009
|
+
t.push(["repo", `${rb.owner}/${rb.repo}`], ["wt", rb.branch]);
|
|
972
1010
|
}
|
|
973
|
-
|
|
1011
|
+
const cli = cliLabel(e);
|
|
1012
|
+
if (cli) t.push(["cli", cli]);
|
|
974
1013
|
if (e._host) t.push(["host", e._host]); // codehost rooms: which machine
|
|
975
1014
|
return t;
|
|
976
1015
|
}
|
|
@@ -1005,10 +1044,31 @@
|
|
|
1005
1044
|
});
|
|
1006
1045
|
}
|
|
1007
1046
|
|
|
1047
|
+
// Compact list: one line per agent (dot + cli + title), persisted per device.
|
|
1048
|
+
let compactList = localStorage.getItem("ay.compactList") === "1";
|
|
1049
|
+
|
|
1008
1050
|
function renderList() {
|
|
1009
1051
|
const toks = $("q").value.trim().split(/\s+/).filter(Boolean);
|
|
1010
1052
|
const shown = entries.filter((e) => matches(e, toks));
|
|
1011
1053
|
$("count").textContent = `${shown.length} / ${entries.length} agents`;
|
|
1054
|
+
$("viewbtn").classList.toggle("on", compactList);
|
|
1055
|
+
if (compactList) {
|
|
1056
|
+
$("list").innerHTML =
|
|
1057
|
+
shown
|
|
1058
|
+
.map((e) => {
|
|
1059
|
+
const t = e.title || e.prompt || "";
|
|
1060
|
+
const id = ident(e, true);
|
|
1061
|
+
const cli = cliLabel(e);
|
|
1062
|
+
return `<div class="row crow ${String(e.pid) === sel ? "sel" : ""}" data-pid="${e.pid}">
|
|
1063
|
+
<span class="dot ${esc(e.status)}"></span>
|
|
1064
|
+
${id ? `<span class="cident" title="${esc(ident(e))}">${esc(id)}</span>` : ""}
|
|
1065
|
+
${cli ? `<span class="cname">${esc(cli)}</span>` : ""}
|
|
1066
|
+
<span class="ctitle ${e.title ? "" : "dim"}" title="${esc(t)}">${esc(t)}</span>
|
|
1067
|
+
<span class="age">${age(e)}</span></div>`;
|
|
1068
|
+
})
|
|
1069
|
+
.join("") || `<div class="empty">no match</div>`;
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1012
1072
|
$("list").innerHTML =
|
|
1013
1073
|
shown
|
|
1014
1074
|
.map((e) => {
|
|
@@ -1020,7 +1080,7 @@
|
|
|
1020
1080
|
.join("");
|
|
1021
1081
|
return `<div class="row ${String(e.pid) === sel ? "sel" : ""}" data-pid="${e.pid}">
|
|
1022
1082
|
<div class="r1"><span class="dot ${esc(e.status)}"></span>
|
|
1023
|
-
<span class="name">${esc(e
|
|
1083
|
+
<span class="name">${esc(cliLabel(e) || ident(e) || "agent")}</span>
|
|
1024
1084
|
<span class="badge">pid ${e.pid}</span>
|
|
1025
1085
|
<span class="age">${age(e)}</span></div>
|
|
1026
1086
|
${e.title ? `<div class="rowtitle" title="${esc(e.title)}">${esc(e.title)}</div>` : ""}
|
|
@@ -1060,12 +1120,9 @@
|
|
|
1060
1120
|
if (!e) return;
|
|
1061
1121
|
renderList();
|
|
1062
1122
|
$("rhead").style.display = "flex";
|
|
1063
|
-
$("composer").style.display = "flex";
|
|
1064
|
-
$("hint").style.display = "block";
|
|
1065
1123
|
$("rdot").className = "dot " + e.status;
|
|
1066
|
-
$("rname").textContent = e.title || e
|
|
1124
|
+
$("rname").textContent = e.title || cliLabel(e) || ident(e) || "agent";
|
|
1067
1125
|
$("rpid").textContent = "pid " + e.pid;
|
|
1068
|
-
$("msg").focus();
|
|
1069
1126
|
|
|
1070
1127
|
// Render the agent's native TUI with xterm.js by feeding it the raw PTY
|
|
1071
1128
|
// stream (ANSI/cursor control intact) — see /api/tail?raw=1.
|
|
@@ -1088,6 +1145,7 @@
|
|
|
1088
1145
|
fit = new FitAddon.FitAddon();
|
|
1089
1146
|
term.loadAddon(fit);
|
|
1090
1147
|
term.open(logEl);
|
|
1148
|
+
term.focus();
|
|
1091
1149
|
// An agent can rename itself by emitting an OSC 0/2 title sequence
|
|
1092
1150
|
// (\x1b]2;my-name\x07); xterm parses it out of the raw PTY stream we already
|
|
1093
1151
|
// feed it, so we just surface the latest title as the header name. Falls
|
|
@@ -1163,42 +1221,22 @@
|
|
|
1163
1221
|
es = { close };
|
|
1164
1222
|
}
|
|
1165
1223
|
|
|
1166
|
-
async function send() {
|
|
1167
|
-
if (!sel) return;
|
|
1168
|
-
const msg = $("msg").value;
|
|
1169
|
-
if (!msg.trim()) return;
|
|
1170
|
-
$("send").disabled = true;
|
|
1171
|
-
try {
|
|
1172
|
-
const r = await Conn.post("/api/send", { keyword: sel, msg, code: "enter" });
|
|
1173
|
-
if (r.ok) {
|
|
1174
|
-
$("msg").value = "";
|
|
1175
|
-
} else {
|
|
1176
|
-
alert("send failed: " + r.text);
|
|
1177
|
-
}
|
|
1178
|
-
} finally {
|
|
1179
|
-
$("send").disabled = false;
|
|
1180
|
-
$("msg").focus();
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1224
|
$("list").addEventListener("click", (ev) => {
|
|
1185
1225
|
const row = ev.target.closest(".row");
|
|
1186
1226
|
if (row) select(row.dataset.pid);
|
|
1187
1227
|
});
|
|
1188
1228
|
$("q").addEventListener("input", renderList);
|
|
1229
|
+
$("viewbtn").addEventListener("click", () => {
|
|
1230
|
+
compactList = !compactList;
|
|
1231
|
+
localStorage.setItem("ay.compactList", compactList ? "1" : "0");
|
|
1232
|
+
renderList();
|
|
1233
|
+
});
|
|
1189
1234
|
window.addEventListener("resize", () => {
|
|
1190
1235
|
if (fit)
|
|
1191
1236
|
try {
|
|
1192
1237
|
fit.fit();
|
|
1193
1238
|
} catch {}
|
|
1194
1239
|
});
|
|
1195
|
-
$("send").addEventListener("click", send);
|
|
1196
|
-
$("msg").addEventListener("keydown", (ev) => {
|
|
1197
|
-
if (ev.key === "Enter" && (ev.metaKey || ev.ctrlKey)) {
|
|
1198
|
-
ev.preventDefault();
|
|
1199
|
-
send();
|
|
1200
|
-
}
|
|
1201
|
-
});
|
|
1202
1240
|
|
|
1203
1241
|
// ---- rooms: localStorage cache + a manager you open by clicking the badge ----
|
|
1204
1242
|
const ROOMS_KEY = "ay.rooms";
|
|
@@ -1504,8 +1542,7 @@
|
|
|
1504
1542
|
history.replaceState(null, document.title, location.pathname + location.search); // eat launch params
|
|
1505
1543
|
if (spec) showLaunch(spec);
|
|
1506
1544
|
setConn("● local", "var(--muted)");
|
|
1507
|
-
|
|
1508
|
-
setInterval(loadList, 3000);
|
|
1545
|
+
startPolling();
|
|
1509
1546
|
return;
|
|
1510
1547
|
}
|
|
1511
1548
|
// #k=<token> — local-mode auth from `ay serve --http`'s printed link.
|
|
@@ -1517,8 +1554,7 @@
|
|
|
1517
1554
|
// SECURITY: strip the token from the URL immediately.
|
|
1518
1555
|
history.replaceState(null, document.title, location.pathname + location.search);
|
|
1519
1556
|
setConn("● local", "var(--muted)");
|
|
1520
|
-
|
|
1521
|
-
setInterval(loadList, 3000);
|
|
1557
|
+
startPolling();
|
|
1522
1558
|
return;
|
|
1523
1559
|
}
|
|
1524
1560
|
const h = decodeURIComponent(raw);
|
|
@@ -1567,10 +1603,97 @@
|
|
|
1567
1603
|
// Render the UI immediately and refresh on a timer; connect to a room (if
|
|
1568
1604
|
// any) in the BACKGROUND so a dead/slow cached room never blanks the page.
|
|
1569
1605
|
if (!pending) setConn("● local", "var(--muted)");
|
|
1570
|
-
|
|
1571
|
-
setInterval(loadList, 3000); // refresh statuses / new agents
|
|
1606
|
+
startPolling();
|
|
1572
1607
|
if (pending) connectRoom(pending.room, pending.token, pending.host);
|
|
1573
1608
|
}
|
|
1609
|
+
|
|
1610
|
+
// ---- activity-gated polling + auto-reload on new deploy ----------------
|
|
1611
|
+
// Poll the agent list while the page is actually in use; pause when the tab
|
|
1612
|
+
// is hidden or the user has been idle for IDLE_MS, so an unattended console
|
|
1613
|
+
// stops making requests. On returning to the tab we refresh immediately.
|
|
1614
|
+
let lastActivity = Date.now();
|
|
1615
|
+
const IDLE_MS = 60_000; // no interaction for 1 min → idle → stop polling
|
|
1616
|
+
["mousemove", "keydown", "pointerdown", "wheel", "touchstart"].forEach((ev) =>
|
|
1617
|
+
window.addEventListener(ev, () => (lastActivity = Date.now()), { passive: true }),
|
|
1618
|
+
);
|
|
1619
|
+
const isActive = () =>
|
|
1620
|
+
document.visibilityState === "visible" && Date.now() - lastActivity < IDLE_MS;
|
|
1621
|
+
|
|
1622
|
+
let polling = false;
|
|
1623
|
+
function startPolling() {
|
|
1624
|
+
if (polling) return;
|
|
1625
|
+
polling = true;
|
|
1626
|
+
loadList();
|
|
1627
|
+
setInterval(() => {
|
|
1628
|
+
if (isActive()) loadList();
|
|
1629
|
+
}, 3000); // refresh statuses / new agents — only while active
|
|
1630
|
+
document.addEventListener("visibilitychange", () => {
|
|
1631
|
+
if (document.visibilityState === "visible") {
|
|
1632
|
+
lastActivity = Date.now();
|
|
1633
|
+
loadList();
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
watchVersion();
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
// Auto-reload when the console's own assets change (a new deploy). HEAD the
|
|
1640
|
+
// page once a minute and compare the cache validator; reload on change.
|
|
1641
|
+
// Best-effort — ignores failures (e.g. remote rooms / cross-origin).
|
|
1642
|
+
function watchVersion() {
|
|
1643
|
+
let seen = null;
|
|
1644
|
+
const check = async () => {
|
|
1645
|
+
if (!isActive()) return;
|
|
1646
|
+
try {
|
|
1647
|
+
const r = await fetch(location.pathname, { method: "HEAD", cache: "no-store" });
|
|
1648
|
+
const tag = r.headers.get("etag") || r.headers.get("last-modified");
|
|
1649
|
+
if (!tag) return;
|
|
1650
|
+
if (seen && seen !== tag) return location.reload();
|
|
1651
|
+
seen = tag;
|
|
1652
|
+
} catch {}
|
|
1653
|
+
};
|
|
1654
|
+
check();
|
|
1655
|
+
setInterval(check, 60_000);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
// ---- draggable middle splitter (VSCode-style) -------------------------
|
|
1659
|
+
// Drag adjusts the left column width (a CSS var on .app), clamped so neither
|
|
1660
|
+
// pane collapses, persisted per device, and refits the terminal as it moves.
|
|
1661
|
+
(function () {
|
|
1662
|
+
const sp = $("splitter");
|
|
1663
|
+
const app = document.querySelector(".app");
|
|
1664
|
+
const saved = localStorage.getItem("ay.leftw");
|
|
1665
|
+
if (saved) app.style.setProperty("--leftw", saved);
|
|
1666
|
+
let dragging = false;
|
|
1667
|
+
sp.addEventListener("pointerdown", (e) => {
|
|
1668
|
+
dragging = true;
|
|
1669
|
+
sp.classList.add("drag");
|
|
1670
|
+
sp.setPointerCapture(e.pointerId);
|
|
1671
|
+
e.preventDefault();
|
|
1672
|
+
});
|
|
1673
|
+
sp.addEventListener("pointermove", (e) => {
|
|
1674
|
+
if (!dragging) return;
|
|
1675
|
+
const w = Math.min(Math.max(e.clientX, 280), window.innerWidth - 360);
|
|
1676
|
+
app.style.setProperty("--leftw", w + "px");
|
|
1677
|
+
if (fit)
|
|
1678
|
+
try {
|
|
1679
|
+
fit.fit();
|
|
1680
|
+
} catch {}
|
|
1681
|
+
});
|
|
1682
|
+
const end = (e) => {
|
|
1683
|
+
if (!dragging) return;
|
|
1684
|
+
dragging = false;
|
|
1685
|
+
sp.classList.remove("drag");
|
|
1686
|
+
try {
|
|
1687
|
+
sp.releasePointerCapture(e.pointerId);
|
|
1688
|
+
} catch {}
|
|
1689
|
+
localStorage.setItem(
|
|
1690
|
+
"ay.leftw",
|
|
1691
|
+
getComputedStyle(app).getPropertyValue("--leftw").trim(),
|
|
1692
|
+
);
|
|
1693
|
+
};
|
|
1694
|
+
sp.addEventListener("pointerup", end);
|
|
1695
|
+
sp.addEventListener("pointercancel", end);
|
|
1696
|
+
})();
|
|
1574
1697
|
boot();
|
|
1575
1698
|
</script>
|
|
1576
1699
|
</body>
|