Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Personal website of Kai Chen — **Next.js 16** (App Router), **React 19**, **TypeScript**, **Tailwind CSS 4**, deployed on **Vercel**.

Canonical documentation: **[README.md](README.md)** (setup, routes, APIs, env, CI, MDX, forking).
Canonical documentation: **[README.md](README.md)** (setup, routes, APIs, env, CI, forking).

---

Expand All @@ -20,7 +20,7 @@ Canonical documentation: **[README.md](README.md)** (setup, routes, APIs, env, C
| **Client polling** | `app/hooks/use-now-playing.ts` — polls every **10s**; `Cache-Control` on API allows short CDN cache |
| **GitHub** | `GET /api/github/contributions` — GraphQL calendar + REST; `GET /api/github/stars` — star counts |
| **Weather** | `GET /api/weather` — Open-Meteo, fixed coordinates (Berkeley), revalidated fetch |
| **Notes** | MDX under `app/notes/**/page.mdx`; pipeline in `next.config.ts` (Webpack + `@mdx-js/loader`); components in `mdx-components.tsx` and `components/notes/` |
| **Notes** | External Notion page linked from navigation |
| **Observability** | Sentry via `instrumentation*.ts` + `sentry.*.config.ts` (DSN optional); Vercel Analytics / Speed Insights in root layout |
| **Theme** | `app/components/theme-provider.tsx` + inline script in `app/layout.tsx` — default **light** when unset |

Expand All @@ -30,7 +30,7 @@ Canonical documentation: **[README.md](README.md)** (setup, routes, APIs, env, C

```bash
npm install # also sets core.hooksPath → .githooks (co-author trailer)
npm run dev # next dev --webpack — required for MDX parity
npm run dev # next dev --webpack
npm run lint && npm run typecheck && npm run test && npm run build
```

Expand All @@ -41,13 +41,12 @@ npm run lint && npm run typecheck && npm run test && npm run build
- [`lib/now-playing.ts`](lib/now-playing.ts) — types for Last.fm payload
- [`lib/lastfm-now-playing-helpers.ts`](lib/lastfm-now-playing-helpers.ts) — pure helpers used by the now-playing route (tested)
- [`app/lib/substack.ts`](app/lib/substack.ts) — RSS fetch + parse (tested)
- [`next.config.ts`](next.config.ts) — MDX webpack rule, `withSentryConfig`
- [`next.config.ts`](next.config.ts) — webpack config, `withSentryConfig`

---

## Conventions

- **Dark mode** — support `dark:` for new UI.
- **MDX** — do not assume Turbopack; dev/build use `--webpack`.
- **Secrets** — never commit `.env.local` or tokens; see AGENTS.md and README.
- **User prompts** — when the user sends copy-paste blocks, keep them as single fenced blocks when echoing back.
54 changes: 17 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,15 @@ This repository is a **[Next.js 16](https://nextjs.org/)** application using the
7. [Routes and features](#routes-and-features)
8. [API routes](#api-routes)
9. [Environment variables](#environment-variables)
10. [MDX lecture notes](#mdx-lecture-notes)
11. [External integrations](#external-integrations)
12. [Local development](#local-development)
13. [Testing](#testing)
14. [CI, Dependabot, and auto-merge](#ci-dependabot-and-auto-merge)
15. [Git hooks](#git-hooks)
16. [Deployment](#deployment)
17. [Documentation map](#documentation-map)
18. [Forking this project](#forking-this-project)
19. [License](#license)
10. [External integrations](#external-integrations)
11. [Local development](#local-development)
12. [Testing](#testing)
13. [CI, Dependabot, and auto-merge](#ci-dependabot-and-auto-merge)
14. [Git hooks](#git-hooks)
15. [Deployment](#deployment)
16. [Documentation map](#documentation-map)
17. [Forking this project](#forking-this-project)
18. [License](#license)

---

Expand All @@ -85,7 +84,7 @@ The site combines:

- A **marketing-style home page** (identity, listening status, weather, **GitHub pinned repositories** via GraphQL with a static fallback, and Substack headlines).
- **Dynamic data** from Last.fm, GitHub, Open-Meteo, and optional Supabase-backed listening history.
- **MDX-powered notes** under `/notes` with math (KaTeX), GitHub-flavored Markdown, and syntax-highlighted code blocks — multiple course segments (UC Berkeley and SUSTech); see [MDX lecture notes](#mdx-lecture-notes).
- **Notes** are hosted externally on Notion (via the top navigation link).
- **Optional observability** via Sentry (client, server, edge) and Vercel Analytics / Speed Insights.

There is **no** `middleware.ts` (or `proxy.ts`) in this repo — every route is publicly accessible and rendered by the App Router directly.
Expand Down Expand Up @@ -120,15 +119,15 @@ npm run dev

Open [http://localhost:3000](http://localhost:3000).

**Important:** `dev` and `build` both pass **`--webpack`** to Next.js. MDX is wired through a **custom `webpack()` block** in [`next.config.ts`](next.config.ts) (`@mdx-js/loader` + remark/rehype plugins). Using Webpack for dev/build keeps MDX behavior aligned with production. Do not assume Turbopack-only behavior for `.mdx` files.
**Important:** `dev` and `build` both pass **`--webpack`** to Next.js. Keep local and production behavior aligned.

---

## npm scripts

| Script | Command | Purpose |
| --- | --- | --- |
| `dev` | `next dev --webpack` | Local development with Webpack (MDX-compatible). |
| `dev` | `next dev --webpack` | Local development with Webpack. |
| `build` | `next build --webpack` | Production bundle (also runs type checking as part of Next). |
| `start` | `next start` | Serve the last `build` output (run `build` first). |
| `lint` | `eslint` | ESLint across the repo ([`eslint.config.mjs`](eslint.config.mjs)). |
Expand Down Expand Up @@ -159,19 +158,17 @@ kaichen.dev/
│ ├── opengraph-image.tsx # OG image for /
│ ├── about/ # Bio / CV-style page + OG
│ ├── projects/ # Projects + GitHub heatmap + OG
│ ├── notes/ # Notes index, course pages, MDX note routes
│ ├── api/ # Route handlers (Last.fm, GitHub, weather)
│ ├── components/ # UI: nav, cards, theme, weather, listening, GitHub heatmap, …
│ ├── hooks/ # e.g. use-now-playing.ts
│ └── lib/ # og.tsx, substack RSS, GitHub pinned repos (GraphQL)
├── components/notes/ # MDX shortcodes: Theorem, Proof, Definition, Example, NoteBlock
├── lib/ # Shared server-oriented helpers + Vitest tests
│ ├── now-playing.ts # Types for Last.fm payload
│ ├── lastfm-now-playing-helpers.ts
│ ├── weather-open-meteo.ts
│ └── *.test.ts
├── mdx-components.tsx # MDX element mapping + shortcode registration
├── next.config.ts # MDX webpack rule + withSentryConfig
├── mdx-components.tsx # MDX element mapping
├── next.config.ts # Webpack config + withSentryConfig
├── instrumentation.ts # Sentry Node/Edge registration
├── instrumentation-client.ts # Sentry browser + router transition hooks
├── sentry.server.config.ts
Expand Down Expand Up @@ -201,7 +198,7 @@ kaichen.dev/
| --- | --- |
| Framework | Next.js **16.2** (App Router), React **19**, TypeScript **5** |
| Styling | Tailwind CSS **4** (`@tailwindcss/postcss`), custom CSS in `app/globals.css` |
| Content | **MDX** via `@mdx-js/loader` + `remark-gfm`, `remark-math`, `rehype-katex`, `rehype-highlight` |
| Content | Markdown/MDX pipeline available in config for future content routes |
| Fonts | `@fontsource/*` (Nunito, Bitter, JetBrains Mono), `geist` (sans/mono CSS variables) |
| Data | Supabase (`@supabase/supabase-js`) — optional listening history DB writes (service role) for `/api/lastfm/now-playing` |
| Monitoring | `@sentry/nextjs` (optional DSN), Vercel Analytics + Speed Insights |
Expand All @@ -218,9 +215,8 @@ Pinned versions are in [`package.json`](package.json).
| `/` | Identity block, Last.fm line + card, Berkeley weather, **pinned GitHub repos** (GraphQL + fallback list), Substack RSS snippets |
| `/about` | Education, experience, courses, volunteering |
| `/projects` | Project cards + **GitHub contribution calendar** (client component, data from `/api/github/contributions`) |
| `/notes` | Index of courses (see [MDX lecture notes](#mdx-lecture-notes)); links to external [SUSTech-Kai-Notes](https://github.com/kaiiiichen/SUSTech-Kai-Notes) for broader collections |
| `/notes/...` | Nested segments per course (e.g. `cs61a`, `data100`, `cs217`, `ma121`–`ma337`); individual notes are `page.mdx` |
**External nav (no in-app route):** the main nav includes **Blog** → [Substack](https://kaiiiichen.substack.com/); there is no `/blog` route in this repo.

**External nav (no in-app route):** the main nav includes **Notes** → [Notion](https://kaichen-dev.notion.site/kaichen-dev-notion-site-357a3294e48780f3b570f2f48f1081a4?pvs=74) and **Blog** → [Substack](https://kaiiiichen.substack.com/); there is no `/notes` or `/blog` route in this repo.

**Open Graph:** several routes ship `opengraph-image` route handlers for social previews. Set [`metadataBase`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadatabase) in `app/layout.tsx` if you see build warnings about resolving OG image URLs.

Expand Down Expand Up @@ -274,20 +270,6 @@ CI does not need any real Supabase keys to build — every Supabase client in th

---

## MDX lecture notes

- Notes are **route segments** with `page.mdx` files (e.g. `app/notes/cs61a/scheme-quote/page.mdx`), not a separate `content/` directory.
- The index at [`/notes`](app/notes/page.tsx) lists courses by code (examples: **CS61A**, **Data 100**, **CS217**, **MA121**–**MA337**). Each course has a `page.tsx` hub and nested folders for individual notes.
- Shared layout: [`app/notes/layout.tsx`](app/notes/layout.tsx) (imports KaTeX CSS, width/padding).
- MDX components and typography are centralized in [`mdx-components.tsx`](mdx-components.tsx).
- Custom shortcodes (Theorem, Definition, Proof, Example, NoteBlock) live in [`components/notes/`](components/notes/) and are registered globally for MDX.
- Metadata in MDX files often uses `export const metadata = { title, description }` (Next.js metadata), not always YAML frontmatter.
- Large PDFs or archives for a course may live under `public/notes/...` and be linked from MDX; keep binary paths in sync if you move files.

To add a new course: add a card on the notes index, create `app/notes/<slug>/page.tsx` plus note folders with `page.mdx`, match existing note headers (breadcrumb, title block) for visual consistency, and run `npm run build` to validate the MDX pipeline.

---

## External integrations

| Service | Use in this repo |
Expand All @@ -306,14 +288,12 @@ To add a new course: add a card on the notes index, create `app/notes/<slug>/pag
## Local development

- **Node 20**, **npm install** then **`npm run dev`**.
- If MDX fails to compile, confirm you did not remove the `--webpack` flag from scripts.
- **Supabase:** only `/api/lastfm/now-playing` uses Supabase (service role) for the optional listening history. The site builds and serves every page without any Supabase env set; the route just falls back to live Last.fm + in-memory cache when the DB is unreachable.

### Common issues

| Symptom | Things to check |
| --- | --- |
| MDX differs between dev and prod | Ensure both use Webpack (`--webpack`). |
| GitHub widgets empty | `GITHUB_TOKEN` set and not expired; API rate limits. |
| "Recently played" never persists across deploys | `NEXT_PUBLIC_SUPABASE_URL` / `SUPABASE_SERVICE_ROLE_KEY` set; tables `listening_history` / `listening_stats` exist with the expected columns. |
| Sentry noisy locally | DSN unset disables reporting; or lower sample rate in `instrumentation-client.ts`. |
Expand Down
6 changes: 5 additions & 1 deletion app/components/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ const WAVE_SVG = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/s
const NAV_LINKS = [
{ href: "/about", label: "About", external: false },
{ href: "/projects", label: "Projects", external: false },
{ href: "/notes", label: "Notes", external: false },
{
href: "https://kaichen-dev.notion.site/kaichen-dev-notion-site-357a3294e48780f3b570f2f48f1081a4?pvs=74",
label: "Notes",
external: true,
},
{ href: "https://kaiiiichen.substack.com/", label: "Blog", external: true },
];

Expand Down
7 changes: 0 additions & 7 deletions app/lib/github-pinned.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ const FALLBACK_PINNED: PinnedProject[] = [
repo: "kaiiiichen/kaichen.dev",
stack: ["Next.js", "TypeScript", "Tailwind"],
},
{
name: "SUSTech-Kai-Notes",
desc: "Open lecture notes for 20+ math and CS courses.",
href: "https://github.com/kaiiiichen/SUSTech-Kai-Notes",
repo: "kaiiiichen/SUSTech-Kai-Notes",
stack: ["LaTeX"],
},
{
name: "SudoSodoku",
desc: "Terminal-style Sudoku for iOS. Minimalist, focus-driven.",
Expand Down
51 changes: 0 additions & 51 deletions app/notes/cs217/dsaa/page.mdx

This file was deleted.

114 changes: 0 additions & 114 deletions app/notes/cs217/page.tsx

This file was deleted.

Loading