diff --git a/apps/docs/lib/releases.ts b/apps/docs/lib/releases.ts index 2294f77..636c2f0 100644 --- a/apps/docs/lib/releases.ts +++ b/apps/docs/lib/releases.ts @@ -12,7 +12,9 @@ * release-topics.ts's render() writes: `| #issue | LC n · Name | set | diff | * closed | [py](…) · [docs](…) |`. Only `### Closed …` sections are read, so * a release published before open problems were dropped from the body still - * shows only what finished. + * shows only what finished. The row's own `[docs]` link is not used: it is + * frozen at release time (a problem solved before the docs existed has + * none), while the page links each problem by its LC number to its page now. * * Auth: GITHUB_TOKEN when set (deploy.yml passes it), else unauthenticated — * one request per 100 releases fits the anonymous limit. A failed request @@ -26,13 +28,13 @@ const KIND = /^(Topic|Review) \d+ — /u; export interface ReleaseProblem { issue: number; + /** The LeetCode number, the key of the docs `/` short links. */ + lc: number; /** `LC 57 · Insert Interval`. */ title: string; set: string; /** `easy` / `medium` / `hard`, the problem's `diff:*` label. */ difficulty: string; - /** The problem's `/` docs short link, once it has a page. */ - docs?: string; } export interface Release { @@ -53,15 +55,10 @@ function closedProblems(body: string): ReleaseProblem[] { for (const line of body.split("\n")) { if (line.startsWith("### ")) closed = line.startsWith("### Closed"); if (!closed || !line.startsWith("| #")) continue; - const [issue, title, set, difficulty, , solution] = line.slice(1, -1).split("|").map((cell) => cell.trim()); - if (!issue || !title || !set || !difficulty) continue; - problems.push({ - issue: Number(issue.slice(1)), - title, - set, - difficulty, - docs: solution?.match(/\[docs\]\(([^)]+)\)/u)?.[1], - }); + const [issue, title, set, difficulty] = line.slice(1, -1).split("|").map((cell) => cell.trim()); + const lc = title?.match(/^LC (\d+) · /u)?.[1]; + if (!issue || !title || !lc || !set || !difficulty) continue; + problems.push({ issue: Number(issue.slice(1)), lc: Number(lc), title, set, difficulty }); } return problems; } diff --git a/apps/docs/pages/changelog.astro b/apps/docs/pages/changelog.astro index 31de922..d6c8a3c 100644 --- a/apps/docs/pages/changelog.astro +++ b/apps/docs/pages/changelog.astro @@ -8,6 +8,8 @@ */ import PageLayout from "blume/components/layout/PageLayout.astro"; import data from "blume:data"; +import { withBase } from "blume/components/islands/base-path.ts"; +import redirects from "../redirects"; import { readReleases } from "../lib/releases.ts"; const { config } = data; @@ -19,6 +21,14 @@ const DATE = new Intl.DateTimeFormat("en-US", { year: "numeric", }); const issueUrl = (n: number) => `https://github.com/prdlk/leetcode/issues/${n}`; +// LC number -> its docs page route, off sync's `/` short links — the +// page itself, not the redirect, so a click is one hop. A problem with no page +// (none yet) links to its issue instead. +const pages = new Map(redirects.map((r) => [Number(r.from.slice(1)), r.to])); +const problemHref = (lc: number, issue: number) => { + const route = pages.get(lc); + return route ? withBase(route) : issueUrl(issue); +}; // The /progress cards' difficulty palette (--chart-1..3 in theme.css), so a // Hard reads the same colour here as in the SolvedRing gauge. const DIFFICULTY: Record = { @@ -84,8 +94,7 @@ const description = "Every topic and review the campaign finished, newest first. {p.set} - {/* The problem's docs page; its issue only until it has one. */} - + {p.title} {DIFFICULTY[p.difficulty] && (