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
13 changes: 8 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@
# =============================================================================

# --- Supabase ----------------------------------------------------------------
# Only used by /api/lastfm/now-playing for optional listening history.
# Used by `/api/spotify/now-playing` for optional listening history.
# NEXT_PUBLIC_SUPABASE_URL is paired with the service role key for server-side
# reads/writes against `listening_history` / `listening_stats`.
NEXT_PUBLIC_SUPABASE_URL=

# Server-only: bypasses Row Level Security — NEVER expose to the client.
SUPABASE_SERVICE_ROLE_KEY=

# --- Last.fm -----------------------------------------------------------------
# API key from https://www.last.fm/api/account/create
# If unset, /api/lastfm/now-playing still responds with graceful fallbacks.
LASTFM_API_KEY=
# --- Spotify ------------------------------------------------------------------
# Spotify app + refresh token (scopes: user-read-currently-playing,
# user-read-recently-played). If unset, the now-playing API falls back to
# in-memory + Supabase `listening_stats`.
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=
SPOTIFY_REFRESH_TOKEN=

# --- GitHub ------------------------------------------------------------------
# Personal access token or fine-grained token with repo read access.
Expand Down
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,5 @@
- [ ] I have read [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
- [ ] No secrets, `.env.local`, or production tokens are included in this PR.
- [ ] **[README.md](../README.md)** / **[`.env.example`](../.env.example)** updated if routes, APIs, user-facing behavior, or env vars changed.
- [ ] **[CLAUDE.md](../CLAUDE.md)** updated if architecture, main data flows, or important paths changed (keeps AI / editor context aligned with README).
- [ ] **[CONTRIBUTING.md](../CONTRIBUTING.md)** / **[AGENTS.md](../AGENTS.md)** updated if contribution or agent workflow rules changed.
22 changes: 14 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ Canonical documentation: **[README.md](README.md)** (setup, routes, APIs, env, C

| Concern | Implementation |
| --- | --- |
| **Now playing** | `GET /api/lastfm/now-playing` — Last.fm + optional iTunes art; optional Supabase writes (`listening_*`) with service role |
| **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 |
| **Now playing** | `GET /api/spotify/now-playing` — Spotify Web API; optional Supabase writes (`listening_*`) with service role |
| **Client polling** | `app/hooks/use-now-playing.ts` — polls every **10s** (`cache: "no-store"`); API `Cache-Control: public, s-maxage=10, stale-while-revalidate=5` |
| **GitHub** | `GET /api/github/contributions` — GraphQL calendar + REST; `GET /api/github/stars` — star counts; pinned repos + static fallback in `app/lib/github-pinned.ts` |
| **Weather** | `GET /api/weather` — Open-Meteo (Berkeley); current temp, **feels-like**, **humidity**, condition, rain chance; `next.revalidate = 600`; local clock in card via `app/components/berkeley-time.tsx` (`America/Los_Angeles`) |
| **Home UI** | Identity + **social links** (brand-colored hovers), **Listening** / **Location** cards — `app/components/listening-card.tsx`, `weather-card.tsx` |
| **Typography** | Geist `font-sans` on `body`; **Nunito** for nav + `.mag-card` / `.mag-label` (`app/globals.css`) |
| **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 @@ -38,15 +40,19 @@ npm run lint && npm run typecheck && npm run test && npm run build

## Important paths

- [`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)
- [`lib/now-playing.ts`](lib/now-playing.ts) — types shared by `/api/spotify/now-playing` + `useNowPlaying`
- [`lib/spotify-now-playing-helpers.ts`](lib/spotify-now-playing-helpers.ts) — pure helpers used by that route (tested)
- [`lib/listening-supabase.ts`](lib/listening-supabase.ts) — optional Supabase reads/writes for listening history
- [`lib/weather-open-meteo.ts`](lib/weather-open-meteo.ts) — Open-Meteo payload parsing (tested)
- [`app/lib/substack.ts`](app/lib/substack.ts) — RSS fetch + parse (tested)
- [`next.config.ts`](next.config.ts) — webpack config, `withSentryConfig`
- [`app/page.tsx`](app/page.tsx) — home layout, social URLs, cards
- [`app/globals.css`](app/globals.css) — `.mag-card`, `.mag-label`, theme surfaces
- [`next.config.ts`](next.config.ts) — webpack + MDX loaders, `withSentryConfig`

---

## Conventions

- **Dark mode** — support `dark:` for new UI.
- **Dark mode** — support `dark:` for new UI (including social icon hover colors where relevant).
- **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.
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ cp .env.example .env.local

Edit `.env.local` as needed. You **do not** need every integration to run the app locally:

- Missing **Last.fm** / **GitHub** keys usually degrade specific widgets.
- **Supabase** placeholders are injected in CI for `next build`; locally, real or dummy public keys may be required for pages that call `createBrowserClient` at import time — see [README — CI placeholders](README.md#ci-placeholders).
- Missing **Spotify** / **GitHub** keys usually degrade specific widgets.
- **Supabase** is optional for `/api/spotify/now-playing` only; `next build` succeeds without Supabase env (see [README — Environment variables](README.md#environment-variables), subsection **CI builds without live Supabase**).

**Never commit** `.env.local`, `.env.vercel.check`, tokens, or `SENTRY_AUTH_TOKEN`.

Expand Down Expand Up @@ -92,8 +92,9 @@ npm run build

### Stack and rendering

- **Next.js 16** App Router, **React 19**, **TypeScript**, **Tailwind CSS 4**.
- **Next.js 16** App Router, **React 19**, **TypeScript**, **Tailwind CSS 4**, **Vitest 4** for unit tests.
- **MDX** uses the **Webpack** loader configured in [`next.config.ts`](next.config.ts). Local `dev` and `build` scripts use **`--webpack`**. Do not rely on Turbopack-only behavior for `.mdx` files.
- **Fonts:** Geist Sans/Mono on `<html>` / default `body`; **Nunito** for nav and magazine-style cards (see [`app/globals.css`](app/globals.css), [`app/layout.tsx`](app/layout.tsx)).

### Style

Expand All @@ -114,7 +115,7 @@ If you touch **OAuth callbacks**, **API routes**, or **Supabase policies**, coor

## Forking for your own site

See the **Forking this project** section in [README.md](README.md) for a checklist of files to replace (Last.fm user, GitHub repos, Supabase, Substack, etc.).
See the **Forking this project** section in [README.md](README.md) for a checklist of files to replace (Spotify app, GitHub repos, Supabase, Substack, etc.).

---

Expand Down
Loading