shadok-ai 0.16.12 → 0.16.14

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/public/index.html +38 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shadok-ai",
3
- "version": "0.16.12",
3
+ "version": "0.16.14",
4
4
  "main": "dist/session.js",
5
5
  "scripts": {
6
6
  "test": "node --import tsx --test --test-timeout=120000 test/*.ts context/*/test/*.test.mjs",
package/public/index.html CHANGED
@@ -350,8 +350,13 @@
350
350
  display: block; margin-top: 3px; opacity: 0.8;
351
351
  white-space: normal; line-height: 1.45;
352
352
  }
353
+ /* Top banners sit ABOVE the header, not over it: they are flow items at the
354
+ very top of the fixed flex chassis (inserted as body's first child), so they
355
+ push the header and everything below down instead of overlapping it. A
356
+ `position:fixed; top:0` strip used to float over the header and hide the
357
+ brand/gauges underneath. */
353
358
  #updateBanner {
354
- position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
359
+ flex: 0 0 auto; width: 100%; z-index: 1000;
355
360
  background: var(--amber); color: var(--on-accent);
356
361
  font-family: var(--sans); font-size: 12px; text-align: center;
357
362
  padding: 6px 12px;
@@ -359,7 +364,7 @@
359
364
  /* The re-login reminder (sign-in within 48h / expired). Same top strip as the
360
365
  update banner; its own id so the two never share styling by accident. */
361
366
  #expiryBanner {
362
- position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
367
+ flex: 0 0 auto; width: 100%; z-index: 1000;
363
368
  background: var(--amber); color: var(--on-accent);
364
369
  font-family: var(--sans); font-size: 12px;
365
370
  padding: 6px 12px;
@@ -4562,8 +4567,10 @@
4562
4567
  swapped = true;
4563
4568
  restoring = true; // freezes pushChannels AND persistGroups (both bail on it)
4564
4569
  const bar = document.createElement("div");
4570
+ // A flow strip at the very top (first child) so it sits ABOVE the header
4571
+ // rather than floating over it — same placement as the update/expiry banners.
4565
4572
  bar.style.cssText =
4566
- "position:fixed;top:0;left:0;right:0;z-index:99999;background:#b23b3b;color:#fff;" +
4573
+ "flex:0 0 auto;width:100%;z-index:99999;background:#b23b3b;color:#fff;" +
4567
4574
  "padding:10px 14px;font:14px system-ui;text-align:center";
4568
4575
  bar.textContent = "This cockpit was replaced by another instance on this port — reload to reconnect. ";
4569
4576
  const btn = document.createElement("button");
@@ -4571,7 +4578,7 @@
4571
4578
  btn.style.cssText = "margin-left:8px;padding:3px 10px;cursor:pointer";
4572
4579
  btn.addEventListener("click", () => location.reload());
4573
4580
  bar.appendChild(btn);
4574
- document.body.appendChild(bar);
4581
+ document.body.insertBefore(bar, document.body.firstChild);
4575
4582
  }
4576
4583
  function pushChannels(list) {
4577
4584
  if (restoring || swapped) return; // never overwrite the server list with a partial one, nor a foreign instance's
@@ -7674,7 +7681,8 @@
7674
7681
  btn.textContent = "Re-login now";
7675
7682
  btn.addEventListener("click", () => openAuthCard(true));
7676
7683
  el.append(span, btn);
7677
- document.body.appendChild(el);
7684
+ // First child → above the header in the flex chassis, not floating over it.
7685
+ document.body.insertBefore(el, document.body.firstChild);
7678
7686
  }
7679
7687
  // Expired is an escalation, not just a reminder — turn the strip red.
7680
7688
  el.style.background = w.expired ? "var(--err)" : "";
@@ -7867,7 +7875,8 @@
7867
7875
  if (!el) {
7868
7876
  el = document.createElement("div");
7869
7877
  el.id = "updateBanner";
7870
- document.body.appendChild(el);
7878
+ // First child → above the header in the flex chassis, not floating over it.
7879
+ document.body.insertBefore(el, document.body.firstChild);
7871
7880
  }
7872
7881
  el.textContent = "Updating to v" + version + "… the page will reload automatically.";
7873
7882
  }
@@ -7887,11 +7896,25 @@
7887
7896
  // reloads onto the new build quickly instead of waiting out a backoff.
7888
7897
  reloadExpectedUntil = Date.now() + 90_000;
7889
7898
  break;
7890
- case "history":
7899
+ case "history": {
7891
7900
  // Full replay from the transcript (source of truth). Clear first so a
7892
7901
  // reconnect (laptop wake / dropped link) rebuilds the transcript instead
7893
7902
  // of appending a second copy of the whole history.
7894
- t.transcriptEl.innerHTML = "";
7903
+ //
7904
+ // Hold the reader's place across the wipe+rebuild. On a RECONNECT the
7905
+ // transcript already carries the conversation, and tearing it down to
7906
+ // rebuild it snaps the view to the top UNLESS the browser's scroll
7907
+ // anchoring happens to save us — which it does not when the tab was in
7908
+ // the background while the replay ran (anchoring needs a live layout),
7909
+ // and does not in Safari. That is the reported "the chat jumps back up
7910
+ // when I come back". The replay is the same messages in the same order,
7911
+ // appended bottom-first, so we restore deterministically: pinned to the
7912
+ // bottom if they were following live, otherwise holding the same
7913
+ // distance from the bottom so what they were reading stays in view.
7914
+ const el = t.transcriptEl;
7915
+ const wasStick = el._stickBottom !== false;
7916
+ const fromBottom = Math.max(0, el.scrollHeight - el.scrollTop - el.clientHeight);
7917
+ el.innerHTML = "";
7895
7918
  t.unreadSep = null; // the separator element went with the wipe
7896
7919
  addTurn(t, "system", "", "— resumed session history —");
7897
7920
  for (const h of msg.turns) {
@@ -7923,7 +7946,14 @@
7923
7946
  if (h.afterInternal) b.parentElement.classList.add("after-gap");
7924
7947
  }
7925
7948
  }
7949
+ // Restore the reader's place deterministically — never trust the
7950
+ // browser's implicit scroll anchoring for a full teardown.
7951
+ el._stickBottom = wasStick;
7952
+ el.scrollTop = wasStick
7953
+ ? el.scrollHeight
7954
+ : Math.max(0, el.scrollHeight - el.clientHeight - fromBottom);
7926
7955
  break;
7956
+ }
7927
7957
  case "ready":
7928
7958
  // The server just named the instance it is. If it's not the one this
7929
7959
  // page was loaded from, the port was taken over — stop writing, prompt a