This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Development
pnpm dev # Start dev server
pnpm build # Production build (Next.js only)
pnpm build:next # Full build with asset downloading and sprite building
# Linting
pnpm lint:eslint # ESLint check
pnpm lint:eslint:fix # ESLint auto-fix
pnpm lint:tsc # TypeScript type check
pnpm lint:cspell # Spell check
pnpm lint:envs-validator:test # Validate environment variable schemas
# Testing
pnpm test:vitest # Unit tests (Vitest, matches **/*.spec.ts(x))
pnpm test:pw # Playwright component/E2E tests (matches *.pw.tsx)
pnpm test:pw:docker # Playwright tests in Docker
# Assets
pnpm svg:build-sprite # Rebuild SVG sprite
pnpm chakra:typegen # Regenerate Chakra UI typesRequirements: Node >=22.14.0, pnpm (see package.json engines / packageManager)
Stack: Next.js 15 (Pages Router, not App Router), React 19, Chakra UI v3, React Query 5, Wagmi 2 / Viem 2, Valibot for schema validation, Vitest + Playwright for testing.
Key directories:
pages/— Next.js page components (require default exports)ui/— React UI components organized by feature (~65 subdirectories)lib/— Business logic, API utilities, custom hooks, context providerstoolkit/— Design system layer:toolkit/chakra/(custom Chakra components),toolkit/theme/(semantic color tokens),toolkit/hooks/configs/app/— Runtime app configuration (features, API endpoints, UI settings)nextjs/— Next.js config utilities: headers, rewrites, redirects, type-safe routes vianextjs-routesmocks/— Mock data for testsdeploy/tools/envs-validator/— Environment variable validation schema and tests
Data flow: Pages use React Query for server state. Global UI state lives in React Context providers (AppContextProvider, SettingsContextProvider, etc.) initialized in pages/_app.tsx. WebSocket real-time data flows through SocketProvider.
Routing: Use nextjs-routes / nextjs/routes utilities for constructing links to application pages — never string concatenation. The full route list is in nextjs/nextjs-routes.d.ts.
These are enforced by ESLint and must be followed:
- Always import from
toolkit/chakra/**before falling back to native Chakra UI. If a custom version exists intoolkit/chakra/, use it. - Never use hardcoded colors (RGB, hex). Use semantic color tokens from
toolkit/theme/foundations/semanticTokens.tsandtoolkit/theme/foundations/colors.ts(e.g.,text.secondary,border.divider,icon.secondary). - No custom
box-shadow— use design system shadow tokens. - Don't override spacing on internal parts of compound components (e.g., don't add custom padding to
DialogHeaderinside aDialog). - Use
toolkit/chakra/linkinstead ofnext/link. - Use
lib/date/dayjs.tsinstead of importingdayjsdirectly. - Date/time rendering must use the shared
TimeorTimeWithTooltipcomponents.
- Never use
(window as any)to access third-party globals. Instead, declare the property inglobal.d.tsinside the existingdeclare global { interface Window { ... } }block. - Use
decs.d.tsonly for untyped third-party module declarations (declare module 'foo').
- Prefer
interfaceovertype. Useinterface extendsover&intersection (performance). - No
enum— useas constobjects instead. - Use top-level
import type { Foo }not inlineimport { type Foo }. - Default exports only when required by the framework (Next.js pages). All other exports are named.
- Declare return types on top-level module functions.
readonlyproperties by default; omit only when genuinely mutable.- Use
satisfiesfor type validation instead ofas MyType[]assertions. - Outside generic functions, use
anyextremely sparingly; preferunknownwith proper narrowing. - Extract magic numbers as
UPPER_SNAKE_CASEconstants above the component definition. - Define empty array/object defaults as static constants outside components (not inline
?? []). - Wrap
.filter(),.map(),.reduce()results inuseMemowhen passed as props or used as hook deps. - Type parameters in generics are prefixed with
T(e.g.,TKey,TValue).
When adding, renaming, or removing an environment variable, all of the following must be updated:
docs/ENVS.md— document name, type, whether required, default, and exampleconfigs/app/— add to the appropriate section (features/,ui.ts,api.ts, etc.)deploy/tools/envs-validator/schema.ts— add/update validation schemadeploy/tools/envs-validator/test/.env.base— add to test presetsnextjs/csp/policies/— update CSP if the variable references an external (non-asset) URLdeploy/scripts/download_assets.sh— add toASSETS_ENVSif it's an asset URL- If it's a JSON config URL: add example to
deploy/tools/envs-validator/test/assets/configs/and extendenvsWithJsonConfigindeploy/tools/envs-validator/index.ts
Vitest (unit): Files named *.spec.ts / *.spec.tsx. Run a single file with pnpm test:vitest path/to/file.spec.ts.
Playwright (component/E2E): Files named *.pw.tsx. Three test projects run against each test: default (desktop Chrome 1200×750), mobile (iPhone 13 Pro), dark-color-mode. Tag tests with @mobile or @dark-mode to target specific projects; use -@default to exclude desktop.
- Use roles, test IDs, and text content as selectors — never CSS class selectors.
- Import mock values from existing files in
mocks/rather than hardcoding them. - Avoid the
testFn: TestFixture<...>pattern unless sharing logic across multiple suites.