diff --git a/apps/docs/blume.config.ts b/apps/docs/blume.config.ts index e610e20..7f020b6 100644 --- a/apps/docs/blume.config.ts +++ b/apps/docs/blume.config.ts @@ -127,10 +127,11 @@ export default defineConfig({ llmsTxt: true, mcp: { enabled: false }, }, - // The only content source. `/changelog` and `/appendix` are custom pages - // (pages/*.astro): `/changelog` reads the GitHub releases itself — one - // timeline, no per-release pages — and `/appendix` is one glossary page - // built from appendix.ts, so neither enters the sidebar tree or prev/next. + // The only content source. `/changelog`, `/appendix` and `/progress` are + // custom pages (pages/*.astro): `/changelog` reads the GitHub releases + // itself — one timeline, no per-release pages — `/appendix` is one glossary + // page built from appendix.ts, and `/progress` is the live stats cards, so + // none enters the sidebar tree or prev/next. content: { sources: [filesystem({ root: "content" })], }, @@ -142,10 +143,11 @@ export default defineConfig({ sidebar: { display: "flat", // "flat" | "group" | "page" }, - // The two custom pages (pages/*.astro) as header tabs. Neither is a - // content folder, so the tabs scope nothing out of the sidebar tree. + // The custom pages (pages/*.astro) as header tabs. None is a content + // folder, so the tabs scope nothing out of the sidebar tree. tabs: [ { label: "Changelog", path: "/changelog" }, + { label: "Progress", path: "/progress" }, { label: "Appendix", path: "/appendix" }, ], }, diff --git a/apps/docs/islands/RecentSolves.tsx b/apps/docs/components/progress/RecentSolves.tsx similarity index 90% rename from apps/docs/islands/RecentSolves.tsx rename to apps/docs/components/progress/RecentSolves.tsx index c449cf9..1356a0a 100644 --- a/apps/docs/islands/RecentSolves.tsx +++ b/apps/docs/components/progress/RecentSolves.tsx @@ -5,12 +5,10 @@ * hover. A row links to the problem's docs page when one exists: the * sync-generated short links (`redirects.ts`) are exactly the pages built. */ -import { StatsPending } from "../components/progress/shared"; -import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card"; -import { useStats, utcDate } from "../lib/stats"; -import redirects from "../redirects"; - -export const client = "load"; +import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; +import { useStats, utcDate } from "../../lib/stats"; +import redirects from "../../redirects"; +import { StatsPending } from "./shared"; const PAGES = new Map(redirects.map((r) => [r.from, r.to])); diff --git a/apps/docs/islands/SolvedRing.tsx b/apps/docs/components/progress/SolvedRing.tsx similarity index 94% rename from apps/docs/islands/SolvedRing.tsx rename to apps/docs/components/progress/SolvedRing.tsx index e04000b..7bc5dfb 100644 --- a/apps/docs/islands/SolvedRing.tsx +++ b/apps/docs/components/progress/SolvedRing.tsx @@ -8,13 +8,11 @@ */ import { useState } from "react"; import { Pie, PieChart } from "recharts"; -import { StatsPending } from "../components/progress/shared"; -import { Card, CardContent } from "../components/ui/card"; -import { type ChartConfig, ChartContainer } from "../components/ui/chart"; -import { DIFFICULTIES, type Difficulty, useStats } from "../lib/stats"; -import { cn } from "../lib/utils"; - -export const client = "load"; +import { Card, CardContent } from "../ui/card"; +import { type ChartConfig, ChartContainer } from "../ui/chart"; +import { DIFFICULTIES, type Difficulty, useStats } from "../../lib/stats"; +import { cn } from "../../lib/utils"; +import { StatsPending } from "./shared"; const chartConfig = { easy: { label: "Easy", color: "var(--chart-1)" }, diff --git a/apps/docs/islands/SubmissionChart.tsx b/apps/docs/components/progress/SubmissionChart.tsx similarity index 96% rename from apps/docs/islands/SubmissionChart.tsx rename to apps/docs/components/progress/SubmissionChart.tsx index d247ed0..1a13480 100644 --- a/apps/docs/islands/SubmissionChart.tsx +++ b/apps/docs/components/progress/SubmissionChart.tsx @@ -9,13 +9,11 @@ */ import { useState } from "react"; import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts"; -import { StatsPending } from "../components/progress/shared"; -import { Card, CardAction, CardContent, CardFooter, CardHeader } from "../components/ui/card"; -import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "../components/ui/chart"; -import { ToggleGroup, ToggleGroupItem } from "../components/ui/toggle-group"; -import { DIFFICULTIES, type Day, type Difficulty, useStats, utcDate } from "../lib/stats"; - -export const client = "load"; +import { Card, CardAction, CardContent, CardFooter, CardHeader } from "../ui/card"; +import { type ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent } from "../ui/chart"; +import { ToggleGroup, ToggleGroupItem } from "../ui/toggle-group"; +import { DIFFICULTIES, type Day, type Difficulty, useStats, utcDate } from "../../lib/stats"; +import { StatsPending } from "./shared"; const chartConfig = { easy: { label: "Easy", color: "var(--chart-1)" }, diff --git a/apps/docs/islands/SubmissionHeatmap.tsx b/apps/docs/components/progress/SubmissionHeatmap.tsx similarity index 96% rename from apps/docs/islands/SubmissionHeatmap.tsx rename to apps/docs/components/progress/SubmissionHeatmap.tsx index 2619e81..9756c98 100644 --- a/apps/docs/islands/SubmissionHeatmap.tsx +++ b/apps/docs/components/progress/SubmissionHeatmap.tsx @@ -8,11 +8,9 @@ * 4) and rows start on Monday like it, so the three heatmaps read the same. * Drawn as SVG in a viewBox, so it scales to its column instead of scrolling. */ -import { StatsPending, TipRow, useHoverTip } from "../components/progress/shared"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card"; -import { type Day, useStats, utcDate } from "../lib/stats"; - -export const client = "load"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"; +import { type Day, useStats, utcDate } from "../../lib/stats"; +import { StatsPending, TipRow, useHoverTip } from "./shared"; const MONTHS = 6; const CELL = 10; diff --git a/apps/docs/islands/TopicBubbles.tsx b/apps/docs/components/progress/TopicBubbles.tsx similarity index 95% rename from apps/docs/islands/TopicBubbles.tsx rename to apps/docs/components/progress/TopicBubbles.tsx index a75f754..ecc0133 100644 --- a/apps/docs/islands/TopicBubbles.tsx +++ b/apps/docs/components/progress/TopicBubbles.tsx @@ -9,12 +9,10 @@ */ import { packEnclose, packSiblings } from "d3-hierarchy"; import { useId } from "react"; -import { StatsPending, TipRow, useHoverTip } from "../components/progress/shared"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card"; -import { type Stats, useStats } from "../lib/stats"; -import { cn } from "../lib/utils"; - -export const client = "load"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"; +import { type Stats, useStats } from "../../lib/stats"; +import { cn } from "../../lib/utils"; +import { StatsPending, TipRow, useHoverTip } from "./shared"; const PAD = 2; // gap between bubbles, in layout units const UNIT = 12; // radius of a one-problem bubble diff --git a/apps/docs/components/progress/shared.tsx b/apps/docs/components/progress/shared.tsx index b412d2b..6cd2afe 100644 --- a/apps/docs/components/progress/shared.tsx +++ b/apps/docs/components/progress/shared.tsx @@ -1,5 +1,5 @@ /** - * What the four progress islands share besides the data: the not-ready body + * What the progress cards share besides the data: the not-ready body * (a skeleton while /api/stats loads, one muted line if it fails) and the * hover tooltip for the two charts drawn as plain SVG (the heatmap calendar * and the topic bubbles — Recharts has neither layout). The tooltip wears the diff --git a/apps/docs/content/progress.mdx b/apps/docs/content/progress.mdx deleted file mode 100644 index a5be3a3..0000000 --- a/apps/docs/content/progress.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Progress -description: Live campaign progress — solved by difficulty, recent attempts, the year by difficulty, topic coverage, and the most recently solved problems, fetched from the SRS Worker. -icon: loader ---- - -
- -
- - -
- - -
diff --git a/apps/docs/content/system-design/index.mdx b/apps/docs/content/system-design/index.mdx index 557ce3a..d759a69 100644 --- a/apps/docs/content/system-design/index.mdx +++ b/apps/docs/content/system-design/index.mdx @@ -1,7 +1,9 @@ --- title: System Design description: "Notes on System Design Interview — An Insider's Guide (Volumes 1 and 2): how to scope a problem, size it, and pick the parts that carry the load." -icon: network +icon: play +sidebar: + label: 'Getting Started' --- These notes follow Alex Xu's [System Design Interview — An Insider's Guide](https://bytebytego.com/courses/system-design-interview), [Volumes 1 and 2](https://www.goodreads.com/book/show/54109255-system-design-interview-an-insider-s-guide). diff --git a/apps/docs/lib/stats.ts b/apps/docs/lib/stats.ts index 025a9b0..5ae376f 100644 --- a/apps/docs/lib/stats.ts +++ b/apps/docs/lib/stats.ts @@ -3,9 +3,9 @@ * browser so `blume build` never depends on the API being up. API_BASE is the * deployed Worker — do not inline it anywhere else. * - * Four islands read this (SolvedRing, SubmissionHeatmap, RecentSolves, - * TopicBubbles). Each hydrates on its own, but they share this module, so the - * one in-flight request is shared too: the page asks the Worker once. + * The five cards on pages/progress.astro read this (components/progress/). + * Each hydrates on its own, but they share this module, so the one in-flight + * request is shared too: the page asks the Worker once. * * Invariants: a malformed body is an error state, never a throw; the first * frame is always the loading state, so server and client render identically. diff --git a/apps/docs/pages/progress.astro b/apps/docs/pages/progress.astro new file mode 100644 index 0000000..51db7b1 --- /dev/null +++ b/apps/docs/pages/progress.astro @@ -0,0 +1,52 @@ +--- +/** + * `/progress`: the live campaign dashboard — five LeetCode-style React cards + * over GET /api/stats (lib/stats.ts), hydrated on load. A custom page like + * `/changelog` and `/appendix`, linked from the header's `navigation.tabs`, + * so it enters neither the sidebar tree nor prev/next. The cards live in + * components/progress/, not islands/: they are imported here, never used from + * MDX, so Blume has no reason to register them as global MDX components. + */ +import PageLayout from "blume/components/layout/PageLayout.astro"; +import data from "blume:data"; +import RecentSolves from "../components/progress/RecentSolves"; +import SolvedRing from "../components/progress/SolvedRing"; +import SubmissionChart from "../components/progress/SubmissionChart"; +import SubmissionHeatmap from "../components/progress/SubmissionHeatmap"; +import TopicBubbles from "../components/progress/TopicBubbles"; + +const { config } = data; +const description = + "Live campaign progress — solved by difficulty, recent attempts, the year by difficulty, topic coverage, and the most recently solved problems, fetched from the SRS Worker."; +--- + + +
+

Progress

+

{description}

+ +
+ +
+ + +
+ + +
+
+