From a4118b92a9ae33db0b45cc0371ac952a0b4234dd Mon Sep 17 00:00:00 2001 From: fen Date: Thu, 17 Sep 2026 14:49:02 -0500 Subject: [PATCH] #267: jump to top/bottom buttons for long pastes and the editor Fixed-position Top/Bottom pills appear only when content exceeds 2x viewport height (window scroll on paste view, textarea scroll on /new). New static/jump.js drives them; markup added to paste.html and new.html. --- internal/web/static/app.css | 14 +++++++++++ internal/web/static/jump.js | 41 +++++++++++++++++++++++++++++++ internal/web/templates/new.html | 5 ++++ internal/web/templates/paste.html | 5 ++++ 4 files changed, 65 insertions(+) create mode 100644 internal/web/static/jump.js diff --git a/internal/web/static/app.css b/internal/web/static/app.css index 389c78f..ba4e7f5 100644 --- a/internal/web/static/app.css +++ b/internal/web/static/app.css @@ -884,3 +884,17 @@ button[type="submit"]:focus-visible, .created-banner { display: block; } /* #167: gutter rows for wrapped paste view — one row per visual code line */ .gutline { display: block; } + +/* #267: jump to top/bottom pills for long pastes and the editor. + Hidden unless JS (jump.js) detects content more than 2x the viewport. */ +.jumpnav { + position: fixed; + right: 18px; + bottom: 18px; + z-index: 50; + display: flex; + flex-direction: column; + gap: 8px; +} +.jumpnav.hidden { display: none; } +.jump-btn { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); } diff --git a/internal/web/static/jump.js b/internal/web/static/jump.js new file mode 100644 index 0000000..03fb611 --- /dev/null +++ b/internal/web/static/jump.js @@ -0,0 +1,41 @@ +/* #267: jump to top / bottom controls for long content. + Paste view scrolls the window; the /new editor scrolls its textarea. + The active scroller is chosen via data-jump-scroll on the script tag. */ +(function () { + var nav = document.getElementById('jumpnav'); + if (!nav) return; + var scroller = window; + var sel = nav.dataset.jumpScroll; + if (sel) scroller = document.querySelector(sel); + + function el() { + return scroller === window ? document.scrollingElement : scroller; + } + function isLarge() { + var e = el(); + if (!e) return false; + var visible = scroller === window ? window.innerHeight : e.clientHeight; + return e.scrollHeight > visible * 2; + } + function refresh() { + nav.classList.toggle('hidden', !isLarge()); + } + function jump(toTop) { + var e = el(); + if (!e) return; + if (scroller === window) { + window.scrollTo({ top: toTop ? 0 : e.scrollHeight }); + } else { + e.scrollTop = toTop ? 0 : e.scrollHeight; + } + } + nav.addEventListener('click', function (ev) { + var b = ev.target.closest('[data-jump]'); + if (!b) return; + ev.preventDefault(); + jump(b.dataset.jump === 'top'); + }); + window.addEventListener('resize', refresh); + if (scroller !== window && scroller) scroller.addEventListener('input', refresh); + refresh(); +})(); diff --git a/internal/web/templates/new.html b/internal/web/templates/new.html index ef90b5f..e21941f 100644 --- a/internal/web/templates/new.html +++ b/internal/web/templates/new.html @@ -33,6 +33,10 @@
+
@@ -90,4 +94,5 @@
+ {{template "foot" .}} diff --git a/internal/web/templates/paste.html b/internal/web/templates/paste.html index 4465632..f035074 100644 --- a/internal/web/templates/paste.html +++ b/internal/web/templates/paste.html @@ -55,8 +55,13 @@
{{.Gutter}}
{{.ContentHTML}}
{{end}} + + {{template "foot" .}} -- 2.54.0