shadok-ai 0.16.11 → 0.16.13
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/package.json +1 -1
- package/public/index.html +38 -3
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -5273,6 +5273,20 @@
|
|
|
5273
5273
|
// source of truth. A new session (e.g. a Telegram /spawn) shows up as a tab
|
|
5274
5274
|
// live; a renamed channel renames its tab; a channel that left the registry
|
|
5275
5275
|
// (ended elsewhere) closes its tab.
|
|
5276
|
+
/** Is a group name being TYPED right now, anywhere? The server→client name
|
|
5277
|
+
* sync must not overwrite the field under the cursor, and there are three
|
|
5278
|
+
* places to rename a group: its head in the agents column, its row in the
|
|
5279
|
+
* rail, and — since the column title became editable — that title. Keying
|
|
5280
|
+
* the guard on the group HEAD alone missed the last two: `inlineRename`
|
|
5281
|
+
* swaps the span it was given for the input, so the input is wherever the
|
|
5282
|
+
* gesture started, not inside `headEl`. One query over all three, and the
|
|
5283
|
+
* name is simply left alone for one poll (4 s), which costs nothing. */
|
|
5284
|
+
function renamingAGroup() {
|
|
5285
|
+
return !!document.querySelector(
|
|
5286
|
+
"#groups input.rename, #railList input.rename, #tabbar .side-label input.rename",
|
|
5287
|
+
);
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5276
5290
|
let lastChannelIds = new Set();
|
|
5277
5291
|
async function syncChannels() {
|
|
5278
5292
|
if (restoring) return;
|
|
@@ -5292,7 +5306,7 @@
|
|
|
5292
5306
|
if (sg == null || sg.id == null) continue;
|
|
5293
5307
|
const g = groups.find((x) => x.id === sg.id);
|
|
5294
5308
|
if (!g) { createGroup(sg); continue; }
|
|
5295
|
-
if (sg.name && sg.name !== g.nameEl.textContent && !
|
|
5309
|
+
if (sg.name && sg.name !== g.nameEl.textContent && !renamingAGroup())
|
|
5296
5310
|
g.nameEl.textContent = sg.name;
|
|
5297
5311
|
if (sg.icon && sg.icon !== g.icon) g.icon = sg.icon;
|
|
5298
5312
|
}
|
|
@@ -7873,11 +7887,25 @@
|
|
|
7873
7887
|
// reloads onto the new build quickly instead of waiting out a backoff.
|
|
7874
7888
|
reloadExpectedUntil = Date.now() + 90_000;
|
|
7875
7889
|
break;
|
|
7876
|
-
case "history":
|
|
7890
|
+
case "history": {
|
|
7877
7891
|
// Full replay from the transcript (source of truth). Clear first so a
|
|
7878
7892
|
// reconnect (laptop wake / dropped link) rebuilds the transcript instead
|
|
7879
7893
|
// of appending a second copy of the whole history.
|
|
7880
|
-
|
|
7894
|
+
//
|
|
7895
|
+
// Hold the reader's place across the wipe+rebuild. On a RECONNECT the
|
|
7896
|
+
// transcript already carries the conversation, and tearing it down to
|
|
7897
|
+
// rebuild it snaps the view to the top UNLESS the browser's scroll
|
|
7898
|
+
// anchoring happens to save us — which it does not when the tab was in
|
|
7899
|
+
// the background while the replay ran (anchoring needs a live layout),
|
|
7900
|
+
// and does not in Safari. That is the reported "the chat jumps back up
|
|
7901
|
+
// when I come back". The replay is the same messages in the same order,
|
|
7902
|
+
// appended bottom-first, so we restore deterministically: pinned to the
|
|
7903
|
+
// bottom if they were following live, otherwise holding the same
|
|
7904
|
+
// distance from the bottom so what they were reading stays in view.
|
|
7905
|
+
const el = t.transcriptEl;
|
|
7906
|
+
const wasStick = el._stickBottom !== false;
|
|
7907
|
+
const fromBottom = Math.max(0, el.scrollHeight - el.scrollTop - el.clientHeight);
|
|
7908
|
+
el.innerHTML = "";
|
|
7881
7909
|
t.unreadSep = null; // the separator element went with the wipe
|
|
7882
7910
|
addTurn(t, "system", "", "— resumed session history —");
|
|
7883
7911
|
for (const h of msg.turns) {
|
|
@@ -7909,7 +7937,14 @@
|
|
|
7909
7937
|
if (h.afterInternal) b.parentElement.classList.add("after-gap");
|
|
7910
7938
|
}
|
|
7911
7939
|
}
|
|
7940
|
+
// Restore the reader's place deterministically — never trust the
|
|
7941
|
+
// browser's implicit scroll anchoring for a full teardown.
|
|
7942
|
+
el._stickBottom = wasStick;
|
|
7943
|
+
el.scrollTop = wasStick
|
|
7944
|
+
? el.scrollHeight
|
|
7945
|
+
: Math.max(0, el.scrollHeight - el.clientHeight - fromBottom);
|
|
7912
7946
|
break;
|
|
7947
|
+
}
|
|
7913
7948
|
case "ready":
|
|
7914
7949
|
// The server just named the instance it is. If it's not the one this
|
|
7915
7950
|
// page was loaded from, the port was taken over — stop writing, prompt a
|