Files
lfnovo--open-notebook/frontend/AGENTS.md
T
2026-07-13 12:10:23 +08:00

3.3 KiB

Frontend Rules (frontend/)

Normative rules for working on the Next.js frontend. Architecture and flow walkthroughs live in docs/7-DEVELOPMENT/frontend.md — this file is only what you must know before changing code. Project-wide rules are in the root AGENTS.md.

Commands (run inside frontend/)

  • Dev server: npm run dev (port 3000; API must be up at 5055 first)
  • Lint: npm run lint (eslint src/)
  • Tests: npm run test (vitest run) · coverage: npm run test:coverage
  • Build: npm run build

Hard rules

  • i18n is mandatory: every UI string goes through t('section.key') and the key must exist in all locales under src/lib/locales/ (currently 14; en-US is the reference). Missing keys fall back to en-US silently — keep locales in sync. Each non-en-US locale ends with satisfies TranslationShape (type derived from en-US), so a missing/extra key fails tsc; the parity test (src/lib/locales/index.test.ts) checks the same at runtime.
  • All requests go through apiClient (src/lib/api/client.ts); never create a second axios instance. Auth token is auto-added from localStorage key auth-storage.
  • Data fetching uses TanStack Query hooks in src/lib/hooks/ with QUERY_KEYS; mutations invalidate caches and show toasts (sonner). Follow the existing hook shape.
  • FormData requests: nested objects/arrays must be JSON.stringify-ed before appending; the interceptor strips Content-Type so the browser sets the multipart boundary — don't re-add it.
  • No automatic request retry — handle failures in the consuming code (podcast retry is an explicit endpoint/hook, not a client retry).

Gotchas

  • Zustand stores with persist: check hasHydrated before rendering persisted state (SSR hydration mismatch), and keep each store's name key unique (localStorage collision).
  • NEXT_PUBLIC_API_TIMEOUT_MS: default 600000 (10 min) for slow LLM calls; 0 disables the timeout.
  • SSE (useAsk): incomplete lines stay in the buffer between reads; incomplete JSON is silently skipped — don't "simplify" the buffer handling.
  • useSourceStatus polls every 2s while status is running/queued/new.
  • Cache invalidation is deliberately broad (e.g. ['sources'] hits all source queries) — a simplicity/perf trade-off, be precise only when it matters.
  • Dark mode requires the dark class on the document root, not just prefers-color-scheme. It's hand-rolled: the zustand theme-store (persisted as theme-storage) sets the class via ThemeProvider — no next-themes.
  • Dialogs don't auto-reset form state (parent clears it) and auto-focus the first input (layout shift risk with conditional inputs). Fixed overlays use z-50.
  • Provider nesting order in app/layout.tsx matters: ErrorBoundary → ThemeProvider → QueryProvider → I18nProvider → ConnectionGuard → Toaster. ErrorBoundary is a class component and uses the raw enUS locale object (no hooks).
  • i18n runs with useSuspense: false (no SSR for translations).

Deep dives

frontend architecture · credentials system · change playbooks (i18n / frontend-only changes) · code standards