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>
346 lines
11 KiB
Markdown
346 lines
11 KiB
Markdown
# 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)
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
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)
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
@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
|
|
|
|
```python
|
|
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)
|
|
|
|
```python
|
|
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:
|
|
|
|
```python
|
|
# 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](https://github.com/jackw01/summer-night-vscode-theme):
|
|
|
|
```python
|
|
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
|
|
|
|
```python
|
|
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/`:
|
|
|
|
```toml
|
|
# .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):
|
|
1. Environment variables (`CLIDE_*`)
|
|
2. `.config/settings.toml`
|
|
3. Defaults in ClideSettings
|
|
|
|
```python
|
|
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](https://textual.textualize.io/)
|
|
- [Textual Testing Guide](https://textual.textualize.io/guide/testing/)
|
|
- [Textual Themes Guide](https://textual.textualize.io/guide/design/)
|
|
- [Typer Documentation](https://typer.tiangolo.com/)
|
|
- [Pydantic v2 Documentation](https://docs.pydantic.dev/latest/)
|
|
- [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
|
|
|
|
### Testing
|
|
- [pytest-asyncio](https://pytest-asyncio.readthedocs.io/en/latest/)
|
|
- [pytest-textual-snapshot](https://github.com/Textualize/pytest-textual-snapshot)
|
|
|
|
### Extensions
|
|
- [Pluggy Documentation](https://pluggy.readthedocs.io/)
|
|
|
|
### Build
|
|
- [PyInstaller Documentation](https://pyinstaller.org/)
|
|
- [Gitea Actions](https://docs.gitea.com/usage/actions/overview)
|
|
|
|
### Theme References
|
|
- [Summer Night VSCode Theme](https://github.com/jackw01/summer-night-vscode-theme)
|