## Language Always respond in Chinese (中文). Use the user's language for all conversations and explanations. Code, commands, and technical terms can remain in English. 页面内容(UI 文案、标签、提示、错误信息)默认使用 **英语**。仅当用户明确要求时才做多语言。 # Project Guidelines — appwebks ## Tech Stack - **Runtime**: Bun (packageManager, scripts, lockfile) - **Framework**: React 19, TypeScript 6.0, Vite 8.0 - **Styling**: Tailwind CSS 4.3 + clsx + tailwind-merge(CVA 变体模式) - **State**: @tanstack/react-query v5(服务端状态)+ URL search/path params(客户端状态) - **Routing**: react-router-dom v7 - **HTTP**: ky(优先)+ axios - **Validation**: zod v4 - **Icons**: lucide-react ## Project Structure ``` src/ pages/ # 页面组件,每个 page 一个文件夹 components/ # 共享组件,按功能域分文件夹 hooks/ # 自定义 hooks(含 react-query hooks) client/ # 自动生成 API 代码(只读!) lib/ # 工具函数、常量 types/ # 共享类型定义 ``` ## Routing 使用 React Router v7 **Data Mode**:`createBrowserRouter` + Route Object 配置。 - 路由配置集中在 `src/routes.ts`,通过 `createBrowserRouter` 组装 - 每个路由模块(Component + loader + action)为独立文件,放在 `src/pages/` 下 - 路由文件不容纳 JSX,只 export RouteObject[] ```ts // src/routes.ts — 路由配置,只组装不渲染 import { createBrowserRouter } from 'react-router'; import { RepoListPage } from '@/pages/RepoList/RepoListPage'; import { RepoDetailPage } from '@/pages/RepoDetail/RepoDetailPage'; import { RootLayout } from '@/pages/RootLayout'; export const router = createBrowserRouter([ { path: '/', Component: RootLayout, children: [ { index: true, Component: RepoListPage }, { path: 'repo/:id', Component: RepoDetailPage }, ], }, ]); ``` ```tsx // src/pages/RepoList/RepoListPage.tsx — 路由模块 export function RepoListPage() { const { data } = useRepoList(); return
...
; } ``` ## Code Style ### File Naming - React 组件文件:**PascalCase**(`Button.tsx`、`UserList.tsx`) - Hooks / utils / types 文件:**camelCase**(`useAuth.ts`、`formatDate.ts`) - 页面文件夹:**PascalCase**(`UserProfile/`、`RepoList/`) ### Component Conventions - 组件用 **function 声明**,不用 arrow function - Props 类型用 **interface**,写在组件文件顶部,不单独导出: ```tsx interface ButtonProps { size?: 'sm' | 'md' | 'lg'; variant?: 'primary' | 'secondary'; children: React.ReactNode; } export function Button({ size = 'md', variant = 'primary', children }: ButtonProps) { // ... } ``` - 原生 HTML 元素 Props 用 `React.ComponentProps<'button'>` 派生 - 单文件不超过 **200 行**,函数不超过 **50 行** - 复杂逻辑抽成 hooks ### Import Order Biome 的 `organizeImports` 会自动按以下顺序排列 import,组间空行分隔: 1. React / React Router / react-query 等核心库 2. 第三方 UI / 工具库(lucide-react、clsx、zod...) 3. `@/` 别名导入 4. 相对路径导入(`./`、`../`) ```tsx import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; import { Search, Plus } from 'lucide-react'; import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { repoService } from '@/client/services/RepoService'; import { Button } from '@/components/ui/Button'; import { useRepoList } from './useRepoList'; ``` ## Styling ### Tailwind + CVA 用 `clsx` + `tailwind-merge` 管理组件变体,按 CVA 模式组织: ```tsx import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; function cn(...inputs: (string | undefined | false | null)[]) { return twMerge(clsx(inputs)); } const variants = { size: { sm: 'px-2 py-1 text-sm', md: 'px-4 py-2 text-base', lg: 'px-6 py-3 text-lg', }, variant: { primary: 'bg-blue-600 text-white hover:bg-blue-700', secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300', }, } as const; interface ButtonProps { size?: keyof typeof variants.size; variant?: keyof typeof variants.variant; } export function Button({ size = 'md', variant = 'primary', ... }: ButtonProps) { return