web: keep the viewport still when clicking a line number
Setting location.hash re-triggers the native fragment jump, so
file-view.js moves the hash with pushState, which fires popstate rather
than hashchange.

Assisted-by: Claude Opus 5 via Claude Code
change ovkwlszpyolnpswwmozuvupsqwtssxrq
commit 064c93c08aad6bbb92f16ca9b355c771a493676e
author Alpha Chen <alpha@kejadlen.dev>
date
parent kmzzkzkr
diff --git a/quire-server/static/file-view.js b/quire-server/static/file-view.js
index 3a8a734..20d3635 100644
--- a/quire-server/static/file-view.js
+++ b/quire-server/static/file-view.js
@@ -120,7 +120,7 @@
       if (selected && !first) first = rows[i];
     }
     if (first) {
-      first.scrollIntoView({ block: initial ? "center" : "nearest" });
+      if (initial) first.scrollIntoView({ block: "center" });
       document.dispatchEvent(
         new CustomEvent("quire:lines-selected", {
           detail: { start: start, end: end },
@@ -132,20 +132,30 @@
   surface.addEventListener("click", function (ev) {
     var num = ev.target.closest(".code-line-num");
     if (!num) return;
+    // The href exists so the number can be copied as a link; following it
+    // would scroll the clicked line under the viewport edge.
+    ev.preventDefault();
     var line = parseInt(num.dataset.line, 10);
+    var hash;
     if (ev.shiftKey && anchorLine !== null) {
-      ev.preventDefault();
       var a = Math.min(anchorLine, line);
       var b = Math.max(anchorLine, line);
-      window.location.hash = a === b ? "#L" + a : "#L" + a + "-L" + b;
+      hash = a === b ? "#L" + a : "#L" + a + "-L" + b;
     } else {
-      anchorLine = line; // plain click: the href sets the hash natively
+      anchorLine = line;
+      hash = "#L" + line;
     }
+    history.pushState(null, "", hash);
+    applySelection(false);
   });
 
+  // pushState skips hashchange; history traversal can fire either or both.
   window.addEventListener("hashchange", function () {
     applySelection(false);
   });
+  window.addEventListener("popstate", function () {
+    applySelection(false);
+  });
 
   function start() {
     if (window.quireHighlightSettled) {