A CLAUDE.md is just a markdown file at the root of your repo. Copy the content below into your own project's CLAUDE.md to give your agent the same context.
npx versuz@latest install star-history-star-history --kind=claude-mdcurl -o CLAUDE.md https://raw.githubusercontent.com/star-history/star-history/HEAD/CLAUDE.md# CLAUDE.md
## Package Manager
Use **pnpm** (not npm/yarn) for all commands: `pnpm install`, `pnpm run dev`, `pnpm run build`, etc.
## Plans
Save all design docs and implementation plans to `plans/` at the repo root. This directory is gitignored.
## Auto-Generated Files (Do NOT commit)
The following files are auto-generated and gitignored. Never `git add -f` them:
- `frontend/helpers/blog.json` — generated from blog markdown frontmatter
- `sitemap*.xml`, `robots.txt` — generated by Next.js build
- `next-env.d.ts`, `*.tsbuildinfo` — TypeScript build artifacts
## Tech Stack
**Frontend:** Next.js ^14.1.0 (Pages Router, static export) · React ^18.2.0 · TypeScript ^4.9.5 · Tailwind CSS ^3.4.0 · D3.js (axis, scale, selection, shape) · Axios ^1.8.2 · FontAwesome ^6.5.1 · Lodash ^4.17.21 · Dayjs ^1.11.10 · Gray-matter ^4.0.3 · Marked ^9.1.6
**Backend:** Hono ^4.7.4 · @hono/node-server · TypeScript ^5.7.0 · D3.js (server-side SVG) · JSDOM 20.0.2 · SVGO ^3.2.0 · Satori ^0.12.0 · LRU cache 7.14.1
## Project Structure
The repo has four main directories: `shared/`, `frontend/`, `backend/`, and `gh/`.
### Shared (`shared/`)
Code shared between frontend and backend. Both import from here: frontend via `@shared/*` tsconfig alias, backend via `../shared/` relative paths. A root `package.json` provides shared dependencies (axios, d3, dayjs, lodash).
| Directory | Purpose |
|-----------|---------|
| `common/utils.tsx` | Cross-platform utilities (date formatting, clipboard, etc.) |
| `common/api.tsx` | GitHub API client (star history fetching, pagination, token auth) |
| `common/chart.tsx` | Chart data transformation (supports `insertZeroPoint` option) |
| `packages/xy-chart.tsx` | D3 XY chart renderer (lobster emoji gated on `envType: "browser"`) |
| `packages/radar-svg.ts` | Pure-math radar SVG string generator (no D3, for satori/OG cards) |
| `packages/card-landscape1.tsx` | OG card layout builder |
| `packages/types.tsx` | D3 chart types, color palettes (`colors`, `darkColors`, `colorsCompact`) |
| `packages/components/` | ToolTip component |
| `packages/utils/` | D3 draw utilities (axis, labels, legend, watermark, filters, fonts) |
| `types/chart.tsx` | TypeScript types (ChartMode, RepoData, StarRecord, etc.) |
### Frontend
All frontend code lives under `frontend/`.
| Directory | Purpose |
|-----------|---------|
| `pages/` | Next.js page routes (Pages Router) |
| `components/` | React UI components |
| `store/` | React Context state management with URL hash sync |
| `helpers/` | Utilities (storage, toast, formatting, constants) and static data JSON |
| `styles/` | Tailwind config, global CSS, fonts (Inter font family) |
| `server/` | LRU cache for repo star data |
| `scripts/` | Blog JSON generation script |
| `public/` | Static assets and blog images |
| `plugins/` | Build scripts and utilities |
## Routes
Next.js **Pages Router** with `output: "export"` (static site generation).
| Route | File (`frontend/pages/`) | Description |
|-------|--------------------------|-------------|
| `/` | `index.tsx` | Home — chart viewer with repo input, sidebars |
| `/blog` | `blog/index.tsx` | Blog listing page |
| `/blog/[slug]` | `blog/[slug].tsx` | Individual blog post (markdown-based) |
| `/:owner/:repo` | `[...slug].tsx` | Repository detail page (catch-all) |
| 404 | `404.tsx` | Custom 404 with context-aware messages |
Layout wrappers: `_app.tsx` (per-page `getLayout` pattern), `_document.tsx`.
## State & Data Flow
1. **URL hash** (`#repo1&repo2&type=date&logscale`) → parsed in `store/index.tsx`
2. `RepoInputer` manages repo list → triggers `StarChartViewer`
3. `@shared/common/api.tsx` fetches star history from GitHub API (with pagination & token auth)
4. `@shared/common/chart.tsx` transforms data to D3-compatible format (frontend passes `{ insertZeroPoint: true }`)
5. `@shared/packages/xy-chart.tsx` renders the SVG chart via D3
## Visual Style Guide
The project uses an **xkcd / hand-drawn aesthetic**. All interactive overlays and UI chrome rendered on charts or cards must follow these conventions:
**Tooltip style** (reference: `shared/packages/components/ToolTip.tsx`):
- White background at 90% opacity, black 2px stroke, 5px border-radius
- `xkcdify` SVG filter on the background rect (feTurbulence fractalNoise + feDisplacementMap scale=5)
- Font: `font-family: "xkcd", cursive`, 15px, bold for titles
- Color indicator: 8×8 rect with 2px border-radius, matching the data series color
- Always use this same tooltip style for any new hover/interactive element across charts and cards
**Frame / stroke style**:
- Borders and outlines use the `xkcdify` SVG filter for a sketchy, wobbly look
- Stroke: black (`#000`) for light theme, white (`#fff`) for dark theme, 2px width
- Border-radius: 5px (`rx="5" ry="5"`)
- Never use clean/straight borders on chart or card UI — always apply the xkcdify filter
## Key Files for Common Changes
- **Chart rendering**: `@shared/packages/xy-chart.tsx`, `components/StarChartViewer.tsx`, `components/Charts/StarXYChart.tsx`
- **GitHub API**: `@shared/common/api.tsx`
- **State management**: `store/index.tsx`
- **Repo detail page**: `pages/[...slug].tsx`, `components/PageShell.tsx`
- **Blog system**: `pages/blog/[slug].tsx`, `scripts/generateBlogJson.mts`
- **Layout & nav**: `components/header.tsx`, `components/footer.tsx`, `components/LeftSidebar.tsx`, `components/RightSidebar.tsx`
- **Styling**: `tailwind.config.js`, `global.css`
## Backend (API Server)
The `backend/` directory is a Hono server (deployed as `api.star-history.com`) that generates star history SVG charts and OG card images.
- **Dev**: `cd backend && pnpm dev`
- **Build**: `cd backend && pnpm build`
- Requires `token.env` with GitHub tokens (one per line)
| File | Purpose |
|------|---------|
| `main.ts` | Hono server with `/svg` endpoint (query params: `repos`, `type`, `style`, `size`, `theme`) |
| `og-card.ts` | Satori-based OG card image renderer (`style=card` → 1200×630 PNG) |
| `cache.ts` | LRU cache (10K repos, 1GB, 24h TTL) for star records |
| `token.ts` | GitHub token rotation |
| `utils.ts` | SVG manipulation, image conversion helpers |
| `assets/` | Fonts (xkcd.ttf, Inter.ttf) and logo for OG card rendering |
## GH (Data Pipelines)
The `gh/` directory contains two pipelines: **star** (repo rankings for the frontend) and **event** (raw GitHub event archive for analytics).
### Star Pipeline
Fetches repo stats and exports JSON files consumed by the frontend.
- **Full run**: `cd gh && pnpm run star:fetch` — fetches from GitHub API + BigQuery, writes to SQLite (`star.db`), exports JSON files, and fetches star counts
- **Generate only**: `cd gh && pnpm run star:generate` — generates JSON files from existing `star.db` without fetching (useful after code changes)
- **DB is ephemeral**: `createDatabase()` deletes and recreates `star.db` on every run. The SQLite DB can always be regenerated from source APIs.
- **Exported JSON files** (written to `gh/data/`, imported by frontend via `@gh-data/*` alias): `leaderboard.json`, `weekly-ranking.json`, `repos.json`, `star-count.json`
| File | Purpose |
|------|---------|
| `star-fetch.ts` | Main entry point — orchestrates the full pipeline |
| `star-generate.ts` | Generates JSON files from existing `star.db` |
| `github.ts` | GitHub API client to find qualifying repos, token rotation |
| `bigquery.ts` | BigQuery client to fetch weekly activity stats |
| `db.ts` | SQLite schema, inserts, JSON export functions, date formatting |
| `star-count.ts` | Fetches repo counts per star threshold from GitHub Search API |
| `types.ts` | Shared TypeScript types |
### Event Pipeline
Downloads hourly GH Archive data into a local SQLite database with per-event-type tables (16 types) for schema exploration and analytics.
- **Run**: `cd gh && pnpm run event [YYYY-MM-DD] [hour]` — defaults to yesterday hour 0
- **DB**: `event.db` — separate per-type tables (e.g. `push_events`, `pull_request_events`), each with common fields (actor, repo, org) plus event-specific payload columns
- **Data source**: `https://data.gharchive.org/{date}-{hour}.json.gz`
| File | Purpose |
|------|---------|
| `event.ts` | Downloads, decompresses, and loads GH Archive events into `event.db` |
## CI/CD Workflows
Three GitHub Actions workflows in `.github/workflows/`:
| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `gh-fetch.yml` | Monday 18:00 UTC cron + manual | Runs `gh/` pipeline (fetch → SQLite → JSON), commits `star.db`, triggers both deploy workflows |
| `deploy-frontend.yml` | Push to `frontend/**`, `shared/**`, workflow file; PR preview; `workflow_dispatch` | Runs `pnpm run star:generate` in `gh/`, builds frontend, deploys to Cloudflare Pages |
| `deploy-backend.yml` | Push to `backend/**`, `shared/**`, workflow file; `workflow_dispatch` | Runs `pnpm run star:generate` in `gh/`, builds Docker image, deploys to GKE |
**Key design decisions:**
- `gh/data/` is gitignored — JSON files are generated on the fly by deploy workflows via `pnpm run star:generate`, not committed
- Only `gh/star.db` is committed (by `gh-fetch.yml`)
- Deploy workflows don't trigger on `star.db` changes — `gh-fetch.yml` explicitly triggers them via `workflow_dispatch` to avoid double deploys