Files
siftlode/frontend/src/components/ReleaseNotes.tsx
T
peter ab873bbcaa feat(theme): R10 S2 — re-close the rem system (px-frozen labels/chrome -> rem)
The ~124 px-frozen text-[Npx] labels didn't track the Settings font-scale slider
while their rem-sized neighbours did. Add text-2xs (0.6875rem) / text-3xs
(0.625rem) font-size-ONLY tokens (no paired line-height -> matches the arbitrary
they replace, zero vertical-rhythm shift) and codemod every text-[Npx] to a rem
class (rare 8/9/12/13px -> rem arbitraries).

Also: --hdr-h 68px -> 4.25rem so header content clears the rem-sized pills at
scale 1.3 instead of sliding under; NavSidebar/SidePanel slot widths + their
icon/badge px sizes go rem so the rails keep pace with their (now-scaling)
labels; a w-24/h-[54px] thumb -> h-13.5 to preserve 16:9 at scale. Media
thumbnails, page-width caps and fixed container widths deliberately stay px.

Verified via getComputedStyle at scale 1.06/1.3: text-2xs 11.66->14.3px, header
clearance 72->88px, nav slot 246->302px — all scale proportionally.
2026-08-09 03:18:04 +02:00

85 lines
3.2 KiB
TypeScript

import { useTranslation } from "react-i18next";
import { Bug, Sparkles } from "lucide-react";
import { RELEASE_NOTES } from "../lib/releaseNotes";
import Modal from "./Modal";
function fmtDate(d: string): string {
const t = new Date(d);
return isNaN(t.getTime())
? d
: t.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" });
}
// Public release notes (chores are intentionally omitted here). `highlight` rings the
// version the user just updated to when opened from the new-version banner.
export default function ReleaseNotes({
onClose,
highlight,
}: {
onClose: () => void;
highlight?: string;
}) {
const { t } = useTranslation();
// Ring the newest note if `highlight` isn't one of the listed versions — e.g. a Vite dev build
// where the caller's FRONTEND_VERSION is "dev", or a VERSION bump whose note hasn't landed yet —
// so the banner always rings *something* current instead of nothing.
const active = RELEASE_NOTES.some((r) => r.version === highlight)
? highlight
: RELEASE_NOTES[0]?.version;
return (
<Modal title={t("about.releaseNotes")} onClose={onClose} maxWidth="max-w-2xl">
<div className="flex flex-col gap-6">
{RELEASE_NOTES.map((r) => (
<section
key={r.version}
className={active === r.version ? "rounded-xl ring-1 ring-accent/40 p-3 -m-3" : ""}
>
<div className="flex items-baseline gap-2 flex-wrap">
<h3 className="text-base font-semibold">v{r.version}</h3>
<span className="text-xs text-muted">{fmtDate(r.date)}</span>
{r.sha && (
<span className="text-2xs font-mono text-muted bg-surface px-1.5 py-0.5 rounded-sm">
{r.sha}
</span>
)}
</div>
{r.summary && <p className="text-sm text-muted mt-1">{r.summary}</p>}
{r.features?.length ? (
<div className="mt-3">
<div className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-accent">
<Sparkles className="w-3.5 h-3.5" /> {t("about.new")}
</div>
<ul className="mt-1.5 space-y-1.5 text-sm">
{r.features.map((f, i) => (
<li key={i} className="flex gap-2">
<span className="text-accent">•</span>
<span>{f}</span>
</li>
))}
</ul>
</div>
) : null}
{r.fixes?.length ? (
<div className="mt-3">
<div className="flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wide text-muted">
<Bug className="w-3.5 h-3.5" /> {t("about.fixes")}
</div>
<ul className="mt-1.5 space-y-1.5 text-sm">
{r.fixes.map((f, i) => (
<li key={i} className="flex gap-2">
<span className="text-muted">•</span>
<span>{f}</span>
</li>
))}
</ul>
</div>
) : null}
</section>
))}
</div>
</Modal>
);
}