From 76d7ac12e76086a97537d9bcecdb40626701a74f Mon Sep 17 00:00:00 2001 From: poslop Date: Tue, 8 Sep 2026 22:35:43 -0500 Subject: [PATCH] Small fixes: gutter alignment, paste column, sort arrows, copy feedback, stats bar, title slug (#50-#55) - #50 gutter/code line misalignment: shared --code-lh/--code-fs tokens; .code .gutter drops its own vertical padding (was 14px vs 0, 14px offset) - #51 history Paste column shows only the paste name, or slug pill when untitled - #52 sort indicator arrow sits LEFT of the column label for all sortable columns - #53 copy buttons give in-place success feedback ('Success!' in --ok for 2s): paste view copy button and new-page result copy button - #54 collapsed stats summary bar: roomier padding (14px 18px) and item gap (16px); wraps gracefully on mobile - #55 paste view title bar: slug pill only shown for custom slugs, id not repeated Closes #50, closes #51, closes #52, closes #53, closes #54, closes #55 --- web/static/app.css | 20 ++++++++++++++++---- web/templates/history.html | 21 ++++++++++++--------- web/templates/mine.html | 4 ++-- web/templates/new.html | 12 ++++++++++-- web/templates/paste.html | 16 ++++++++++++---- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/web/static/app.css b/web/static/app.css index 3bf5371..d6f433c 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -95,10 +95,13 @@ body { border: 1px solid var(--border); background: var(--surface-2); color: var(--muted-fg); border-radius: var(--radius); padding: 4px 10px; font: inherit; font-size: 21.6px; cursor: pointer; } +/* shared code line metrics (#50): gutter + code must share one line box */ +:root { --code-lh: 1.7; --code-fs: 21.6px; } + .editor-wrap { flex: 1; display: flex; min-height: 0; } .gutter { padding: 14px 10px; text-align: right; color: var(--muted); font-family: var(--font-mono); - font-size: 21.6px; line-height: 1.7; user-select: none; white-space: pre; overflow: hidden; + font-size: var(--code-fs); line-height: var(--code-lh); user-select: none; white-space: pre; overflow: hidden; border-right: 1px solid var(--border); } .editor { @@ -145,11 +148,12 @@ body { .paste-title-bar { display: flex; align-items: center; gap: 12px; padding: 12px 18px; flex-wrap: wrap; } .paste-title-bar h1 { font-size: 29.2px; font-weight: 600; margin: 0; } .stats-pill { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } -.stats-head { display: flex; align-items: center; gap: 10px; width: 100%; background: none; border: 0; color: var(--muted-fg); font: inherit; font-size: 21.6px; padding: 10px 14px; cursor: pointer; text-align: left; } +.stats-head { display: flex; align-items: center; gap: 16px; width: 100%; background: none; border: 0; color: var(--muted-fg); font: inherit; font-size: 21.6px; padding: 14px 18px; cursor: pointer; text-align: left; } .stats-head:hover { color: var(--fg); background: var(--surface-2); } .stats-chev { width: 18px; height: 18px; flex: none; transition: transform 0.15s ease; } .stats-pill.open .stats-chev { transform: rotate(180deg); } -.stats-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.stats-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; letter-spacing: .01em; } +@media (max-width: 640px) { .stats-summary { white-space: normal; word-break: break-word; } } .stats-body { border-top: 1px solid var(--border); } .stats-grid { display: grid; grid-template-columns: max-content 1fr; gap: 6px 18px; padding: 12px 16px; font-size: 20.7px; } .stats-k { color: var(--muted-fg); } @@ -169,10 +173,12 @@ body { } .code-head .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); } .code { - font-family: var(--font-mono); font-size: 22.4px; line-height: 1.7; + font-family: var(--font-mono); font-size: var(--code-fs); line-height: var(--code-lh); padding: 14px 0; display: flex; overflow-x: auto; } .code .gutter { flex-shrink: 0; } +/* gutter/code share line metrics; the editor gutter keeps its own padding (#50) */ +.code .gutter { padding-top: 0; padding-bottom: 0; } .codebody { padding: 0 18px; white-space: pre; } /* syntax highlight tokens (#1) */ .tok-kw { color: #c792ea; } @@ -370,6 +376,12 @@ th.sorted.desc .sort-ind { border-top: 6px solid var(--accent); } outline-offset: 1px; } +/* in-place copy success feedback (#53) */ +.iconbtn.ok, .btn.ok { + color: var(--ok); + border-color: var(--ok); +} + /* headings: unified treatment (mirrors .side-section h3) */ .settings-head h1, .paste-title-bar h1, .head-row h1, .inner h1 { letter-spacing: -0.01em; diff --git a/web/templates/history.html b/web/templates/history.html index b65148a..4bcd4ff 100644 --- a/web/templates/history.html +++ b/web/templates/history.html @@ -10,13 +10,13 @@ - - - - - - - + + + + + + +
PasteLanguageSizeViewsCreatedURLIDPasteLanguageSizeViewsCreatedURLID
@@ -115,8 +115,11 @@ async function load() { } else { $('empty').style.display = 'none'; rows.innerHTML = items.map(it => - `${esc(it.id)}` + - (it.title ? `
${esc(it.title)}
` : `
none
`) + `` + + `` + + (it.title + ? `${esc(it.title)}` + : `${esc(it.id)}`) + + `` + `${esc(it.language || 'text')}` + `${fmtSize(it.size)}${it.view_count}${ago(it.created_at)}` + (it.custom_slug ? `/${esc(it.custom_slug)}` : `none`) + diff --git a/web/templates/mine.html b/web/templates/mine.html index a933261..73cc647 100644 --- a/web/templates/mine.html +++ b/web/templates/mine.html @@ -57,8 +57,8 @@ async function load() { } $('empty').style.display = 'none'; rows.innerHTML = data.items.map(it => - `${esc(it.id)}` + - (it.title ? `
${esc(it.title)}
` : `
none
`) + `` + + `` + + (it.title ? `${esc(it.title)}` : `${esc(it.id)}`) + `` + `${esc(it.language || 'text')}` + `${fmtSize(it.size)}${ago(it.created_at)}` + (it.custom_slug ? `/${esc(it.custom_slug)}` : `none`) + diff --git a/web/templates/new.html b/web/templates/new.html index 1862108..48bd805 100644 --- a/web/templates/new.html +++ b/web/templates/new.html @@ -169,9 +169,17 @@ async function create() { return; } const url = location.origin + '/' + (data.custom_slug || data.id); - showResult('' + url + '', false); + showResult('' + url + ' ', false); $('result').dataset.token = data.deletion_token || ''; - try { navigator.clipboard.writeText(url); toast('Copied', 'success'); } catch(e) {} + const copyBtn = document.getElementById('result-copy'); + copyBtn.addEventListener('click', () => { + try { + navigator.clipboard.writeText(url); + copyBtn.classList.add('ok'); // in-place success feedback (#53) + copyBtn.textContent = 'Success!'; + setTimeout(() => { copyBtn.classList.remove('ok'); copyBtn.textContent = '⧉'; }, 2000); + } catch(e) { toast('Copy failed', 'error'); } + }); const dest = '/' + data.id + '?created=1&token=' + encodeURIComponent(data.deletion_token || ''); // password-protected: unlock now with the password we already have (#26) if ($('haspw').checked && data.id) { diff --git a/web/templates/paste.html b/web/templates/paste.html index 507e9cf..1464145 100644 --- a/web/templates/paste.html +++ b/web/templates/paste.html @@ -4,10 +4,10 @@

{{if .Title}}{{.Title}}{{else}}Untitled paste{{end}}

- /{{if .CustomSlug}}{{.CustomSlug}}{{else}}{{.ID}}{{end}} + {{if .CustomSlug}}/{{.CustomSlug}}{{end}}
raw - copy + copy {{if .DeletionToken}}delete{{end}}
@@ -63,9 +63,17 @@ function toggleStats() { pill.classList.toggle('open', open); btn.setAttribute('aria-expanded', open ? 'true' : 'false'); } -function copyContent() { +function copyContent(btn) { navigator.clipboard.writeText(document.getElementById('raw-content').value); - toast('Copied'); + // in-place success feedback (#53) + if (btn) { + btn.classList.add('ok'); + btn.textContent = 'Success!'; + clearTimeout(btn._okh); + btn._okh = setTimeout(() => { btn.classList.remove('ok'); btn.textContent = 'copy'; }, 2000); + } else { + toast('Copied', 'success'); + } } function redeem(token) { if (!confirm('Hard delete this paste immediately?')) return;