style(downloads): bump library-row meta text + action buttons a step (E7 S1)

Enlarge every non-title text on the card (channel, status/format/size/date meta,
source-ref line, progress readout) one step (xs→sm) plus their inline icons, and
grow the row action buttons (padding + icon) via the actions wrapper so the shared
IconBtn stays smaller in the modals. Buttons keep the title's line — the wrapper is
shrink-0/no-wrap, so the text column shrinks instead.
This commit is contained in:
2026-08-29 05:29:19 +02:00
parent bdbff89507
commit bb6cb6e335
+17 -12
View File
@@ -43,12 +43,12 @@ export function StatusPill({ job }: { job: DownloadJob }) {
// confused by the bar resetting between the separate video and audio streams. // confused by the bar resetting between the separate video and audio streams.
if (job.status === "running" && job.phase && PHASE_KEYS.includes(job.phase)) { if (job.status === "running" && job.phase && PHASE_KEYS.includes(job.phase)) {
return ( return (
<span className="text-xs font-medium text-accent">{t(`downloads.phase.${job.phase}`)}</span> <span className="text-sm font-medium text-accent">{t(`downloads.phase.${job.phase}`)}</span>
); );
} }
const key = job.expired ? "expired" : job.status; const key = job.expired ? "expired" : job.status;
return ( return (
<span className={clsx("text-xs font-medium", STATUS_CLS[job.status] ?? "text-muted")}> <span className={clsx("text-sm font-medium", STATUS_CLS[job.status] ?? "text-muted")}>
{t(`downloads.status.${key}`)} {t(`downloads.status.${key}`)}
</span> </span>
); );
@@ -145,8 +145,8 @@ function SourceRef({ url }: { url: string }) {
} }
}; };
return ( return (
<div className="flex items-center gap-1 mt-1 text-xs text-muted min-w-0"> <div className="flex items-center gap-1 mt-1 text-sm text-muted min-w-0">
<Link2 className="w-3 h-3 shrink-0" /> <Link2 className="w-3.5 h-3.5 shrink-0" />
<a <a
href={url} href={url}
target="_blank" target="_blank"
@@ -162,7 +162,7 @@ function SourceRef({ url }: { url: string }) {
title={t("downloads.source.copy")} title={t("downloads.source.copy")}
className="shrink-0 p-0.5 rounded-sm hover:bg-surface hover:text-fg transition" className="shrink-0 p-0.5 rounded-sm hover:bg-surface hover:text-fg transition"
> >
<Copy className="w-3 h-3" /> <Copy className="w-3.5 h-3.5" />
</button> </button>
</div> </div>
); );
@@ -173,7 +173,7 @@ function ChannelLine({ job }: { job: DownloadJob }) {
const { name, url } = jobChannel(job); const { name, url } = jobChannel(job);
if (url) { if (url) {
return ( return (
<div className="text-xs mt-0.5 truncate"> <div className="text-sm mt-0.5 truncate">
<a <a
href={url} href={url}
target="_blank" target="_blank"
@@ -186,7 +186,7 @@ function ChannelLine({ job }: { job: DownloadJob }) {
</div> </div>
); );
} }
return <div className="text-xs text-muted truncate mt-0.5">{name}</div>; return <div className="text-sm text-muted truncate mt-0.5">{name}</div>;
} }
export function DownloadRow({ export function DownloadRow({
@@ -206,14 +206,14 @@ export function DownloadRow({
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="font-medium leading-snug line-clamp-1 flex items-center gap-1.5"> <div className="font-medium leading-snug line-clamp-1 flex items-center gap-1.5">
{job.job_kind === "edit" && ( {job.job_kind === "edit" && (
<span className="inline-flex items-center gap-1 shrink-0 px-1.5 py-0.5 rounded-sm text-3xs font-semibold uppercase tracking-wide bg-accent/15 text-accent"> <span className="inline-flex items-center gap-1 shrink-0 px-1.5 py-0.5 rounded-sm text-2xs font-semibold uppercase tracking-wide bg-accent/15 text-accent">
<Scissors className="w-3 h-3" /> {t("downloads.clipBadge")} <Scissors className="w-3 h-3" /> {t("downloads.clipBadge")}
</span> </span>
)} )}
<span className="truncate">{jobTitle(job)}</span> <span className="truncate">{jobTitle(job)}</span>
</div> </div>
<ChannelLine job={job} /> <ChannelLine job={job} />
<div className="flex items-center gap-2 mt-1 text-xs"> <div className="flex items-center gap-2 mt-1 text-sm">
<StatusPill job={job} /> <StatusPill job={job} />
{job.asset?.container ? ( {job.asset?.container ? (
// Concrete container (mp4 / m4a / webm …) — reads as the file format, and its audio-vs-video // Concrete container (mp4 / m4a / webm …) — reads as the file format, and its audio-vs-video
@@ -231,7 +231,7 @@ export function DownloadRow({
className="text-muted inline-flex items-center gap-1" className="text-muted inline-flex items-center gap-1"
title={new Date(job.created_at).toLocaleString()} title={new Date(job.created_at).toLocaleString()}
> >
· <CalendarClock className="w-3 h-3" /> {relativeTime(job.created_at)} · <CalendarClock className="w-3.5 h-3.5" /> {relativeTime(job.created_at)}
</span> </span>
) : null} ) : null}
{job.status === "error" ? ( {job.status === "error" ? (
@@ -264,7 +264,7 @@ export function DownloadRow({
style={{ width: `${job.progress}%` }} style={{ width: `${job.progress}%` }}
/> />
</div> </div>
<div className="text-2xs text-muted mt-0.5 flex gap-2"> <div className="text-xs text-muted mt-0.5 flex gap-2">
<span>{job.progress}%</span> <span>{job.progress}%</span>
{job.speed_bps ? <span>{formatSpeed(job.speed_bps)}</span> : null} {job.speed_bps ? <span>{formatSpeed(job.speed_bps)}</span> : null}
{job.eta_s ? <span>{formatEta(job.eta_s)}</span> : null} {job.eta_s ? <span>{formatEta(job.eta_s)}</span> : null}
@@ -274,7 +274,12 @@ export function DownloadRow({
</div> </div>
)} )}
</div> </div>
<div className="flex items-start gap-0.5 shrink-0">{children}</div> {/* Row action buttons: grown a step here (bigger tap target + icon) without touching the shared
IconBtn — scoped so the same primitive stays smaller in the modals. `shrink-0` + no wrap keeps
them on the title's line; the text column shrinks instead. */}
<div className="flex items-start gap-1 shrink-0 [&_button]:p-2 [&_svg]:size-[18px]">
{children}
</div>
</div> </div>
); );
} }