2026-09-28 11:20:41 -04:00
|
|
|
---
|
|
|
|
|
/**
|
2026-09-29 17:03:28 -04:00
|
|
|
* The MDX `wrapper`: the badge row between a page's description and its body.
|
|
|
|
|
* Blume's page template renders the title and description itself and then
|
|
|
|
|
* the MDX content, with no slot in between, so wrapping the content is the
|
|
|
|
|
* only way to land right after the description. MDX hands a wrapper no
|
|
|
|
|
* route, so it is read off the URL, and the page's frontmatter is then
|
|
|
|
|
* 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 only their content.
|
2026-09-28 11:20:41 -04:00
|
|
|
*
|
|
|
|
|
* - `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.
|
2026-09-29 15:12:54 -04:00
|
|
|
* `tagTarget()` picks the route (and between two landings, e.g. DFS); the
|
|
|
|
|
* `PageFooter` backlinks list reads the same answer.
|
2026-09-28 11:20:41 -04:00
|
|
|
* - `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.
|
|
|
|
|
*/
|
|
|
|
|
import { getEntry } from "astro:content";
|
|
|
|
|
import data from "blume:data";
|
|
|
|
|
import Badge from "blume/components/content/Badge.astro";
|
2026-09-29 17:03:28 -04:00
|
|
|
import { stripBase, withBase } from "blume/components/islands/base-path.ts";
|
2026-09-29 15:12:54 -04:00
|
|
|
import { tagTarget, type TopicTag } from "../topic-landings.ts";
|
2026-09-28 11:20:41 -04:00
|
|
|
|
2026-09-29 17:03:28 -04:00
|
|
|
// `/sub/tree-dfs/invert-binary-tree/` (or `.../index.html` in a static build)
|
|
|
|
|
// -> `/tree-dfs/invert-binary-tree`, the base-less shape of `data.routes`.
|
|
|
|
|
const route =
|
|
|
|
|
stripBase(import.meta.env.BASE_URL ?? "/", Astro.url.pathname)
|
|
|
|
|
.replace(/(\/index)?\.html$/u, "")
|
|
|
|
|
.replace(/(.)\/+$/u, "$1");
|
2026-09-28 11:20:41 -04:00
|
|
|
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;
|
|
|
|
|
|
2026-09-29 15:12:54 -04:00
|
|
|
const served = new Set<string>(data.routes.map((r: { path: string }) => r.path));
|
2026-09-28 11:20:41 -04:00
|
|
|
|
|
|
|
|
const badges = tags.map((tag) => {
|
2026-09-29 15:12:54 -04:00
|
|
|
const target = tagTarget(tag, { route, tags }, served);
|
|
|
|
|
const external = target.kind === "external";
|
|
|
|
|
return { name: tag.name, href: external ? target.href : withBase(target.href), accent: target.kind === "landing", external };
|
2026-09-28 11:20:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const linkClass = "no-underline transition-opacity hover:opacity-80";
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
(badges.length > 0 || video || list) && (
|
2026-09-29 17:03:28 -04:00
|
|
|
<div class="not-prose mb-6 flex flex-wrap gap-1.5">
|
2026-09-28 11:20:41 -04:00
|
|
|
{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>
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-09-29 17:03:28 -04:00
|
|
|
|
|
|
|
|
<slot />
|