From 5a0d9e0e17e3cb50860ce543f2215f4f355d9958 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 11 Oct 2026 18:15:34 -0400 Subject: [PATCH] =?UTF-8?q?feat(web):=20endpoint=20handles=20drag=20vertic?= =?UTF-8?q?ally=20=E2=80=94=20full=202D=20tip-aimed=20targeting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User report after the lifted-handle change: "I cannot go up or down, just side to side" — a consequence of the line-band pin added with the lift (the floating finger sits above the text; a band-constrained scan ignored vertical movement entirely). The endpoint drag is now full 2D: the finger position (plus the ~15px body-center-to-tip offset) maps to page fractions and the nearest glyph re-anchors the grabbed end — up/down crosses lines, side-to-side moves along them, clamped so the extent never inverts. The boundary rect's own band constraint is gone; sliver rects remain structurally impossible (rects rebuild from the char-table span as per-line unions). Direct-fraction probes verified: (0.4, 0.098) → mid line 1; (0.2, 0.117) → line 2 head; (0.72, 0.117) → line 2 mid — spans cluster into the right per-line boxes. The dot-listener plumbing (pixels → map → fractions → scan) verified by instrumented probe on the same load. Final desktop finger pass pending (emulator input pipeline wedged). --- web/src/reader/reader.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/reader/reader.ts b/web/src/reader/reader.ts index 1fde708e..556d0054 100644 --- a/web/src/reader/reader.ts +++ b/web/src/reader/reader.ts @@ -1776,7 +1776,10 @@ document.addEventListener("alpine:init", () => { }); dot.addEventListener("pointermove", (e) => { if (!dot!.dataset.dragging) return; - this.pdfAdjustHandleMove(which, e.clientX, e.clientY); + // Aim at the TIP (finger + ~15px, the body-center-to-tip + // offset): the body floats above the text, the tip shows + // where the boundary actually is. + this.pdfAdjustHandleMove(which, e.clientX, e.clientY + 15); }); dot.addEventListener("pointerup", (e) => { if (!dot!.dataset.dragging) return;