Merge origin/dev into fix-261 for QA
This commit is contained in:
@@ -875,6 +875,9 @@ button[type="submit"]:focus-visible,
|
||||
.col-a { width: 260px; } .col-b { width: 140px; } .col-c { width: 120px; }
|
||||
.col-d { width: 96px; } .col-d2 { width: 150px; } .col-e { width: 190px; }
|
||||
.col-f { width: 100px; } .col-g { width: 190px; }
|
||||
/* #255: history's URL column had its own narrow width (col-f doubles as
|
||||
/mine's ID column); give it a dedicated class. */
|
||||
.col-url { width: 150px; }
|
||||
/* #210: /mine rows render a delete button cell that had no declared column,
|
||||
so under table-layout:fixed it overlapped the ID column. */
|
||||
.col-del { width: 64px; }
|
||||
@@ -890,3 +893,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); }
|
||||
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -9,6 +9,8 @@ function updateGutter() {
|
||||
gutter.textContent = s;
|
||||
}
|
||||
content.addEventListener('input', updateGutter);
|
||||
// #259: the editor scrolls itself; keep the gutter's numbers in step with it.
|
||||
content.addEventListener('scroll', () => { gutter.scrollTop = content.scrollTop; });
|
||||
updateGutter();
|
||||
|
||||
function toast(msg, kind) {
|
||||
@@ -197,7 +199,7 @@ function finishCreate(data) {
|
||||
try {
|
||||
navigator.clipboard.writeText(url);
|
||||
copyBtn.classList.add('ok'); // in-place success feedback (#53)
|
||||
copyBtn.textContent = 'Success!';
|
||||
copyBtn.textContent = '✓';
|
||||
setTimeout(() => { copyBtn.classList.remove('ok'); copyBtn.textContent = '⧉'; }, 2000);
|
||||
} catch(e) { toast('Copy failed', 'error'); }
|
||||
});
|
||||
|
||||
@@ -44,6 +44,23 @@
|
||||
// measured, not derived from span counts or heights.
|
||||
function renumber() {
|
||||
var lines = body.querySelectorAll('.codeline');
|
||||
// #257: size the gutter column to the widest line number so numbers in
|
||||
// the 100s+ fit their own column instead of bleeding into the code text.
|
||||
// The gutter is box-sizing: border-box, so the column width must be the
|
||||
// digits PLUS the 10px left + 10px right padding; at the CSS default 3ch
|
||||
// the padding alone leaves only ~19px of content, and any 2+ digit
|
||||
// number overflows into the code. Numbers are right-aligned, and the
|
||||
// width below fits the widest number exactly. Set via CSSOM (CSP
|
||||
// forbids inline style attributes). Only touch the width when it
|
||||
// changes: the resize observer below re-runs renumber() when the gutter
|
||||
// width reflows the code column, and rewriting the same value would
|
||||
// ping-pong the fixed point forever.
|
||||
var digits = String(lines.length || 1).length;
|
||||
var w = 'calc(' + digits + 'ch + 20px)';
|
||||
if (gutter.style.width !== w) {
|
||||
gutter.style.minWidth = w;
|
||||
gutter.style.width = w;
|
||||
}
|
||||
if (!wrapOn() || !lines.length) {
|
||||
// wrap OFF: one number per logical line (pre-existing behavior,
|
||||
// including the gutter scrolling with horizontal scroll).
|
||||
|
||||
@@ -20,7 +20,7 @@ function copyFeedback(btn) {
|
||||
if (!btn) return;
|
||||
if (!btn.dataset.label) btn.dataset.label = btn.textContent; // remember the original label (Copy/Link)
|
||||
btn.classList.add('ok');
|
||||
btn.textContent = 'Success!';
|
||||
btn.textContent = '✓';
|
||||
clearTimeout(btn._okh);
|
||||
btn._okh = setTimeout(() => { btn.classList.remove('ok'); btn.textContent = btn.dataset.label; }, 2000);
|
||||
}
|
||||
@@ -42,7 +42,7 @@ function redeem() {
|
||||
try { tok = sessionStorage.getItem('deletion_token_' + PASTE_ID) || ''; } catch(e) {}
|
||||
if (!tok) { alert('deletion token not available in this browser'); return; }
|
||||
fetch('/api/pastes/' + PASTE_ID + '/redeem', {method: 'DELETE', headers: {'Authorization': 'Bearer ' + tok}})
|
||||
.then(r => { if (r.ok) location.href = '/history'; else alert('delete failed'); });
|
||||
.then(r => { if (r.ok) location.href = '/public'; else alert('delete failed'); });
|
||||
}
|
||||
|
||||
// wiring (moved from inline handlers for CSP #139)
|
||||
|
||||
Reference in New Issue
Block a user