# frontend/CLAUDE.md Frontend agent guide. Full detail: `frontend/README.md` and the root `CONTRIBUTING.md` ("Working on the frontend"). This is the short version. ## What this is React 19 + Ant Design 6 + Vite 8 + TypeScript. The Vite config is `vite.config.js` (plain JS). Three bundles, each emitted into `internal/web/dist/` and embedded into the Go binary: - `index.html` — admin panel SPA (entry `src/main.tsx`; react-router under `/panel`, lazy routes). - `login.html` — login + 2FA (`src/entries/login.tsx`). - `subpage.html` — public subscription viewer (`src/entries/subpage.tsx`). The `@` import alias maps to `src/`. ## Data flow - Server state via TanStack Query (`src/api/`, keys in `src/api/queryKeys.ts`); invalidate on mutation. WebSocket pushes feed the cache (`src/api/websocketBridge.ts`). - Local UI state in the page (`useState`); shared concerns via `src/hooks/`. Extend an existing hook before adding a global. - Zod (`src/schemas/`) is the single source of truth for the xray config model. Infer types with `z.infer`. Go-side types are mirrored into `src/generated/` by `npm run gen:zod` (`go run ./tools/openapigen`) — do not hand-edit that folder (every file is marked `DO NOT EDIT`). - xray domain logic (links, defaults, form<->wire adapters) is pure functions in `src/lib/xray/`. HTTP goes through `HttpUtil` in `src/utils/index.ts`. ## Rules - Ant Design 6 for components; no Tailwind/shadcn (a migration was rolled back). Form *state* runs on React Hook Form (`src/components/form/rhf/`), not Ant Design's `Form` store. - Function components + hooks only; no class components. - No `//` line comments in committed TS/TSX. HTML comments are fine. - TS strict; `no-explicit-any` is an error. Build forms with `useZodForm` + `FormField` from `@/components/form/rhf` (wrap the tree in `FormProvider`); validate through the `zodResolver` or per-field `rules={{ validate: rhfZodValidate(Schema.shape.field) }}` — messages are Zod issue keys resolved via `t()`, never inline `z.string()`. AntD `