mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-18 01:00:59 +00:00
8cec47a8a5
The three prompts still enforced the comment ban CLAUDE.md replaced with
the 2-line cap on Aug 1 (1ff90c5b), so the review bot would flag every
legitimate short comment; frontend/CLAUDE.md and CONTRIBUTING.md carried
the same stale rule. The PR reviewer's recipe for reading a post-change
file (headRefOid + pr diff) was unfulfillable with its allowlist - it now
fetches refs/pull/N/head and reads blobs via git show, object-only, no
checkout. Conventions the reviewer checks now include the unchecked docs
openapi.json copy step, the docs/lib/xray third link implementation, the
both-ways route contract, and the i18n dead-key half of the rule.
Also: drop the SUBPROCESS_ENV_SCRUB=0 override on the two untrusted-input
jobs (the mention job proves gh works scrubbed); teach the triage prompt
the issue forms (pre-applied labels, required fields, no re-asking); add
a security-report exception plus SECURITY.md so vulnerabilities are not
confirmed publicly; add a clarification follow-up job so a reporter's
reply to "clarification needed" is actually processed; review PRs again
on ready_for_review and skip drafts; stamp the reviewed head SHA so
force-pushes visibly date a review; scope gh issue/pr edit to label and
title flags; per-job concurrency; comment guards now match the actual
bot login after the run started; artifact names survive re-runs; the
mention prompt's repo map and env-var facts corrected (XUI_PORT,
XUI_TUNNEL_HEALTH_*, distro env files, memory.high, encrypt-tokens).
The bug and feature forms also referenced a "needs triage" label that
does not exist in the repo and was silently never applied - dropped.
4.0 KiB
4.0 KiB
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 (entrysrc/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 tosrc/.
Data flow
- Server state via TanStack Query (
src/api/, keys insrc/api/queryKeys.ts); invalidate on mutation. WebSocket pushes feed the cache (src/api/websocketBridge.ts). - Local UI state in the page (
useState); shared concerns viasrc/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 withz.infer. Go-side types are mirrored intosrc/generated/bynpm run gen:zod(go run ./tools/openapigen) — do not hand-edit that folder (every file is markedDO NOT EDIT). - xray domain logic (links, defaults, form<->wire adapters) is pure functions in
src/lib/xray/. HTTP goes throughHttpUtilinsrc/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'sFormstore. - Function components + hooks only; no class components.
- Comments in committed TS/TSX: 2 lines MAX per comment block, spent on the why a name cannot hold (same rule as root CLAUDE.md). HTML comments are fine.
- TS strict;
no-explicit-anyis an error. Build forms withuseZodForm+FormFieldfrom@/components/form/rhf(wrap the tree inFormProvider); validate through thezodResolveror per-fieldrules={{ validate: rhfZodValidate(Schema.shape.field) }}— messages are Zod issue keys resolved viat(), never inlinez.string(). AntD<Form>stays only as a layout wrapper. Complex shared config editors (FinalMask / Sniffing / Sockopt) remain AntD-Formislands wrapped as value/onChange adapters insrc/lib/xray/forms/fields/, bound via aController. - New
g.POST/g.GETroute => add it tosrc/pages/api-docs/endpoints.ts, thennpm run gen. - i18n strings live in
internal/web/translation/<locale>.json, NOT underfrontend/, 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}}. - Persian/Arabic (RTL) users are first-class — isolate code identifiers on their own line when writing Persian text in labels/toasts.
- Vite is pinned to an exact version (no
^) — bump deliberately, then verifynpm run devANDnpm run build.
Adding a panel route
src/pages/<page>/<Page>.tsx(kebab folder, PascalCase component).- Register in
src/routes.tsxunder/panel(lazy import). - Add a sidebar link in
src/layouts/AppSidebar.tsxif it needs nav. Only standalone bundles (login/subpage) need a new.html+src/entries/*+rollupOptions.input(invite.config.js) + a Go controller route.
Commands
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 storybook(workbench on :6006) /npm run build-storybook(CI compile-checks every story). Reusablesrc/components/get a co-located<Component>.stories.tsxwithtags: ['autodocs']; document props viaargTypes/parameters.docsstring metadata, never JSDoc.- After
npm run build, RESTARTgo run .(see the XUI_DEBUG gotcha in root CLAUDE.md) before checking the panel.