You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The panel is a React 19 single-page application built with Vite, Ant Design, Zustand, and React Router v7. It communicates with the daemon via a REST API and Server-Sent Events (SSE). The panel runs in two modes:
Browser (dev/standalone): The daemon serves the panel over HTTP. The panel communicates via fetch to the daemon's API endpoint (default http://127.0.0.1:6767).
Tauri desktop shell: The daemon runs as a sidecar process. Lifecycle control (start/stop) delegates to Rust commands. API calls go through http://127.0.0.1:6767 as well.
Architecture Overview
The panel follows a feature-based directory structure under packages/panel/src/features/. Each feature contains its own components/, hooks/, services/, and domain/ directories:
packages/panel/src/
├── app/ # App entry, routing, theme
│ ├── App.tsx
│ ├── routes.tsx
│ └── theme/
│ ├── antdTheme.ts
│ └── ThemeProvider.tsx
├── features/
│ ├── dashboard/ # Landing page with status overview, quick launch, shell setup
│ ├── history/ # Session archive, provider/model stats, request logs
│ ├── live-session/# Live monitoring of currently active sessions
│ ├── logs/ # Real-time server log viewer via SSE
│ ├── model-chain/ # Model fallback chain editor with drag-and-drop
│ ├── openai-gateway/ # Local OpenAI-compatible endpoint, key, models, and examples
│ ├── providers/ # Provider management: API keys, OAuth, model selection, favorites
│ ├── routing/ # Tier-based routing rules (default/opus/sonnet/haiku)
│ ├── settings/ # Server, web tools, proxy, and token saver configuration
│ └── shell/ # App layout: sidebar, top bar, daemon lifecycle controls
└── shared/
├── api/ # HTTP client, base URL resolution
├── components/ # Reusable UI components (PageHeader, LoadingState, etc.)
├── hooks/ # Shared React hooks (useSSE, usePolling, etc.)
└── utils/ # Time formatting, etc.
All features use React Router lazy loading — pages are code-split and loaded on demand. The routes are defined in app/routes.tsx.
Shared Modules
Before diving into features, here are the foundational modules every feature depends on.
API Client (shared/api/)
The API client resolves the daemon URL and provides typed HTTP helpers.
File
Purpose
base.ts
Resolves the API base URL. In Tauri mode, prepends http://127.0.0.1:6767. In browser dev mode, uses relative /api/... paths. Controlled by VITE_CC_GATEWAY_EXTERNAL_DAEMON env var.
client.ts
Core request<T>(path, init) function wrapping fetch. Sets Content-Type: application/json. Throws ApiError on non-OK responses.
http.ts
Convenience object: http.get<T>, http.put<T>, http.post<T>, http.delete<T>. All return Promise<T>.
Exports antdTheme: ThemeConfig — the full Ant Design theme object with tokens and component overrides for Layout, Card, Table, Menu, Button, Input, Select, Modal, etc.
ThemeProvider.tsx
Wraps children in <ConfigProvider theme={antdTheme}><App>{children}</App></ConfigProvider>. Used at the root in main.tsx.
Feature: Shell (features/shell/)
The shell feature provides the application chrome — layout, navigation, and daemon lifecycle controls.
Components
Component
Purpose
AppShell
Root layout using Ant Design Layout with Sidebar + TopBar + Outlet. Handles lazy route loading with a Skeleton fallback.
Sidebar
Left sidebar with brand logo, navigation menu, GitHub link, and version display. Uses useLiveIndicator() to pulse the "Live Sessions" nav item. Collapsible.
TopBar
Top header bar showing daemon status (running/stopped/checking) with a Badge indicator and a Start/Stop control button.
navItems
Navigation menu configuration — maps each route to an icon and label.
Hooks
Hook
Purpose
useDaemonStatus
Polls GET /api/status every 5 seconds. Returns state ("running"/"offline"/"unknown"), status, refresh(), pause(), resume().
useGatewayControl
Wraps useDaemonStatus with start/stop actions. Start delegates to Tauri start_daemon (in Tauri mode) or shows an error (dev mode). Stop calls POST /api/control/shutdown (dev) or Tauri stop_daemon. Includes a lifecycle lock to prevent concurrent start/stop.
useLiveIndicator
Polls GET /api/sessions every 10 seconds to check if any live sessions exist. Returns a boolean. Used by the sidebar to show a processing dot on the Live Sessions nav item.
Services
Service
Purpose
daemonControl.ts
Daemon lifecycle commands: start(), stop(), canStartFromPanel(). Dual-runtime: delegates to Tauri invoke for desktop, HTTP POST /api/control/shutdown for dev.
Domain
DaemonState: "running" | "offline" | "unknown" — state returned by useDaemonStatus.
STATE_LABEL / STATE_BADGE: Maps DaemonState to display labels and Ant Design badge statuses.
Feature: Dashboard (features/dashboard/)
The landing page. Composed of four main card sections.
Components
Component
Purpose
DashboardPage
Main page layout combining all dashboard cards. Conditionally shows ShellSetupCard based on shell setup state.
StatusOverview
Shows gateway running status, uptime, top model (most-used model from session history), and daemon PID.
EnabledProvidersCard
Displays per-provider stats (request count, avg latency, last activity) for enabled providers.
QuickLaunchCard
Copyable CLI launch commands for quick provider selection: "All providers", "All Model Chains", and per-provider flags.
LiveLogsPanel
Real-time streaming log viewer (last 1000 lines) embedded on the dashboard. Uses SSE.
ShellSetupCard
Guided shell integration setup: shows shell-appropriate ccpg command aliases, provides one-click install for zsh/bash/fish/powershell. Expandable/collapsible with dismiss.
ShellInstallActions
Per-shell install buttons with status feedback.
ShellCommandCopyBox
Copy-to-clipboard box for the shell snippet.
ProviderStatCard
Individual provider stat display card.
Hooks
Hook
File
Purpose
useDashboardPage
useDashboardPage.ts
Orchestrator hook composing useGatewayStatus, useLaunchCommands, and useShellSetup. Computes topProviders and topModel from session history. Handles shell setup card dismiss state via localStorage.
useGatewayStatus
useGatewayStatus.ts
Polls GET /api/status, GET /api/stats, and GET /api/sessions every 5 seconds. Returns status, stats, sessions, isLoading.
useLaunchCommands
useLaunchCommands.ts
Fetches GET /api/quick-launch once on mount. Derives LaunchItem[] for quick launch cards including CLI flags.
useShellSetup
useShellSetup.ts
Fetches GET /api/shell-setup once on mount. Returns setup and refresh().
useShellSetupCard
useShellSetupCard.ts
Manages shell install flow: install(shells, key), install state, open/collapsed state. Announces install results via Ant Design message.
useLiveLogs
useLiveLogs.ts
Subscribes to SSE at /api/logs. Maintains last 1000 lines with paused, clear, and togglePaused controls.
useStatusOverview
useStatusOverview.tsx
Builds the 4 status cards: running status, uptime, top model (from session history), and daemon PID.
useProviderStatCard
useProviderStatCard.ts
Fetches per-provider models for a single stat card.
Services
Service
Endpoints Used
dashboardService
GET /api/status, GET /api/stats, GET /api/sessions, GET /api/launch-commands, GET /api/quick-launch, GET /api/shell-setup, POST /api/shell-setup/install
Monitors all currently active Claude Code sessions in real time.
Components
Component
Purpose
LiveSessionPage
Shows all active sessions with a "RUNNING"/"IDLE" badge, aggregate metric summary, and one collapsed-by-default panel per session containing metadata, provider stats, models used, and request log. Auto-refreshes every 5 seconds.
Hooks
Hook
Purpose
useLiveSession
Polls GET /api/sessions every 5 seconds, extracting currentSessions (all active sessions). Uses a request ID ref to ignore stale responses. Returns sessions, isLoading, refresh(), pollIntervalMs.
useLiveIndicator
Lightweight poll (every 10s) that checks if current is non-null. Used by the Sidebar for the pulsing dot indicator.
Relation to History
The live session page reuses components from the history feature:
SessionMetadataCards — metadata display
ProvidersTable — per-provider stats table
ModelsUsedTable — per-model stats table
RequestLogTable — request log entries
Feature: History (features/history/)
Displays archived Claude Code sessions with detailed breakdowns.
Components
Component
Purpose
HistoryPage
Main page: summary stats, session list with expandable rows, per-session JSON export, and clear-history controls.
Table of individual API requests within a session (model, provider, latency, status, response/preview availability, warnings).
RequestDetails
Detailed view of a single request log entry, including the human-readable prompt, sanitized provider request preview, conversion warnings, and response preview.
ProvidersTable
Per-provider aggregate stats within a session (or globally).
ModelsUsedTable
Per-model aggregate stats within a session (or globally).
TableExpandButton
Toggle to expand/collapse a session row.
SectionLabel
Section header used in table expand rows.
Hooks
Hook
Purpose
useHistory
Polls GET /api/sessions and GET /api/stats every 5 seconds. Manages sessions, provider stats, totals, expanded keys, clear/delete/export operations.
useHistoryPage
Orchestrator hook that filters and sorts sessions, derives top provider/model info, manages the clear modal.
Services
Service
Endpoints Used
historyService
GET /api/sessions, GET /api/stats, DELETE /api/sessions (clear archive), DELETE /api/sessions/:id (delete single session)
sessionExport
Exports a single SessionRecord as formatted JSON. In Tauri it invokes save_session_json; in browser/dev fallback it downloads a Blob.
Domain
File
Purpose
types.ts
Type aliases: Session, ModelStat, RequestLogEntry. Imports contract types from daemon.
metrics.ts
getTopProviderInfo(sessions), getTopModelInfo(sessions) — computes top providers/models from session data.
useHistory polls /api/sessions and /api/stats every 5 seconds.
Sessions are stored in archive (sorted newest-first) with per-session requestLog, modelStats, and providerStats.
SessionsTable renders sessions as expandable Ant Design table rows.
Expanding a row renders SessionDetails, which shows RequestLogTable from the session's requestLog entries.
Exporting a row serializes that session to session-{id}.json. Desktop builds save it through Tauri to the user's Downloads directory; browser/dev builds use a normal Blob download.
Global aggregate stats come from the /api/stats endpoint.
Feature: Logs (features/logs/)
Real-time server log viewer using Server-Sent Events.
Components
Component
Purpose
ServerLogsPage
Main page: log summary cards (error/warn/info/debug counts), toolbar (search, level filter, pause, wrap, line numbers, download), and the LogViewer terminal.
LogsSummary
Card grid showing log level counts.
LogViewer
Virtualized log display terminal with syntax highlighting per log level. Auto-scrolls to bottom (unless user scrolls up).
LogsToolbar
Toolbar with search input, level filter dropdown, pause/resume, wrap toggle, line number toggle, clear, and download buttons.
Hooks
Hook
Purpose
useServerLogs
Subscribes to SSE at /api/logs. Maintains last 5000 lines. Provides filteredLogs (by level + search), stats (counts per level), and all toolbar state (paused, search, levelFilter, wrapLines, showLineNumbers). Includes downloadLogs() to export as .log; desktop builds invoke save_server_logs, while browser/dev builds use a Blob download.
useLogViewerScroll
Manages auto-scroll behavior. Tracks whether the user is at bottom, scrolls to bottom on new lines when unpaused and at bottom.
getLogLevelColor(level) — Maps levels to Ant Design semantic colors.
getLogLineBackground(level) — Returns subtle background tint for error/warn lines.
detectLogLevel(line) — Regex-based level detection from raw log lines.
Feature: Model Chain (features/model-chain/)
Editor for model fallback chains. When the primary model fails (rate limit, error, empty/malformed stream, or pre-content stream idle), the gateway falls back to the next model in the chain.
Components
Component
Purpose
ModelChainPage
Main page: list of chains with enable/disable toggles, edit/delete actions, "Economy/Local" preset, and "New Chain" button.
ChainCard
Card displaying a single chain with its models list, enable toggle, copy snippet button, and edit/delete actions.
ChainModal
Modal for creating/editing a chain. Includes name, slug (auto-derived), enable toggle, routing strategy, primary attempts, advanced chain timeout settings, and drag-and-drop model list.
SortableModels
Drag-and-drop list container using @dnd-kit/sortable.
SortableModelRow
Individual draggable model row with provider + model label and delete button.
AddModelRow
Form row to add a new model: provider dropdown + model dropdown (filtered by selected provider).
CopySnippet
Copies the CLI snippet for launching with a specific chain (ccpg --chain-slug).
Hooks
Hook
Purpose
useModelChainPage
Fetches GET /api/config (model fallbacks/provider state) and GET /api/routing/options. Manages chain list state, enabled providers, persist(), deleteChain(), toggleChainEnabled().
useChainDraft
Manages the chain edit modal: openNew(), openEdit(chain), saveDraft(), cancelEdit(). Normalizes slugs via normalizeSlug().
Services
Service
Endpoints Used
modelChainService
GET /api/config (model fallbacks), GET /api/routing/options, PUT /api/config (save model fallbacks)
Compact setup page for external OpenAI-compatible clients. It shows the local
base URL, API key, endpoint paths, searchable active model picker, and copyable
curl examples for /v1/models and /v1/chat/completions.
Components
Component
Purpose
OpenAIGatewayPage
Main page layout with endpoint fields, client setup details, active model picker, and examples.
ActiveModelsCard
Searchable popover/command-palette style model picker that fetches /api/openai-gateway/models and copies selected model IDs.
CopyableField
Compact read-only field with copy action for URLs, API keys, and commands.
ExampleCard
Displays ready-to-run curl examples from the daemon.
Services
Service
Endpoints Used
openaiGatewayService
GET /api/openai-gateway, GET /api/openai-gateway/models
The model picker displays the same public IDs returned by OpenAI-compatible
GET /v1/models, including short IDs such as <provider>/<model> and any
provider-specific aliases.
Feature: Providers (features/providers/)
The most feature-rich module. Manages built-in and user-created LLM providers: API key configuration, OAuth login, custom OpenAI/Anthropic-compatible provider creation, runtime limits, model selection, connection testing, and favorites.
Components
Component
Purpose
ProvidersPage
Main page: search bar, status filter, provider grid grouped by configuration type, custom provider section, favorites toolbar, and custom-provider modal state.
ProviderToolbar
Search input + status filter (all/enabled/disabled/configured/unconfigured).
ProviderTabs
Tab bar with "Favorites" and "All Providers" tabs plus right-aligned tab actions for adding OpenAI- or Anthropic-compatible custom providers.
ProviderGridSection
Renders a group of provider cards with a section title. Built-in groups render first; the custom provider section renders last on the All Providers tab.
SortableFavoritesGrid
Drag-and-drop grid for favorite providers using @dnd-kit.
ProviderCard
Individual provider card: logo, label, enabled switch, test button, status indicator, favorite star.
ProviderCardSkeleton
Loading skeleton for a provider card.
ProviderLogo
Provider logo image. Built-ins load from packages/panel/public/providers/; custom providers can use daemon-served logoUrl files uploaded by the user.
AddCustomProviderModal
Creates OpenAI-compatible or Anthropic-compatible custom providers from name, immutable slug, base URL, API key, optional PNG/WebP logo, and a connection test that can return discovered models.
ProviderConfigModal
Full-featured configuration modal. Contains sections for API key, OAuth, base URL, runtime limits, model picker, manual/extra models, and custom-provider deletion.
ProviderConfigContent
Modal content orchestrator — renders the appropriate sections based on provider type. Custom providers show Custom Base URL, API Key Authentication, then Manual models before the shared sections.
ApiKeySection
API key input with preview, reveal/hide, and remove.
OAuthSection
OAuth login/logout buttons with status display.
OAuthProviderSettings
OAuth-specific UI for device flow providers (GitHub Copilot, Kilo Code) showing user code and verification URI.
BaseUrlSection
Custom base URL override input.
RuntimeLimitsSection
Collapsible Advanced settings section for per-provider maxConcurrency, rateLimit, and rateWindow. Shows only when the provider is usable and explains that 0 disables a limit.
ModelPickerSection
Enabled/disabled model toggle list with search. Uses useModelSelector.
ExtraModelsSection
Add/remove additional model IDs not in the auto-discovered list. Custom providers label this surface as Manual models.
ModelSelector
Dual-list model picker: search, toggle individual models, toggle all, expand/collapse.
Prompts the user to enter their device code when the daemon returns a Copilot device flow.
Hooks
Hook
Purpose
useProviders
Core hook: fetches GET /api/providers, manages test(), toggleEnabled(), saveKey(), removeKey(), saveBaseUrl(), addModel(), removeModel(), setDisabledModels(), setRuntimeLimits(), testCustom(), createCustom(), and deleteCustom().
useProvidersPage
Page orchestrator: composes useProviders, useOAuth, useFavorites. Manages search, status filter, selected provider for modal, custom-provider compatibility modal, and confirm action state.
useOAuth
Manages OAuth flows: startBrowserFlow(id) and startDeviceFlow(id). Polls GET /api/providers/:id/oauth/status/:key until login completes or times out. Handles logout(). Device flow opens verification URI in external browser.
useFavorites
Persists favorite provider order to backend via GET /api/config and PUT /api/config (panel settings). Handles toggleFavorite(), reorderFavorites(), dismissTip().
useModelSelector
Model selection logic: search filtering, toggle individual/all, counts (active/total).
useProviderModels
Fetches GET /api/models/:id lazily when the config modal opens. Returns models, loading, load().
Services
Service
Endpoints Used
providersService
GET /api/providers, POST /api/providers/:id/test, GET /api/models/:id, PUT /api/config (provider config), custom provider endpoints (POST /api/custom-providers/test, POST /api/custom-providers, DELETE /api/custom-providers/:id), OAuth endpoints (/providers/:id/oauth/start, /providers/:id/oauth/status/:key, /providers/:id/oauth/logout)
Domain
File
Purpose
types.ts
Type aliases: ProviderInfo (including custom, customCompatibility, and logoUrl), ModelInfo, TestResult, OAuthInfo, OAuthStatusResponse, CopilotFlow, ConfirmAction.
getProviderKind(provider) — "local" / "oauth" / "api-key". isProviderReady(provider) — whether the provider is configured and ready to use. canTestProvider(provider) — whether test is possible (enabled + ready).
providerGroups.ts
groupProvidersByConfiguration(providers) — sorts built-in providers into Local / OAuth / API Key groups, sorted by label. Custom providers are rendered separately at the end of All Providers.
providerFilters.ts
filterProviders(providers, { searchTerm, status }) — filters by search text and status.
utils.ts
mergeModelLists(value) — deduplicates and trims model IDs. stripModelPrefix(displayName) — removes "provider · " prefix.
oauthPresentation.ts
Presentation helpers for OAuth provider labels and descriptions.
apiKeyLinks.ts
Maps provider IDs to their API key dashboard URLs (e.g., OpenAI's platform.openai.com/api-keys).
data/suggestedModels.ts
SUGGESTED_MODELS — curated model suggestions for ~20 providers (GLM, Kimi, MiniMax, Qwen, DeepSeek, etc.). Used to pre-populate the "Add Model" input with common model IDs.
Feature: Routing (features/routing/)
Configures tier-based routing rules that override which provider+model handles specific Claude model tiers.
Components
Component
Purpose
RoutingPage
Main page: save button, four tier cards (default/opus/sonnet/haiku), thinking toggle.
TierCard
Card for a single routing tier. Shows tier description, enabled toggle, provider dropdown, model dropdown. Color-coded border glow.
TierCardSkeleton
Loading skeleton for a tier card.
ThinkingToggle
Toggle switch for thinking mode (extended reasoning).
Hooks
Hook
Purpose
useRouting
Fetches GET /api/config (routing) and GET /api/routing/options. Manages routing rules map (RoutingMap), thinking toggle, updateRule(), and save(). Sanitizes rules before save (disables rules with empty provider/model).
useTierCard
Derives display data for a single tier: provider options, model options (filtered by selected provider), validation (missing model detection), enable-ability.
Services
Service
Endpoints Used
routingService
GET /api/config (routing), GET /api/routing/options, PUT /api/config (save routing + thinking)
default: Catch-all — applies when no tier-specific rule matches.
opus: Overrides claude-opus-* model requests.
sonnet: Overrides claude-sonnet-* model requests.
haiku: Overrides claude-haiku-* model requests.
Feature: Settings (features/settings/)
Configures server, web tools, proxy, and token saver settings.
Components
Component
Purpose
SettingsPage
Main page with save button and four settings sections.
SettingsSection
Wraps a settings card with a title and description.
SettingsCard
Base card container for a settings group.
ServerCard
Server port and host configuration (Ant Design Form).
WebToolsCard
Web tools toggle + "Allow private networks" checkbox.
ProxyCard
Proxy enable toggle + URL input.
TokenSaversCard
RTK and Caveman toggles + Caveman level selector ("lite"/"full").
Hooks
Hook
Purpose
useSettings
Fetches GET /api/config (settings subset). Manages server form, web tools, proxy, and token savers state. Handles save() with optimistic update and save feedback.
Services
Service
Endpoints Used
settingsService
GET /api/config (settings), PUT /api/config (server + webTools + proxy + tokenSavers)
Check if running in Tauri (window.__TAURI_INTERNALS__)
If Tauri and not external dev daemon: prepend http://127.0.0.1:6767
Otherwise: use relative path (same-origin in dev, proxied by Vite)
SSE streaming:
The useSSE hook creates an EventSource to apiUrl(path). The daemon sends JSON-encoded events on the message channel. Used by:
Live logs (/api/logs) — Dashboard live logs panel and Server Logs page.
Polling:
Most features use usePolling at 5-second intervals for real-time updates:
Gateway status (/api/status)
Sessions (/api/sessions)
Live session indicator (10-second interval)
Data Contracts
All types shared between panel and daemon are defined in packages/daemon/src/panel/types.ts. The panel feature domain files re-export and alias these types. Key contracts:
Contract
Used By
GatewayStatusResponse
Shell (TopBar), Dashboard (StatusOverview)
GatewayProviderStat
Dashboard, History
ProviderInfo
Providers, including runtime limit fields (maxConcurrency, rateLimit, rateWindow) surfaced in provider configuration
All feature pages are lazy-loaded via React.lazy() in app/routes.tsx. The AppShell provides a <Suspense> boundary with an Ant Design Skeleton fallback. This means each feature's code is split into its own chunk, loaded only when the user navigates to that route.