From f988e682566495df725eea2336d982c30418c3f5 Mon Sep 17 00:00:00 2001 From: Sulthan Zaki Date: Tue, 18 Aug 2026 10:20:24 +0700 Subject: [PATCH] prototype: address owner review round 1 (#122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - logo: restore the mirrored right half — the inline mark was missing the mirror, rendering a lopsided single-page glyph - verdict line: one uniform 22px line; counts inherit the display face - font sizes up across the board (table 10->11px, titles 16/17->17/18px, stat figures 17->19px, sections 10->11px) - overview Sites table: state column widens and never wraps - topbar: Library + Log out grouped right, matching the original chrome - Lanes: sidecar line moved under the Poll lanes explanation - Resume renders inside the same pausebar slot as Pause - lane problems (refusing / no browser / not checking) render danger red; a deliberate pause stays patina - series notes: chips capped at two with a +N tail instead of stacking - pending 'requested N ago' now renders once, in the action cell only --- .../web/templates/admin-prototype.html | 126 +++++++++++------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/backend/internal/web/templates/admin-prototype.html b/backend/internal/web/templates/admin-prototype.html index 760c57d..fb2c876 100644 --- a/backend/internal/web/templates/admin-prototype.html +++ b/backend/internal/web/templates/admin-prototype.html @@ -163,6 +163,12 @@ body[data-variant="B"] .sheet, body[data-variant="C"] .sheet { padding: 22px 20px 14px; } .topbar form { margin: 0; } +.topbar-actions { + display: flex; + align-items: center; + gap: 18px; + margin-left: auto; +} .ghost { position: relative; @@ -210,7 +216,7 @@ main.page { padding: 0 20px 40px; } .sec { margin: 0; padding: 18px 0 8px; - font: 500 10px/1 var(--font-mono); + font: 500 11px/1 var(--font-mono); letter-spacing: .2em; text-transform: uppercase; color: var(--mute-2); @@ -230,12 +236,8 @@ main.page { padding: 0 20px 40px; } color: var(--paper); } .verdict .attn { color: var(--patina); } -.verdict .counts { - font: 500 11px/1.5 var(--font-mono); - letter-spacing: .08em; - color: var(--mute); -} -.verdict .counts b { color: var(--paper-dim); font-weight: 600; } +.verdict .counts { font: inherit; color: var(--mute); } +.verdict .counts b { color: var(--paper-dim); font-weight: 400; } /* ---- stats block: label / figure rows, every figure a link but a zero ---- */ .stats { margin: 4px 0 0; } @@ -248,13 +250,13 @@ main.page { padding: 0 20px 40px; } border-bottom: 1px solid var(--rule); } .stat .lbl { - font: 500 10px/1 var(--font-mono); + font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); } .stat .fig { - font: 400 17px/1 var(--font-display); + font: 400 19px/1 var(--font-display); color: var(--paper); } .stat a.fig:hover { color: var(--patina); } @@ -262,15 +264,18 @@ main.page { padding: 0 20px 40px; } .stat a.fig:hover { border-bottom-color: var(--patina); } .stat .fig.zero { color: var(--mute); } -/* ---- marks: patina is the admin accent; never ember, never danger ---- */ +/* ---- marks: patina is the admin accent; never ember, never danger. + A lane in trouble overrides to danger (#122 review); paused stays + patina — deliberate, not a problem. ---- */ .mark, .mark-p { - font: 500 10px/1 var(--font-mono); + font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--patina); white-space: nowrap; } .mark-faint { color: var(--mute-2); } +.mark.bad { color: var(--danger); } /* ================= VARIANT A — narrow, prose rows (760px) ================= */ /* No new tokens. Facts are a meta line under the title, like the reading @@ -297,7 +302,7 @@ main.page { padding: 0 20px 40px; } flex-wrap: wrap; gap: 3px 10px; align-items: baseline; - font: 500 10px/1.5 var(--font-mono); + font: 500 11px/1.5 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); @@ -309,7 +314,7 @@ main.page { padding: 0 20px 40px; } .pst .pst-site { font: 400 19px/1.2 var(--font-display); color: var(--paper-dim); } .pst .pst-facts { margin-top: 4px; - font: 500 10px/1.6 var(--font-mono); + font: 500 11px/1.6 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); @@ -327,9 +332,9 @@ main.page { padding: 0 20px 40px; } border-bottom: 1px solid var(--rule); } .lrow .lane-site { font: 400 19px/1.2 var(--font-display); color: var(--paper-dim); } -.lrow.attention .lane-site { color: var(--patina); } +.lrow.attention .lane-site { color: var(--danger); } .lrow .lane-fact { - font: 500 10px/1 var(--font-mono); + font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); @@ -364,7 +369,7 @@ main.page { padding: 0 20px 40px; } .tbl .thead span { padding: 10px 0 8px; border-bottom: 1px solid var(--rule); - font: 500 10px/1 var(--font-mono); + font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); @@ -373,13 +378,13 @@ main.page { padding: 0 20px 40px; } .tbl .trow > * { padding: 11px 0; border-bottom: 1px solid var(--rule); - font: 500 10px/1.6 var(--font-mono); + font: 500 11px/1.6 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); } .tbl .trow .c-title { - font: 400 16px/1.3 var(--font-display); + font: 400 17px/1.3 var(--font-display); letter-spacing: 0; text-transform: none; color: var(--paper); @@ -388,27 +393,30 @@ main.page { padding: 0 20px 40px; } .tbl .trow.attention .c-title { color: var(--patina); } .tbl .trow .c-site { letter-spacing: .04em; } .tbl .trow .c-ch { color: var(--paper-dim); } -.tbl .trow .c-note { color: var(--mute-2); } +.tbl .trow .c-note { display: flex; flex-wrap: wrap; gap: 2px 8px; color: var(--mute-2); } .tbl .trow .c-act { text-align: right; } -.tbl .trow .c-act .ghost { font-size: 10px; } +.tbl .trow .c-act .ghost { font-size: 11px; } .tbl .trow > .confirm-row { grid-column: 1 / -1; } .tbl .trow .c-act .ghost:hover { color: var(--patina); border-bottom-color: var(--patina); } /* Landing per-Site table: site + library shape + five outcome sums over 12h. */ -.tbl.sites { grid-template-columns: 120px 1fr repeat(8, 62px) 120px; } -.tbl.sites .trow > * { font: 500 10px/1.6 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute-2); } -.tbl.sites .c-site { font: 400 17px/1.3 var(--font-display); letter-spacing: 0; text-transform: none; color: var(--paper-dim); } -.tbl.sites .c-state { color: var(--patina); } +.tbl.sites { grid-template-columns: 120px 1fr repeat(8, 54px) minmax(150px, 1fr); } +.tbl.sites .trow > * { font: 500 11px/1.6 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute-2); } +.tbl.sites .c-site { font: 400 18px/1.3 var(--font-display); letter-spacing: 0; text-transform: none; color: var(--paper-dim); } +.tbl.sites .c-state { color: var(--patina); white-space: nowrap; } +.tbl.sites .c-state.bad { color: var(--danger); } .tbl.sites .c-num { text-align: right; color: var(--paper-dim); } /* Lanes page in B: one aligned row per Lane. */ -.tbl.lanes { grid-template-columns: 130px 60px 70px 64px 96px 1fr 190px; } -.tbl.lanes .trow > * { font: 500 10px/1.6 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute-2); } -.tbl.lanes .c-site { font: 400 17px/1.3 var(--font-display); letter-spacing: 0; text-transform: none; color: var(--paper-dim); } -.tbl.lanes .trow.attention .c-site { color: var(--patina); } -.tbl.lanes .c-skip { color: var(--patina); } +.tbl.lanes { grid-template-columns: 130px 60px 70px 64px 96px 1fr 170px; } +.tbl.lanes .trow > * { font: 500 11px/1.6 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute-2); } +.tbl.lanes .c-site { font: 400 18px/1.3 var(--font-display); letter-spacing: 0; text-transform: none; color: var(--paper-dim); } +.tbl.lanes .trow.attention .c-site { color: var(--danger); } +.tbl.lanes .c-skip { white-space: nowrap; } +.tbl.lanes .c-skip .bad { color: var(--danger); } +.tbl.lanes .c-skip .ok { color: var(--patina); } .tbl.lanes .c-ctrl { text-align: right; } -.tbl.lanes .c-ctrl .ghost { font-size: 10px; } +.tbl.lanes .c-ctrl .ghost { font-size: 11px; } /* ========== VARIANT C — wide, two-line blocks (no table, no card) ========= */ /* Line one: title + action. Line two: every fact as one indented mono line. @@ -431,7 +439,7 @@ main.page { padding: 0 20px 40px; } flex-wrap: wrap; gap: 4px 12px; align-items: baseline; - font: 500 10px/1.6 var(--font-mono); + font: 500 11px/1.6 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); @@ -444,7 +452,7 @@ main.page { padding: 0 20px 40px; } .pblk .p1 .mark { font-size: 10px; } .pblk .p2 { margin-top: 4px; - font: 500 10px/1.6 var(--font-mono); + font: 500 11px/1.6 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); @@ -453,14 +461,14 @@ main.page { padding: 0 20px 40px; } /* Lane rows in C: site + state line, facts + controls line. */ .lblk { padding: 10px 0; border-bottom: 1px solid var(--rule); } .lblk .l1 { display: flex; align-items: baseline; gap: 14px; font: 400 19px/1.2 var(--font-display); color: var(--paper-dim); } -.lblk.attention .l1 { color: var(--patina); } +.lblk.attention .l1 { color: var(--danger); } .lblk .l2 { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: center; - font: 500 10px/1.6 var(--font-mono); + font: 500 11px/1.6 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); @@ -743,7 +751,7 @@ const SERIES = [ sighting: false, pending: false, orphan: true }, { key: "demonic:return-of-the-sss-class-rankers", site: "demonic", title: "Return of the SSS-Class Ranker", kind: "manga", - ch: null, chLabel: "—", checked: null, readers: 1, cover: false, url: false, + ch: null, chLabel: "—", checked: 13 * 60, readers: 1, cover: false, url: false, sighting: false, pending: false }, { key: "novelfull:reverend-insanity", site: "novelfull", title: "Reverend Insanity", kind: "novel", @@ -866,10 +874,18 @@ function rowMarks(s) { if (!s.cover) m.push("no cover"); if (s.sighting) m.push("reader sighting"); if (s.readers === 0) m.push("orphan"); - if (s.pending) m.push(`requested ${age(s.pendingAgo)}`); return m; } +/* notes render as chips, capped at two with a +N tail — a wall of stacked + marks at list density reads as noise, not signal */ +function noteChips(s) { + const m = rowMarks(s); + const chips = m.slice(0, 2).map(x => `${x}`).join(""); + const more = m.length - 2; + return chips + (more > 0 ? `+${more}` : ""); +} + /* ------------------------------------------------------------------ */ /* State: variant + page from the URL, filter/site/kind from search. */ /* ------------------------------------------------------------------ */ @@ -890,7 +906,7 @@ const esc = s => String(s).replace(/[&<>"']/g, c => ({ "&": "&", "<": "<" /* ------------------------------------------------------------------ */ /* Shared chrome */ /* ------------------------------------------------------------------ */ -const mark = ``; +const mark = ``; function chrome(active) { const nav = [ @@ -899,8 +915,10 @@ function chrome(active) { `${label}`).join(""); return `

${mark}BookmarkManager

- Library - + + Library + +
`; } @@ -957,6 +975,8 @@ function perSiteTable() { const lane = LANES.find(l => l.site === site.id); const out = lane ? lane.out : { refused: 0, unreachable: 0, no_chapter: 0, unfetchable: 0, errors: 0 }; const state = lane ? laneStateWord(lane) : ""; + const tone = lane ? laneStateTone(lane) : ""; + const stateMark = state ? `${state}` : ""; const n = (v) => `${v}`; if (variant === "A") { const facts = [ @@ -968,7 +988,7 @@ function perSiteTable() { `no chapter ${out.no_chapter}`, `unfetchable ${out.unfetchable}`, `errors ${out.errors}`, ].join(" · "); return `
${site.label}` + - (state ? ` ${state}` : "") + + (state ? ` ${stateMark}` : "") + `
${facts}
`; } if (variant === "B") { @@ -979,7 +999,7 @@ function perSiteTable() { ${series.filter(s => s.checked === null && s.url).length} ${series.filter(s => s.checked !== null && s.checked > STALE_MIN).length} ${n(out.refused)}${n(out.unreachable)}${n(out.no_chapter)}${n(out.unfetchable)}${n(out.errors)} - ${state} + ${state} `; } const facts = [ @@ -990,7 +1010,7 @@ function perSiteTable() { `refused ${out.refused}`, `unreachable ${out.unreachable}`, `no chapter ${out.no_chapter}`, `unfetchable ${out.unfetchable}`, `errors ${out.errors}`, ].join(" · "); - return `
${site.label}${state ? `${state}` : ""}
${facts}
`; + return `
${site.label}${stateMark}
${facts}
`; }).join(""); if (variant === "B") { @@ -1015,8 +1035,15 @@ function laneStateWord(l) { } return l.skip; } +/* tone: a lane in trouble renders danger; a deliberate pause stays patina */ +function laneStateTone(l) { + if (l.skip === "refusing" || l.skip === "sidecar-down") return "bad"; + if (l.skip === "" && l.due > 0 && l.checked === 0) return "bad"; + if (l.skip === "paused") return "ok"; + return ""; +} function pauseControls(l) { - if (l.skip === "paused") return `Resume`; + if (l.skip === "paused") return `Resume`; return `