Clide is being rebuilt as a Flutter desktop app. The Python Textual implementation moves wholesale into legacy/ rather than being deleted: its pane model, panel set, git skills, and panel communication design are real thought that should remain readable next to the new code while the rebuild finds its shape. Git's rename tracking preserves history, so `git log -- legacy/` still works. The Flutter rebuild lives at the repo root alongside a Go sidecar (the architecture claudian was heading toward, which folds into clide as a core component rather than a separate plugin project). Bootstrap of the new shape lands in subsequent commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
11 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Clide is a TUI IDE wrapper for Claude Code CLI, designed to be Claude-centric with VSCode-familiar keybindings. See docs/tui-ide-spec.md for full specification.
Design Principles
- Claude-centric: Claude Code is the primary workspace, always visible
- Contextual panels: Editor/Diff/Terminal appear only when needed
- Alt-key shortcuts: Alt-based keybindings don't interfere with input fields
- Responsive: Works on 13" laptop to widescreen monitors
- State preservation: Hiding panels preserves all state (never destroy widgets)
Tech Stack
| Component | Library | Version |
|---|---|---|
| Runtime | Python | 3.12+ |
| TUI Framework | Textual | latest |
| CLI | Typer | latest |
| Data Validation | Pydantic | v2 (strict mode) |
| Settings | pydantic-settings | latest |
| Database | SQLModel + SQLite | latest |
| Web Server | FastAPI + uvicorn | latest |
| Testing | pytest + pytest-asyncio + pytest-textual-snapshot | latest |
| Extensions | pluggy | latest |
Development Commands
Clide (TUI)
make setup # Create venv, install deps
make run # Run application
make test # Run all tests
make test-single # Run single test (TEST=path::test_name)
make typecheck # Run mypy
make lint # Run ruff check
make format # Run ruff format
make build # Build for current platform
clide-web (Web Server)
cd clide-web/
make setup # Create venv, install deps, run setup wizard
make run # Run the web server (foreground)
make dev # Run with auto-reload
make start-server # Start systemd service
make stop-server # Stop systemd service
make restart-server # Restart systemd service
make status-server # Show service status
make logs-server # Tail service logs
Panel Architecture
┌─────────────────┬─────────────────────────┬──────────────────┐
│ panel-sidebar │ panel-workspace (60%) │ panel-context │
│ │ [Editor][Diff][Terminal]│ │
│ [Files][Git] │ (hidden when inactive) │ [Problems][TODOs]│
│ [Tree] ├─────────────────────────┤ [Jira] │
│ │ │ │
│ (content area) │ panel-claude │ (content area) │
│ │ (40% when workspace │ │
│ │ visible, else 100%) │ │
├─────────────────┤ ├──────────────────┤
│ ⎇ main ▾ │ │ [⚠ 3][✓12][Jira]│
└─────────────────┴─────────────────────────┴──────────────────┘
Project Structure
clide/
├── clide/ # Package source
│ ├── __init__.py
│ ├── __main__.py
│ ├── app.py # Main App, layout, keybindings
│ ├── cli.py # Typer entry point
│ ├── controllers/ # Domain logic (no UI)
│ ├── widgets/ # UI components
│ │ ├── panels/ # Main layout containers
│ │ └── components/ # Reusable UI pieces
│ ├── models/ # Pydantic data models
│ ├── services/ # Background task logic
│ ├── themes/ # Theme system
│ ├── extensions/ # Plugin system
│ └── helpers/ # Utility functions
│
├── tests/
│ ├── conftest.py
│ ├── harnesses/
│ │ ├── app_harness.py
│ │ └── controller_harness.py
│ ├── unit/
│ ├── integration/
│ └── snapshots/
│
├── .config/ # User config (gitignored)
│ ├── settings.toml # User settings
│ └── themes/ # Custom user themes
│ └── my-theme.toml
├── docs/
│ ├── tui-ide-spec.md # Full UI/UX specification
│ ├── web-deployment.md # Web deployment architecture
│ └── ARCHITECTURE.md # Framework best practices
├── pyproject.toml
└── Makefile
│
clide-web/ # Web server package (wraps clide)
├── clide_web/
│ ├── server.py # FastAPI app, routes, WebSocket handler
│ ├── sessions.py # tmux session manager
│ ├── pty_bridge.py # PTY ↔ WebSocket bridge
│ ├── config.py # Pydantic settings with DB overlay
│ ├── setup_wizard.py # Interactive first-run config
│ └── static/
│ ├── index.html # HTML page (toolbar + xterm.js)
│ └── vendor/ # Vendored xterm.js (offline)
├── pyproject.toml
└── Makefile
Key Patterns
Panel Visibility (Hide, Don't Destroy)
def toggle_workspace(self, visible: bool) -> None:
workspace = self.query_one("#panel-workspace")
workspace.display = visible # Preserves all child state
claude = self.query_one("#panel-claude")
claude.styles.height = "40%" if visible else "100%"
Background Tasks
Use @work decorator for non-blocking operations:
@work(thread=True)
def refresh_git_status(self) -> None:
result = subprocess.run(["git", "status", "--porcelain"], ...)
self.call_from_thread(self.update_git_view, result.stdout)
Reactive State
class ClideApp(App):
current_file: reactive[str | None] = reactive(None)
workspace_visible: reactive[bool] = reactive(False)
problem_count: reactive[int] = reactive(0)
todo_count: reactive[int] = reactive(0)
compact_mode: reactive[bool] = reactive(False)
Pydantic Models (Strict + Frozen)
from pydantic import BaseModel, ConfigDict
class GitChange(BaseModel):
model_config = ConfigDict(strict=True, frozen=True)
path: str
status: Literal["added", "modified", "deleted", "untracked", "renamed"]
staged: bool
Controller → Widget Communication
Controllers emit Textual messages; widgets subscribe:
# In controller
class GitStatusUpdated(Message):
def __init__(self, status: GitStatus) -> None:
self.status = status
super().__init__()
self.post_message(GitStatusUpdated(status))
# In widget
def on_git_status_updated(self, event: GitStatusUpdated) -> None:
self.refresh_view(event.status)
Theme System
Default Theme: Summer Night
Based on jackw01/summer-night-vscode-theme:
SUMMER_NIGHT = ThemeColors(
primary="#00a3d2", # cyan
secondary="#00a9b9", # teal
accent="#fa5f8b", # pink
background="#21262f", # mono_8
surface="#393e48", # mono_7
panel="#292e38",
foreground="#e2e8f5", # mono_1
success="#00ab9a", # green
warning="#d08447", # orange
error="#f06c6f", # red
)
Built-in Themes (22 total)
| Category | Themes |
|---|---|
| Core | summer-night (default), summer-day |
| Popular | one-dark, one-dark-pro, one-light, dracula, nord, gruvbox-dark, gruvbox-light |
| GitKraken | one-dark-teal, gamma |
| Seasonal - Winter | winter-is-coming, monokai-winter |
| Seasonal - Fall | fall, dark-autumn |
| Seasonal - Halloween | all-hallows-eve, halloween |
| Seasonal - Christmas | christmas, santa-baby |
| Hacker | pro-hacker, hacker-style |
| Bonus | houston |
Theme Definition
class ThemeColors(BaseModel):
model_config = ConfigDict(strict=True, frozen=True)
primary: str # Main accent
secondary: str # Secondary accent
accent: str # Highlight accent
background: str # Main background
surface: str # Elevated surfaces
panel: str # Panel backgrounds
foreground: str # Primary text
success: str # Success/green
warning: str # Warning/yellow
error: str # Error/red
class ThemeDefinition(BaseModel):
name: str # Identifier (e.g., "summer-night")
display_name: str # Human-readable name
dark: bool # Dark or light theme
colors: ThemeColors
Custom Themes
Users can add themes in .config/themes/:
# .config/themes/my-theme.toml
name = "my-theme"
display_name = "My Custom Theme"
dark = true
[colors]
primary = "#007acc"
secondary = "#3c3c3c"
accent = "#0e639c"
background = "#1e1e1e"
surface = "#252526"
panel = "#2d2d30"
foreground = "#d4d4d4"
success = "#4ec9b0"
warning = "#dcdcaa"
error = "#f44747"
Theme Switching
- Keybinding:
Ctrl+K Ctrl+T - Settings:
theme = "summer-night"in ClideSettings - Runtime:
app.theme = "dracula"
Keybindings (Alt-based)
| Action | Binding |
|---|---|
| Quit | Alt+Q |
| Command palette | Alt+P |
| Quick open | Alt+O |
| Toggle left sidebar | Alt+B |
| Toggle right sidebar | Alt+Shift+B |
| Toggle terminal | Alt+` |
| Focus Claude | Alt+1 |
| Focus Editor | Alt+2 |
| Focus Terminal | Alt+3 |
| Toggle compact mode | Alt+C |
| Git panel | Alt+G |
| Problems panel | Alt+M |
| Select theme | Alt+T |
| Save file | Alt+S |
Configuration
Settings are loaded from multiple sources (in priority order):
- Environment variables (
CLIDE_*) .config/settings.toml- Defaults in ClideSettings
class ClideSettings(BaseSettings):
model_config = SettingsConfigDict(
env_prefix="CLIDE_",
env_file=".env",
extra="ignore",
)
theme: str = "summer-night"
jira_enabled: bool = False
jira_cli_path: str = "jira"
panels: PanelConfig = PanelConfig()
keybindings: KeybindingsConfig = KeybindingsConfig()
Documentation Links
Core Stack
- Textual Documentation
- Textual Testing Guide
- Textual Themes Guide
- Typer Documentation
- Pydantic v2 Documentation
- Pydantic Settings