Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7a3cf55
docs: add terminal mode feature design spec
adeotek May 12, 2026
c42e79f
docs: add terminal mode implementation plan
adeotek May 12, 2026
bed65f9
feat: add session mode DB column and settings key
adeotek May 12, 2026
6ed5e67
feat: add terminal WS handler and TerminalManager
adeotek May 12, 2026
17d9781
fix: terminal session spawn error handling and lifecycle cleanup
adeotek May 12, 2026
7bd9ecf
feat: wire terminal WS route and mode into sessions API
adeotek May 12, 2026
918244a
feat: add mode field to SessionContext state and actions
adeotek May 12, 2026
46de54e
fix: point lint at tsconfig.app.json and stub mode in DashboardView d…
adeotek May 12, 2026
80d0f0d
feat: add mode to Session type and fetch settings in useDashboard
adeotek May 12, 2026
315afcd
feat: skip chat WS connection when session mode is terminal
adeotek May 12, 2026
a035fe3
feat: add useTerminalSession hook for /ws/terminal/:id
adeotek May 12, 2026
62c427c
feat: add full-height TerminalSession component
adeotek May 12, 2026
e45c9cc
feat: wire DashboardView for terminal mode rendering (Task 9)
adeotek May 12, 2026
9c622f1
feat: add session mode toggle to SettingsView (Task 10)
adeotek May 12, 2026
bc116ea
fix: remove dead terminal session from manager on spawn error
adeotek May 12, 2026
d246753
fix: defer PTY spawn until initial resize to prevent TUI dimension mi…
adeotek May 13, 2026
e7de0df
fix: auto-restart terminal on PTY exit and use current mode on resume
adeotek May 13, 2026
c98ad4d
fix: always send dimensions on WS connect, not just when size changes
adeotek May 13, 2026
d737523
fix: CSS-toggle TerminalSession to preserve xterm buffer across navig…
adeotek May 13, 2026
2a850f6
feat: save and replay terminal scrollback on reconnect
adeotek May 13, 2026
fb8a563
[fix:ui] session header fix
adeotek May 13, 2026
eb7262b
[fix:ui] settings page fix
adeotek May 13, 2026
4879bd1
[fix:global] fixes
adeotek May 13, 2026
d7ee017
[feat:tests] packages upgrades and unit tests added
adeotek May 13, 2026
ea99661
[fix:global] fixes
adeotek May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# Anthropic API key for billing usage stats (optional — local logs work without it)
ANTHROPIC_API_KEY=

# Path to the claude binary (defaults to claude from PATH)
CLAUDE_BIN=claude

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/claude-code-webui-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ jobs:
- name: Build backend
working-directory: backend
run: npm run build

- name: Test backend
working-directory: backend
run: npm test

- name: Test frontend
working-directory: frontend
run: npm test
21 changes: 14 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ cp .env.example backend/.env

| Variable | Default | Description |
|---|---|---|
| `ANTHROPIC_API_KEY` | — | Optional; enables billing data in Usage view |
| `CLAUDE_BIN` | `claude` | Path to the `claude` binary |
| `PORT` | `9998` | Backend listen port |
| `HOST` | `0.0.0.0` | Backend listen address |
Expand All @@ -69,22 +68,30 @@ The Makefile's `dev-backend` target and `run.sh` both conditionally set `NODE_EX
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | `/api/account` | `routes/account.ts` | Account info + claude version |
| GET | `/api/usage` | `routes/usage.ts` | Usage stats (local logs + optional billing API) |
| GET | `/api/usage` | `routes/usage.ts` | Usage stats (local logs) |
| GET/POST | `/api/sessions` | `routes/sessions.ts` | List sessions, create session |
| POST | `/api/sessions/:id/stop` | `routes/sessions.ts` | Stop active session |
| GET/POST | `/api/settings` | `routes/settings.ts` | Persistent key-value settings (SQLite-backed) |
| GET | `/health` | `server.ts` | Health check |
| WS | `/ws/session/:id` | `ws/session.ts` | PTY I/O over WebSocket |
| WS | `/ws/session/:id` | `ws/session.ts` | Chat PTY I/O over WebSocket |
| WS | `/ws/terminal/:id` | `ws/terminal.ts` | Terminal PTY I/O over WebSocket |
| DELETE | `/api/sessions/:id` | `routes/sessions.ts` | Delete session (DB + `~/.claude/projects/` file) |

### Settings route

`routes/settings.ts` stores app settings in the SQLite `settings` table. Currently the only key is `bypass_permissions` (default `true`). Only keys in the `ALLOWED` set are accepted — add new keys there before using them.
`routes/settings.ts` stores app settings in the SQLite `settings` table. Keys: `bypass_permissions` (default `true`) and `session_mode` (default `'terminal'`). Only keys in the `ALLOWED` set are accepted — add new keys there before using them.

### Session modes

Two modes are supported per session, stored in the `sessions.mode` column:
- **`chat`** — structured JSON streaming via `/ws/session/:id`; messages stored in `messages` table
- **`terminal`** — raw PTY via `/ws/terminal/:id`; scrollback persisted in `sessions.terminal_scrollback` (256 KB circular buffer, flushed to SQLite on activity with a 500 ms debounce)

### Frontend data flow

- `useDashboard.ts` fetches account + usage + sessions in parallel with a 60s auto-refresh
- `SessionContext.tsx` holds the active session state; `useWebSocket.ts` manages the WS connection with exponential-backoff reconnect
- `TerminalDrawer.tsx` wraps xterm.js and is **never unmounted** — it is CSS-toggled (display: none) to preserve terminal state across view switches
- `SessionContext.tsx` holds the active session state (including `mode`); `useWebSocket.ts` manages the chat WS connection; `useTerminalSession.ts` manages the terminal WS connection
- `TerminalSession.tsx` and `TerminalDrawer.tsx` are **never unmounted** — both are CSS-toggled (`display: none`) to preserve xterm scroll buffer across navigation

### Syntax highlighting

Expand All @@ -102,7 +109,7 @@ docker-compose up
# Dashboard at http://localhost:8080
```

`docker-compose.yml` mounts `~/.claude` (read-only) and `~/projects` (read-write) from the host.
`docker-compose.yml` mounts `~/.claude` (read-write — required for session deletion) and `~/projects` (read-write) from the host.

## Systemd service (Linux)

Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: dev dev-backend dev-frontend build lint install clean run service-install service-uninstall service-test
.PHONY: dev dev-backend dev-frontend build lint test install clean run service-install service-uninstall service-test

# Fedora / RHEL hosts lack the Google Trust Services intermediate CA that
# Anthropic's API uses. Set NODE_EXTRA_CA_CERTS only when the bundle exists
Expand All @@ -23,6 +23,10 @@ lint:
cd backend && npm run lint
cd frontend && npm run lint

test:
cd backend && npm test
cd frontend && npm test

install:
cd backend && npm install
cd frontend && npm install
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
[![Docker](https://img.shields.io/badge/docker-ghcr.io-blue)](https://github.com/adeotek/claude-code-webui/pkgs/container/claude-code-webui)
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)

A browser-based UI for [Claude Code](https://claude.ai/code) — manage sessions, track token usage, and chat with Claude from any device on your network.
A browser-based UI for [Claude Code](https://claude.ai/code) — manage sessions, track token usage, and interact with Claude from any device on your network.

## Features

- **Session management** — create, resume, rename, and delete Claude Code sessions
- **Real-time chat** — markdown rendering with syntax-highlighted code blocks
- **Terminal drawer** — integrated xterm.js terminal that persists across view switches
- **Usage dashboard** — daily token usage graph with optional Anthropic billing data
- **Terminal mode** — full interactive xterm.js terminal connected directly to Claude Code (default)
- **Chat mode** — structured message view with markdown rendering and syntax-highlighted code blocks
- **Usage dashboard** — daily token usage graph
- **Account overview** — Claude version, authentication status, and model info
- **Persistent history** — session messages and token counts stored in SQLite
- **Persistent history** — session messages, token counts, and terminal scrollback stored in SQLite

## Quick Start

Expand Down Expand Up @@ -55,7 +55,7 @@ docker-compose up

Open **http://localhost:8080**.

`docker-compose.yml` mounts `~/.claude` (read-only) and `~/projects` (read-write) from the host. To build the image locally instead of pulling from the registry:
`docker-compose.yml` mounts `~/.claude` (read-write — required for session deletion) and `~/projects` (read-write) from the host. To build the image locally instead of pulling from the registry:

```bash
docker build -t claude-code-webui .
Expand All @@ -72,7 +72,6 @@ cp .env.example backend/.env

| Variable | Default | Description |
|---|---|---|
| `ANTHROPIC_API_KEY` | — | Optional — enables billing data in the Usage view |
| `CLAUDE_BIN` | `claude` | Path to the `claude` binary |
| `PORT` | `9998` | Backend listen port |
| `HOST` | `0.0.0.0` | Backend listen address |
Expand Down
Loading
Loading