fix(feed): make the row's widths actually come from one table

The comment claimed the boxes were generated from ROW_COL. They weren't —
still w-32/w-16/w-48 by hand — so a comment promised a link that didn't
exist, which is worse than not claiming it. The boxes take the numbers
directly now (inline, since Tailwind can't read a runtime value), and the
thresholds add up in plain arithmetic rather than an IIFE whose result
depended on key order.
This commit is contained in:
2026-07-17 02:16:16 +02:00
parent ba23c0c996
commit 99c1bd9916
+47 -26
View File
@@ -322,33 +322,40 @@ function Thumb({
); );
} }
// The one-line row's column widths, in ONE place the Tailwind classes below are generated from // The one-line row's column widths, in ONE place: the boxes below take these directly (as inline
// them, so the thresholds can't drift out of step with the boxes they describe (they did when both // widths — Tailwind can't accept a runtime value), and so do the drop-out thresholds. Two
// were written by hand). Each is measured against the worst case in the library AND in both // hand-written sets would drift, and the symptom would be silent: a column appearing just before
// languages; see the row's own comment for where each number comes from. // it actually fits. Each is measured against the worst case in the LIBRARY and in BOTH languages —
// see the row's own comment for where each number comes from.
const ROW_COL = { const ROW_COL = {
actions: 192, // w-48 — six buttons at 28 + five 4px gaps = 188 actions: 192, // six buttons at 28 + five 4px gaps = 188
title: 120, // — not a column: the floor below which a title isn't worth showing channel: 128,
channel: 128, // w-32 meta: 104, // RowMetaCells' own grid: 4rem + 1rem + 1rem + two 4px gaps
meta: 104, // — RowMetaCells' own grid: 4rem + 1rem + 1rem + two 4px gaps views: 64,
views: 64, // w-16 published: 192,
published: 192, // w-48
} as const; } as const;
const ROW_GAP = 12; // gap-3 const ROW_GAP = 12; // gap-3
const ROW_PAD = 26; // px-3 both sides, plus a rounding allowance const ROW_PAD = 26; // px-3 both sides, plus a rounding allowance
const ROW_TITLE_MIN = 120; // not a column: the floor below which a title isn't worth showing
// What the row must measure before each column earns its place — cumulative from the left, so each // What the row must measure before each column earns its place — cumulative, so each appears only
// one only appears once everything ahead of it AND a readable title already fit. // once everything to its left AND a readable title already fit.
const ROW_FITS = (() => { const ROW_BASE = ROW_PAD + ROW_COL.actions + ROW_GAP + ROW_TITLE_MIN;
let w = ROW_PAD + ROW_COL.actions + ROW_GAP + ROW_COL.title; const ROW_FITS = {
const at = (px: number) => (w += ROW_GAP + px); channel: ROW_BASE + ROW_GAP + ROW_COL.channel,
return { meta: ROW_BASE + ROW_GAP + ROW_COL.channel + ROW_GAP + ROW_COL.meta,
channel: at(ROW_COL.channel), views: ROW_BASE + ROW_GAP + ROW_COL.channel + ROW_GAP + ROW_COL.meta + ROW_GAP + ROW_COL.views,
meta: at(ROW_COL.meta), published:
views: at(ROW_COL.views), ROW_BASE +
published: at(ROW_COL.published), ROW_GAP +
}; ROW_COL.channel +
})(); ROW_GAP +
ROW_COL.meta +
ROW_GAP +
ROW_COL.views +
ROW_GAP +
ROW_COL.published,
};
function VideoCard({ function VideoCard({
video, video,
@@ -489,7 +496,9 @@ function VideoCard({
<div className={clsx(rowShell, "flex items-center gap-3 px-3 py-2", watched && "opacity-55")}> <div className={clsx(rowShell, "flex items-center gap-3 px-3 py-2", watched && "opacity-55")}>
{/* Actions lead: this is where the eye already is (the title is the thing you read), so {/* Actions lead: this is where the eye already is (the title is the thing you read), so
they're the shortest pointer trip from it. */} they're the shortest pointer trip from it. */}
<div className="shrink-0 w-48">{actions}</div> <div className="shrink-0" style={{ width: ROW_COL.actions }}>
{actions}
</div>
{/* Everything else is shrink-0, so the title is the only thing that can give — which means {/* Everything else is shrink-0, so the title is the only thing that can give — which means
once the fixed columns outgrow the row, it gives ALL of it and vanishes. Columns drop once the fixed columns outgrow the row, it gives ALL of it and vanishes. Columns drop
from the right as the row narrows, least important first: a title with no date beats a from the right as the row narrows, least important first: a title with no date beats a
@@ -497,18 +506,30 @@ function VideoCard({
filter panel are doing. Until the first measure lands (width 0) show everything: the filter panel are doing. Until the first measure lands (width 0) show everything: the
row is off-screen for that frame anyway, and guessing narrow would flash. */} row is off-screen for that frame anyway, and guessing narrow would flash. */}
<div className="min-w-0 flex-1">{title}</div> <div className="min-w-0 flex-1">{title}</div>
{fits(ROW_FITS.channel) && <div className="shrink-0 w-32">{channelButton}</div>} {fits(ROW_FITS.channel) && (
<div className="shrink-0" style={{ width: ROW_COL.channel }}>
{channelButton}
</div>
)}
{fits(ROW_FITS.meta) && <RowMetaCells video={video} />} {fits(ROW_FITS.meta) && <RowMetaCells video={video} />}
{/* Right-aligned like the duration: it's a number, so the digits share an edge. 64px {/* Right-aligned like the duration: it's a number, so the digits share an edge. 64px
holds the widest bare form ("101.7K" at 48) in either language — dropping the word holds the widest bare form ("101.7K" at 48) in either language — dropping the word
took this from 144 and handed the difference to the title. */} took this from 144 and handed the difference to the title. */}
{fits(ROW_FITS.views) && ( {fits(ROW_FITS.views) && (
<div className="shrink-0 w-16 text-sm text-muted text-right tabular-nums truncate"> <div
className="shrink-0 text-sm text-muted text-right tabular-nums truncate"
style={{ width: ROW_COL.views }}
>
{viewsCell(false)} {viewsCell(false)}
</div> </div>
)} )}
{fits(ROW_FITS.published) && ( {fits(ROW_FITS.published) && (
<div className="shrink-0 w-48 text-sm text-muted truncate">{published}</div> <div
className="shrink-0 text-sm text-muted truncate"
style={{ width: ROW_COL.published }}
>
{published}
</div>
)} )}
{/* Thumb draws this over the image; with no image it goes on the row itself. */} {/* Thumb draws this over the image; with no image it goes on the row itself. */}
{pct > 0 && ( {pct > 0 && (