diff --git a/.env.example b/.env.example index d1847516..f33d32f9 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,18 @@ # The Graph (optional — falls back to hardcoded values) THEGRAPH_API_KEY= + +# Notion — the roadmap register (optional locally; without it /roadmap falls +# back to content/roadmap/*.md). Internal integration token, shared with both +# the "Roadmap commitments" and "Roadmap people" databases. +NOTION_TOKEN= + +# Lets an agent that has just edited the roadmap in Notion push the change live +# immediately, instead of waiting out NOTION_REVALIDATE. Any long random string; +# the endpoint refuses to run when it is unset. +REVALIDATE_SECRET= + +# Set by the Notion webhook handshake — POST the subscription from Notion's +# Webhooks tab, read verification_token out of the deployment logs, and put it +# here. It is the signing key for every event after it and is not recoverable +# later; re-subscribe to get a new one. +NOTION_WEBHOOK_SECRET= diff --git a/.github/workflows/check-ecosystem-urls.yml b/.github/workflows/check-ecosystem-urls.yml index c2954e2a..3fcbf698 100644 --- a/.github/workflows/check-ecosystem-urls.yml +++ b/.github/workflows/check-ecosystem-urls.yml @@ -1,10 +1,12 @@ name: Check Ecosystem URLs on: - # Run on PRs that touch ecosystem data + # Run on PRs that touch ecosystem data. The catalogue is markdown in + # content/ecosystem — data/ecosystem.json was its old shape and no longer + # exists, so this trigger had stopped firing on the files it is meant to guard. pull_request: paths: - - "data/ecosystem.json" + - "content/ecosystem/**" - "public/ecosystem/**" # Weekly check — Mondays at 9am UTC @@ -23,13 +25,18 @@ jobs: steps: - uses: actions/checkout@v7 + # pnpm, not npm: the repo pins it via package.json's packageManager field + # and ships pnpm-lock.yaml. This step must precede setup-node so that + # `cache: pnpm` can find the store. + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v7 with: node-version: 20 - cache: npm + cache: pnpm - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile - name: Check ecosystem URLs run: node scripts/check-ecosystem-urls.mjs diff --git a/.gitignore b/.gitignore index 219d8bd0..7073f836 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ img/ .vscode/ *.code-workspace .playwright-mcp/ +.env* +!.env.example diff --git a/CLAUDE.md b/CLAUDE.md index d4602fd7..7df1d01e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,231 +1,130 @@ # CLAUDE.md -**Stack:** Next.js 15, React 19, TypeScript, Tailwind CSS v4, Framer Motion 11. Package manager: pnpm (v10.x, `packageManager` pinned). No test framework. +**Stack:** Next.js 16 (App Router, built and served with `--webpack` — the `.wgsl` loader and the watch tweaks in `next.config.ts` are webpack rules), React 19, TypeScript, Tailwind CSS v4, framer-motion 13. Package manager: **pnpm** (pinned via `packageManager` — use `corepack`). No test framework. ESLint runs `eslint-config-next` 16's flat configs directly; do not reintroduce the `FlatCompat` shim, which cannot serialise them. -**Environment variables** (set in Vercel / `.env.local`): +This repo is mid-migration to the **Livepeer UI design system** (a shadcn registry). Read the "Design system" section below before touching UI — it overrides older habits. -- `THEGRAPH_API_KEY` — optional; authenticated subgraph requests for live protocol stats (falls back to hardcoded values) +## Design system — Livepeer UI (source of truth) -## Commands - -- `pnpm dev` — start Next.js dev server on localhost:3000 -- `pnpm build` — production build (use to verify changes compile) -- `pnpm start` — start production server (requires prior build) -- `pnpm lint` — run ESLint - -## Project Structure - -### `app/` - -- **Routes**: home (`/`), blog, blog/[slug], primer, foundation, brand -- **Redirects**: developers, lpt, community → `/` via `redirect()`; use-cases/world-models → `/` -- **API routes**: none currently active. -- **Use cases**: `use-cases/` has 7 sub-routes — all currently redirect to home. No `/use-cases` index page. -- **Layout**: `layout.tsx` wraps all pages with Header + Footer. Global metadata and font classes defined here. -- **SEO**: OG images generated via `next/og` at root and `/blog` levels. Primer and blog pages have per-page metadata. -- **Styling**: `globals.css` — Tailwind v4 `@theme` block, utility classes, keyframe animations. - -### `components/` - -- **`home/`** — self-contained homepage sections. Render order in `app/page.tsx`: Hero, BuiltOnLivepeer, CommunityCTA. (`NetworkParticipants.tsx` exists but is not currently rendered.) -- **`layout/`** — Header, Footer. Header has headroom behavior on `/primer` (hides on scroll down, reveals on scroll up). -- **`ui/`** — shared primitives (Button, Card, Container, SectionHeader, Badge, ImageMask, GlowOverlay, etc.). Reuse these; don't create new wrappers for the same purpose. Also contains canvas components: `GenerativeCanvas.tsx` (GLSL shader), `LiveNetwork.tsx` (Canvas 2D particle trails), `AiVideoHero.tsx` (Sobel edge detection on video texture). All canvas components follow `useEffect` + `useRef` + `requestAnimationFrame` + cleanup. -- **`blog/`** — blog listing page (`BlogListingClient`, `BlogCategoryFilter`, `BlogPostCard`) and post detail (`BlogPostHeader`, `BlogPostContent`). -- **`primer/`** — 10-chapter educational primer with chapter navigation, scroll-based background color transitions, and live protocol stats from subgraph. Primer uses a light theme override. Components: `PrimerContent`, `InflationMeter`, `MintingDiagram`. -- **`icons/`** — `LivepeerLogo.tsx` exports `LivepeerSymbol` (icon), `LivepeerWordmark` (text), `LivepeerLockup` (icon + text). - -### `lib/` - -- `constants.ts` — `NAV_ITEMS` and `EXTERNAL_LINKS` -- `fonts.ts` — Favorit Pro and Favorit Mono config via `next/font/local` -- `useCountUp.ts` — IntersectionObserver-triggered count-up animation hook -- `blog.ts` — blog content loading, markdown→HTML pipeline (gray-matter + unified/remark/rehype), reading time calculation -- `subgraph.ts` — fetches live protocol stats (inflation, participation, supply) from The Graph Livepeer subgraph with hardcoded fallbacks - -### `public/` - -- `images/` — static images and SVGs (including `primer/` and `blog/` subdirectories) -- `videos/` — MP4 files for hero backgrounds and visual effects -- `fonts/` — Favorit Pro (.woff2, .otf) and Favorit Mono (.woff2) - -### `content/` - -- `blog/` — markdown blog posts with YAML frontmatter (title, description, date, author, category, tags, image, draft). Draft posts are hidden in production. - -### Reference docs - -- `brand-tokens.md` — full brand spec (colors, typography, logo, gradients, greyscale ramp, graphic elements) -- `livepeer-website-brief.md` — project brief with messaging and positioning context - -## Conventions - -### Component patterns - -- All homepage sections and interactive components use `"use client"`. -- Scroll animations: Framer Motion `whileInView` with `viewport={{ once: true }}` and staggered children. Hero/brand pages use `initial`/`animate` (fires on mount) instead. -- Section headers: use the `SectionHeader` component with `label`, `title`, `description` props. -- Section dividers: `.divider-gradient` divs between sections. -- Imports: path alias `@/*` maps to repo root. Use `@/components/...`, `@/lib/...`. - -### Assets - -- Static files in `public/images/`, `public/videos/`, `public/fonts/`. -- Videos use `autoPlay muted loop playsInline`. -- `globals.css` includes `prefers-reduced-motion` to blanket-disable all animations. - -### Grid system — "Holographik" visual language - -The site's signature visual is a layered grid system that combines B&W video/imagery, geometric shapes, animated particle trails, and liquid glass effects. This creates an "outer space control room" aesthetic — technical, cinematic, and distinctly Livepeer. - -**Layer stack (bottom to top):** - -1. **Media layer** — B&W video or image with green tint, darkened (`ImageMask`) -2. **Tile grid** — 9-column square grid with 1px white borders, overlaid on the media -3. **Geometric shapes** — circles, crosshairs, and a starburst node positioned at grid intersections -4. **Pulse trail** — an animated dot that traverses the grid lines and shape edges via SVG `getPointAtLength()`, creating a living-network feel -5. **Liquid glass overlay** — frosted panel with subtle gradient fill, chromatic rainbow refraction, specular highlights, and corner crosshairs, snapped to grid lines -6. **Content** — text, CTAs, rendered on top of everything - -**`ImageMask`** (`components/ui/ImageMask.tsx`) — Pure CSS grid, the core brand component. Uses `container-type: inline-size` with `100cqw / cols` row heights so tiles stay square relative to their container (not the viewport). Columns are `1fr`. Used on the homepage hero and brand page hero. The homepage overlays geometric shapes positioned with `${N * TILE}vw` units (`Hero.tsx`); the brand page uses percentage-based positions inside an `aspect-ratio: 9/5` wrapper. **Do not cap tile size independently on the homepage** — the vw-based positions of every overlay element depend on tiles spanning the full viewport. To control tile size on wide screens, adjust the column count or constrain the section width. - -### Don't - -- **No `next/image`** — use raw `` tags. `ImageMask` needs direct CSS filter/absolute stacking that `next/image`'s wrapper breaks. Primer SVGs are incompatible with required width/height props. WebGL components use `