mirror of
https://github.com/prdlk/leetcode.git
synced 2026-11-02 13:12:44 +00:00
92 lines
3.7 KiB
Plaintext
92 lines
3.7 KiB
Plaintext
---
|
|||
|
|
/**
|
||
|
|
* The `PageHeader` slot: the badge row above a page's title. Blume hands the
|
||
|
|
* slot only the route, so the page's frontmatter is looked up through the
|
||
|
|
* route manifest — the same `getEntry` call Blume's own page template makes.
|
||
|
|
* Pages with none of the keys below (landing pages, the changelog) render
|
||
|
|
* nothing.
|
||
|
|
*
|
||
|
|
* - `topics:` (problem pages, written by `apps/cli/sync.ts`) is LeetCode's own
|
||
|
|
* topic tags. A tag the campaign teaches (`topic-landings.ts`) links to that
|
||
|
|
* topic's landing page and reads accent; any other tag links to its guide
|
||
|
|
* page, `/guides/<slug>` (sync creates one per such tag), and reads neutral.
|
||
|
|
* - `video:` (problem pages, written by `apps/cli/videos.ts`) is the NeetCode
|
||
|
|
* walkthrough's YouTube id, the red "Explanation" badge at the end of the row.
|
||
|
|
* - `list:` (guide pages, written by sync on create) is the tag's LeetCode
|
||
|
|
* problem list, an orange "LeetCode list" badge.
|
||
|
|
*
|
||
|
|
* A tag naming two campaign topics (`Depth-First Search` is both Tree DFS and
|
||
|
|
* Graph DFS) links to the page's own topic — its route's first segment — when
|
||
|
|
* that is one of them; otherwise DFS/BFS prefer the tree topic for a problem
|
||
|
|
* LeetCode also tags `Tree` and the graph topic for the rest. A route the site
|
||
|
|
* does not serve (a landing or guide page removed) falls back to LeetCode's
|
||
|
|
* problem list for the tag, so a badge never links to a 404.
|
||
|
|
*/
|
||
|
|
import { getEntry } from "astro:content";
|
||
|
|
import data from "blume:data";
|
||
|
|
import Badge from "blume/components/content/Badge.astro";
|
||
|
|
import { withBase } from "blume/components/islands/base-path.ts";
|
||
|
|
import { TOPIC_LANDINGS } from "../topic-landings.ts";
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
route: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface TopicTag {
|
||
|
|
name: string;
|
||
|
|
slug: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
const { route } = Astro.props;
|
||
|
|
const at = data.routes.find((r: { path: string }) => r.path === route);
|
||
|
|
const entry = at ? await getEntry(at.collection, at.entryId) : undefined;
|
||
|
|
const tags: TopicTag[] = entry?.data.topics ?? [];
|
||
|
|
const video: string | undefined = entry?.data.video;
|
||
|
|
const list: string | undefined = entry?.data.list;
|
||
|
|
|
||
|
|
const served = new Set(data.routes.map((r: { path: string }) => r.path));
|
||
|
|
const own = `/${route.split("/")[1]}`;
|
||
|
|
const prefer = tags.some((t) => t.slug === "tree") ? "/tree-" : "/graph-";
|
||
|
|
|
||
|
|
const badges = tags.map((tag) => {
|
||
|
|
const landings = (TOPIC_LANDINGS[tag.slug] ?? []).filter((path) => served.has(path));
|
||
|
|
const landing = landings.includes(own) ? own : (landings.find((p) => p.startsWith(prefer)) ?? landings[0]);
|
||
|
|
if (landing) return { name: tag.name, href: withBase(landing), accent: true, external: false };
|
||
|
|
const guide = `/guides/${tag.slug}`;
|
||
|
|
if (served.has(guide)) return { name: tag.name, href: withBase(guide), accent: false, external: false };
|
||
|
|
return { name: tag.name, href: `https://leetcode.com/problem-list/${tag.slug}/`, accent: false, external: true };
|
||
|
|
});
|
||
|
|
|
||
|
|
const linkClass = "no-underline transition-opacity hover:opacity-80";
|
||
|
|
---
|
||
|
|
|
||
|
|
{
|
||
|
|
(badges.length > 0 || video || list) && (
|
||
|
|
<div class="not-prose mx-auto mb-4 flex max-w-content flex-wrap gap-1.5">
|
||
|
|
{badges.map((b) => (
|
||
|
|
<a
|
||
|
|
href={b.href}
|
||
|
|
target={b.external ? "_blank" : undefined}
|
||
|
|
rel={b.external ? "noopener" : undefined}
|
||
|
|
class={linkClass}
|
||
|
|
>
|
||
|
|
<Badge variant={b.accent ? "accent" : "default"}>{b.name}</Badge>
|
||
|
|
</a>
|
||
|
|
))}
|
||
|
|
{video && (
|
||
|
|
<a href={`https://www.youtube.com/watch?v=${video}`} target="_blank" rel="noopener" class={linkClass}>
|
||
|
|
<Badge variant="danger" icon="youtube">
|
||
|
|
Explanation
|
||
|
|
</Badge>
|
||
|
|
</a>
|
||
|
|
)}
|
||
|
|
{list && (
|
||
|
|
<a href={list} target="_blank" rel="noopener" class={linkClass}>
|
||
|
|
<Badge variant="warning" icon="list">
|
||
|
|
LeetCode list
|
||
|
|
</Badge>
|
||
|
|
</a>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|