Frontend agent guide. Full detail: frontend/README.md and the root
CONTRIBUTING.md ("Working on the frontend"). This is the short version.
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/.src/api/, keys in src/api/queryKeys.ts);
invalidate on mutation. WebSocket pushes feed the cache
(src/api/websocketBridge.ts).useState); shared concerns via src/hooks/.
Extend an existing hook before adding a global.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).src/lib/xray/. HTTP goes through HttpUtil in src/utils/index.ts.// line comments in committed TS/TSX. HTML comments are fine.no-explicit-any is an error. Validate form fields with
antdRule(Schema.shape.field, t) from @/utils/zodForm, not inline
z.string().g.POST/g.GET route => add it to src/pages/api-docs/endpoints.ts,
then npm run gen.internal/web/translation/<locale>.json, NOT under
frontend/, and are shared with the Go backend. A new English key must be
added to every locale. Interpolation here uses single braces {var}, not the
i18next default {{var}}.^) — bump deliberately, then verify
npm run dev AND npm run build.src/pages/<page>/<Page>.tsx (kebab folder, PascalCase component).src/routes.tsx under /panel (lazy import).src/layouts/AppSidebar.tsx if it needs nav.
Only standalone bundles (login/subpage) need a new .html + src/entries/* +
rollupOptions.input (in vite.config.js) + a Go controller route.npm run dev (HMR on :5173, proxies to the Go panel on :2053 — start Go first).npm run typecheck / npm run lint / npm run test / npm run build.npm run gen = gen:zod (Go → src/generated/) + gen:api
(build-openapi.mjs → public/openapi.json).npm run build, RESTART go run . (see the XUI_DEBUG gotcha in root
CLAUDE.md) before checking the panel.