This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm run dev— Start Next.js dev server (http://localhost:3000)npm run build— Production build. run this locally before committing to verify output.npm run lint— Run ESLint (flat config, core-web-vitals + typescript)
No test framework is configured.
This is a Next.js 16 app (App Router, TypeScript, Tailwind v4). The current codebase is primarily a screenshot generator — it renders a Leaflet map with an activity route, photos, and stats, then uses Puppeteer to capture the result as a PNG/JPEG.
Planned direction: The app is being redesigned into a full activity browsing + route planning product (combined map view, see mockup-h.html). The screenshot pipeline is one feature of this broader product, not the entire app. When touching architecture, bear this in mind.
(main)— Standard app pages with fonts/global CSS (currently default Next.js landing page)(render)— Minimal layout (no fonts/styles) used exclusively for Puppeteer screenshot rendering. The render layout strips all margin/padding/overflow so the page captures cleanly.
- API route (
app/api/activity-printout/route.ts) — Entry point. AcceptsactivityId,width,height,formatquery params. Launches headless Chromium via Puppeteer, navigates to the render page, waits forwindow.__MAP_READY__, then captures a screenshot. - Render page (
app/(render)/render/[activityId]/page.tsx) — Server component that fetches activity data and passes it toRenderClient. - RenderClient — Client component that dynamically imports
ActivityMapwith SSR disabled (Leaflet requires browser APIs). - ActivityMap — Renders a
react-leafletMapContainer with route polyline, photo markers, tile layer, and a text overlay with stats. Setswindow.__MAP_READY__ = trueonce tiles load (with a 5s fallback timeout).
@sparticuz/chromium+puppeteer-core— Headless Chrome for screenshots (configured asserverExternalPackagesinnext.config.ts)react-leaflet/leaflet— Map rendering (client-side only, must usenext/dynamicwithssr: false)
lib/strava.ts currently returns mock data. It's designed to be replaced with real Strava API calls. Types are defined in lib/types.ts (ActivityData, ActivityPhoto, ActivityStats).
The Strava API returns more fields than the current types expose. Fields available from GET /activities/{id} not yet in ActivityStats: elev_high, elev_low, average_heartrate, max_heartrate, average_cadence, calories, gear.name. Photos come from GET /activities/{id}/photos?size=600 — each has urls, location[lat, lng], caption.
Refactoring to eliminate duplication is encouraged, even as part of a bug fix. If fixing a bug reveals that two call sites share identical logic, extracting a shared helper is the right move — don't leave duplicated code just because the primary goal was a bug fix.
Use Lucide React icons (import { IconName } from 'lucide-react'). Never use Unicode symbols (↩ ↪ ⊗ etc.) as UI icons. Inline SVG is acceptable only when no suitable Lucide icon exists.
Standalone HTML mockup files (mockup-a.html through mockup-h.html) live at the project root. These are design references only — not app code. The most current target design is mockup-h.html (combined map: activities + planner on a single full-bleed map surface). See project memory for design tokens and UX decisions.
@/* maps to the project root (configured in tsconfig.json).
- We are using a more recent version of nextjs where middleware was renamed to
proxy - There are limitations in nextjs on where cookies can be updated, which we
need to bear in mind