- AI responses: Always in Chinese (Simplified)
- Code: English only
- Commit messages: English, conventional commit format (
feat:,fix:,refactor:, etc.)
- React 18 + TypeScript 5 + Vite 5
- react-router-dom v7 (BrowserRouter, lazy-load all pages)
- TailwindCSS 3 (dark theme)
- Ant Design 5, shadcn/ui, Lucide React icons
- Valtio (proxy + useSnapshot + derive)
- Axios via
src/apis/request.ts - Framer Motion, GSAP
- Path alias:
@/→src/
| Directory | Purpose | Naming |
|---|---|---|
src/views/ |
Page-level components | default export; snake_case for frontiers |
src/components/ |
Reusable UI components | named export; kebab-case dirs |
src/apis/ |
API modules | {domain}.api.ts, default export object |
src/stores/ |
Valtio stores | {domain}.store.ts(x) |
src/hooks/ |
Custom hooks | use-{name}.ts |
src/router/routes/ |
Route definitions | {group}.routes.tsx |
src/utils/ |
Utilities | kebab-case |
| Kind | Convention | Example |
|---|---|---|
| Component | PascalCase | SubmitSuccessModal |
| Hook | use prefix |
useCountdown |
| Store file | {domain}.store.ts(x) |
user.store.tsx |
| API file | {domain}.api.ts |
frontiter.api.ts |
| Util file | kebab-case | auth-redirect.ts |
| View file | snake_case (frontier) / kebab-case | airdrop_food.tsx |
| Constants | UPPER_SNAKE_CASE | TEMPLATE_ID |
- Strict mode; avoid
any interfacefor object shapes,typefor unions/intersectionsRecord<K, V>over index signatures- Catch blocks: use
error.message
- Functional components + hooks only, never class components
- File order: imports → types → component → export
- Destructure props;
useCallbackfor prop callbacks;useMemoonly when expensive - Import order: react → third-party →
@/aliases → relative → assets @/alias for all imports, never relative../../
// src/apis/{domain}.api.ts
import request from '@/apis/request'
const xxxApi = {
getList: (params: P) => request.get<R>('/v1/xxx', { params }),
create: (data: D) => request.post<R>('/v1/xxx', data)
}
export default xxxApi- Never import raw
axios, always use@/apis/request - Never hardcode base URLs
// src/stores/{domain}.store.ts
const state = proxy({ ... })
export const useXxxStore = () => useSnapshot(state)
export const xxxStoreActions = {
update: (val) => { state.field = val }
}- Mutate proxy directly in actions, never mutate snapshot
- Use
derivefor computed values
// src/router/routes/{group}.routes.tsx
const Page = lazy(() => import('@/views/path/to/page'))- Frontier routes:
simpleTemplateRoute(id, Component)orappDetectorRoute(id, Web, App) useParams()for path params;useSearchParams()with{ replace: true }for query
- Dark theme: bg
#1C1C26, surface#252532, primary#875DFF - Container:
max-w-[1320px] mx-auto px-6 - Fonts:
font-mona,font-zendots,font-inter - Border:
border-[#FFFFFF1F], subtle bg:bg-[#FFFFFF0A] - Error text:
text-sm text-red-400 - Buttons:
h-[44px] rounded-full text-base font-bold - Responsive: mobile-first,
md:breakpoint - No inline styles; no
!important; no custom CSS unless necessary
- Auth pages: wrap with
<AuthChecker> - Loading:
<Spin spinning={loading}> - Notifications:
message.error()/message.success() - Icons:
lucide-react - Mobile:
AppContainerDetectorfor web/app split; mobile components use-appsuffix
- Wrap API calls in try/catch
message.error()for user-facing errors;console.error()for debug- Fallback:
error.message || 'Operation failed'
- State:
useState<FormData>({})+useState<Partial<Record<keyof FormData, string>>>({})for errors validateForm()returns boolean and sets errorsupdateFormData(field, value)auto-clears field error- Disable submit during loading; prevent double-submit
Preferred: use the interactive commitizen prompt (runs lint + commitlint):
git add <files>
npm run commitFollow the prompts: select type → enter short description (English) → skip the rest.
Allowed types: feat fix refactor docs test build other
Note: Claude Code cannot drive interactive CLI prompts, so when committing on your behalf it uses
git commit -mdirectly with a conforming message.
Use these slash commands for common workflows:
| Command | Description |
|---|---|
/new-page |
Add a new page with route registration |
/new-frontier-task |
Scaffold a new Frontier task template |
/build-check |
Run tsc + lint + build before merging |
/compress-images |
Compress image assets with sharp-cli |