# 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)