# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**GPUStack UI** — Dashboard/management UI for [GPUStack](https://github.com/gpustack/gpustack), a GPU model serving and management platform. Features include model deployment, playground (chat/embedding/rerank/image/speech), resource management, cluster management, benchmark, and access control.

## Tech Stack

- **Framework**: UmiJS Max 4.x (`@umijs/max`) — enterprise React framework with built-in routing, state, request, access, locale, and layout plugins
- **UI Library**: Ant Design 6.x (`antd`) + Pro Components (`@ant-design/pro-components`)
- **Core UI package**: `@gpustack/core-ui` — shared internal UI components
- **State Management**: Jotai (atomic state via `src/atoms/`)
- **HTTP Client**: Axios via Umi request layer
- **Build Tool**: Webpack (via Umi)
- **Language**: TypeScript
- **Package Manager**: pnpm
- **Styling**: Less + CSS modules + styled-components + antd-style

## Key Commands

```bash
# Install dependencies
pnpm install

# Start dev server (http://localhost:9000)
npm run dev          # or: pnpm dev

# Production build
npm run build

# Preview build output
npm run preview

# Format code
npm run format

# Check locale files
npm run check:locales
```

Environment variables:

- `PROXY_HOST` — API proxy target for dev server
- `ENABLE_ENTERPRISE` — enable enterprise features
- `ENABLE_PLAYGROUND` — enable playground section (default: true)
- `APP_TITLE` — custom app title

## Project Structure

```
config/                 # Umi build configuration
  config.ts             # Main config (proxy, build, plugins, routes)
  routes.ts             # Route definitions (main entry for navigation structure)
  routes.extensions.ts  # Build-time route extensions
  proxy.ts              # Dev proxy configuration
  plugins.ts            # Webpack plugins (compression, monaco)
  keep-alive.ts         # Route keep-alive configuration
  mfsu.extensions.ts    # MFSU exclusions
src/
  app.tsx               # App entry: getInitialState, request config, plugin initialization
  access.ts             # Role-based access control (canSeeAdmin, canSeeOrgAdmin, etc.)
  request-config.tsx    # Axios interceptors, error handling, 401 redirect
  global.tsx            # Global setup (dayjs plugins)
  global.less           # Global styles
  atoms/                # Jotai atomic state (user, settings, models, clusters, etc.)
  config/               # Runtime config (settings, theme, hotkeys, breakpoints)
  layouts/              # Custom layout system (sidebar, menu, logo, error boundary)
  pages/                # Page components (feature-based subdirectories)
    _components/        # Shared page-level components
    dashboard/          # Dashboard
    playground/         # Chat, embedding, rerank, images, speech
    llmodels/           # Model catalog, user models, deployments
    model-routes/       # Model routing
    usage/              # Usage statistics
    maas-provider/      # Model providers
    benchmark/          # Benchmark tests
    gpu-service/        # GPU service (instances, templates, storage, public keys)
    resources/          # Workers, GPUs, backends, modelfiles
    cluster-management/ # Clusters, credentials
    users/              # User management
    api-keys/           # API key management
    login/              # Login page
    profile/            # User profile
  components/           # Global shared components
  plugins/              # Plugin system (manager, enterprise-ready, locale merger)
  services/             # API service layer (minimal, most API calls are inline)
  locales/              # i18n locale files
  assets/               # Images, logos, provider logos
  request.extensions.ts # Build-time request interceptor hooks (empty by default)
  access.extensions.ts  # Build-time access extension hooks (empty by default)
```

## Architecture Patterns

### Routing

- Hash-based routing (`history.type: 'hash'`)
- Routes defined in `config/routes.ts` with icon, access control, and keep-alive support
- Route extensions applied via `config/routes.extensions.ts` for build-time plugin injection

### Authentication & Access Control

- Umi `access` plugin with predicate-based role checks
- Key predicates: `canSeeAdmin`, `canSeeOrgAdmin`, `canManageCurrentOrg`, `canSeeUser`, `canDelete`, `canLogin`
- 401 responses trigger logout → redirect to `/login`
- First-login flow redirects admin to `/resources/workers`

### State Management

- **Jotai atoms** in `src/atoms/` for global state (user info, settings, pagination, route cache, etc.)
- **Umi `initialState`** for currentUser and fetchUserInfo
- Persistent storage via `setAtomStorage` / `clearAtomStorage` utilities

### HTTP Requests

- Base URL: `/v2` (GPUSTACK_API_BASE_URL)
- OpenAI-compatible APIs: `/v1`
- Special paths (`/auth`, `/v1`, `/version`, `/proxy`, `/update`) bypass the API base URL
- Request/response interceptors in `src/request-config.tsx`
- Extension point: `src/request.extensions.ts` (empty by default, populated by build-time tooling)

### Plugin System

- `GPUStackPluginManager` in `src/plugins/manager.ts`
- Plugins register with `onAppInit` and `onAppReady` lifecycle hooks
- Enterprise plugin loaded via `src/plugins/enterprise-ready.ts`
- Build-time extensions populate `*.extensions.ts` files

### Layout

- Custom layout in `src/layouts/` with sidebar, menu, logo, right panel
- Error boundary and exception pages
- Render links and extra render hooks for plugin injection

### Internationalization

- Umi locale plugin with `en-US` default
- Ant Design locale auto-synced
- Locale files in `src/locales/`

## Important Notes

- The `.extensions.ts` files (`routes.extensions.ts`, `request.extensions.ts`, `access.extensions.ts`, etc.) are **generated at build time** by external tooling — do not edit them directly
- Monaco Editor is integrated via `@monaco-editor/react` and `monaco-editor-webpack-plugin`
- XTerm.js used for terminal/console output
- Markdown rendering with `react-markdown` + GFM + KaTeX math support
- The project uses hash routing, so all routes have `#/` prefix in the browser
