move python clide to legacy/
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>
This commit is contained in:
@@ -1,432 +0,0 @@
|
||||
# Clide Architecture
|
||||
|
||||
Technical documentation covering Clide's architecture, the frameworks it builds on, and implementation patterns.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Application Architecture](#application-architecture)
|
||||
- [Textual TUI Framework](#textual-tui-framework)
|
||||
- [Pydantic Data Validation](#pydantic-data-validation)
|
||||
- [Extension System](#extension-system)
|
||||
- [Testing Strategy](#testing-strategy)
|
||||
- [Build and Distribution](#build-and-distribution)
|
||||
|
||||
---
|
||||
|
||||
## Application Architecture
|
||||
|
||||
Clide follows a layered architecture with clear separation between UI, business logic, and data.
|
||||
|
||||
### Layer Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ ClideApp (app.py) │
|
||||
│ Main application, layout, keybindings │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Widgets Layer │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Panels │ │ Components │ │ Themes │ │
|
||||
│ │ (layout) │ │ (reusable) │ │ (styling) │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Controllers Layer │
|
||||
│ Business logic, state management │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Services Layer │
|
||||
│ Git, files, scanning, settings, skills │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Models Layer │
|
||||
│ Pydantic data structures │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
User Action (click, keypress)
|
||||
│
|
||||
▼
|
||||
Widget Event
|
||||
│
|
||||
▼
|
||||
Message Bubbles Up
|
||||
│
|
||||
▼
|
||||
App Event Handler
|
||||
│
|
||||
▼
|
||||
Controller Method
|
||||
│
|
||||
▼
|
||||
Service Call
|
||||
│
|
||||
▼
|
||||
Return Data/Status
|
||||
│
|
||||
▼
|
||||
Update UI State
|
||||
│
|
||||
▼
|
||||
Reactive UI Update
|
||||
```
|
||||
|
||||
### Key Patterns
|
||||
|
||||
**Message-based communication** — Widgets emit messages that bubble up. Parent widgets or the app handle messages and coordinate responses.
|
||||
|
||||
**Reactive properties** — UI state uses Textual's `reactive` type. Changes automatically trigger `watch_*` methods.
|
||||
|
||||
**Background workers** — Long operations use `@work(thread=True)` to avoid blocking the UI.
|
||||
|
||||
**State preservation** — Hiding panels uses `display: none`, never destroying widgets. All state persists.
|
||||
|
||||
For detailed code organization, see [Code Organization](code-organization.md).
|
||||
|
||||
---
|
||||
|
||||
## Textual TUI Framework
|
||||
|
||||
Textual provides the foundation for Clide's terminal UI.
|
||||
|
||||
### Core Concepts
|
||||
|
||||
**Widgets** — Building blocks of the UI. Everything visible is a widget.
|
||||
|
||||
**Containers** — Widgets that hold other widgets (Vertical, Horizontal, Container).
|
||||
|
||||
**Reactive Programming** — State changes trigger automatic UI updates.
|
||||
|
||||
**CSS Styling** — Layout and appearance defined in CSS, similar to web development.
|
||||
|
||||
### Layout System
|
||||
|
||||
Clide uses CSS Grid for the main layout:
|
||||
|
||||
```css
|
||||
Screen {
|
||||
layout: grid;
|
||||
grid-size: 3 1;
|
||||
grid-columns: 20% 1fr 25%;
|
||||
}
|
||||
```
|
||||
|
||||
Panels use percentage widths with minimum sizes:
|
||||
|
||||
```css
|
||||
#panel-sidebar {
|
||||
width: 20%;
|
||||
min-width: 25;
|
||||
}
|
||||
```
|
||||
|
||||
### Widget Lifecycle
|
||||
|
||||
```python
|
||||
class MyWidget(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Initialize instance variables
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
# Yield child widgets
|
||||
yield Label("Hello")
|
||||
|
||||
def on_mount(self) -> None:
|
||||
# Called after widget is added to DOM
|
||||
# Safe to query other widgets here
|
||||
|
||||
def on_unmount(self) -> None:
|
||||
# Cleanup when removed
|
||||
```
|
||||
|
||||
### Event Handling
|
||||
|
||||
Events bubble up through the widget tree:
|
||||
|
||||
```python
|
||||
# Define a message
|
||||
class FileSelected(Message):
|
||||
def __init__(self, path: Path):
|
||||
self.path = path
|
||||
super().__init__()
|
||||
|
||||
# Emit the message
|
||||
self.post_message(self.FileSelected(path))
|
||||
|
||||
# Handle in parent (naming convention: on_<widget>_<message>)
|
||||
def on_files_view_file_selected(self, event: FilesView.FileSelected):
|
||||
self.open_file(event.path)
|
||||
```
|
||||
|
||||
### Background Tasks
|
||||
|
||||
Use `@work` for operations that shouldn't block the UI:
|
||||
|
||||
```python
|
||||
from textual import work
|
||||
|
||||
@work(thread=True)
|
||||
def fetch_data(self) -> dict:
|
||||
"""Runs in thread pool."""
|
||||
result = expensive_operation()
|
||||
return result
|
||||
|
||||
def on_worker_state_changed(self, event: Worker.StateChanged) -> None:
|
||||
if event.state == WorkerState.SUCCESS:
|
||||
self.update_ui(event.worker.result)
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- [Textual Documentation](https://textual.textualize.io/)
|
||||
- [Textual Widgets](https://textual.textualize.io/widgets/)
|
||||
- [Textual CSS](https://textual.textualize.io/guide/CSS/)
|
||||
|
||||
---
|
||||
|
||||
## Pydantic Data Validation
|
||||
|
||||
All data models use Pydantic v2 with strict mode.
|
||||
|
||||
### Model Configuration
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
class GitChange(BaseModel):
|
||||
model_config = ConfigDict(strict=True, frozen=True)
|
||||
|
||||
path: str
|
||||
status: Literal["added", "modified", "deleted"]
|
||||
staged: bool
|
||||
```
|
||||
|
||||
**strict=True** — No type coercion. `"123"` won't become `123`.
|
||||
|
||||
**frozen=True** — Immutable instances. Enables hashing for use as dict keys.
|
||||
|
||||
### Settings Management
|
||||
|
||||
Application settings use `pydantic-settings`:
|
||||
|
||||
```python
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
class ClideSettings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
env_prefix="CLIDE_",
|
||||
env_file=".env",
|
||||
)
|
||||
|
||||
theme: str = "summer-night"
|
||||
jira_enabled: bool = False
|
||||
```
|
||||
|
||||
Settings load from (in priority order):
|
||||
1. Environment variables (`CLIDE_THEME=dracula`)
|
||||
2. `.env` file
|
||||
3. Default values
|
||||
|
||||
### References
|
||||
|
||||
- [Pydantic Documentation](https://docs.pydantic.dev/latest/)
|
||||
- [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)
|
||||
|
||||
---
|
||||
|
||||
## Extension System
|
||||
|
||||
Clide uses Pluggy for hook-based extensibility.
|
||||
|
||||
### Hook Specifications
|
||||
|
||||
Hooks define extension points:
|
||||
|
||||
```python
|
||||
# clide/extensions/hookspecs.py
|
||||
import pluggy
|
||||
|
||||
hookspec = pluggy.HookspecMarker("clide")
|
||||
hookimpl = pluggy.HookimplMarker("clide")
|
||||
|
||||
class ClideHookSpec:
|
||||
@hookspec
|
||||
def clide_startup(self, app: App) -> None:
|
||||
"""Called when the app starts."""
|
||||
|
||||
@hookspec
|
||||
def clide_on_file_changed(self, event: FileEvent) -> None:
|
||||
"""Called when a file changes."""
|
||||
```
|
||||
|
||||
### Implementing Hooks
|
||||
|
||||
Extensions implement hooks with the `@hookimpl` decorator:
|
||||
|
||||
```python
|
||||
from clide.extensions import hookimpl
|
||||
|
||||
class MyExtension:
|
||||
@hookimpl
|
||||
def clide_startup(self, app: App) -> None:
|
||||
app.notify("Extension loaded!")
|
||||
|
||||
@hookimpl
|
||||
def clide_on_file_changed(self, event: FileEvent) -> None:
|
||||
if event.path.suffix == ".py":
|
||||
# React to Python file changes
|
||||
pass
|
||||
```
|
||||
|
||||
### Distribution
|
||||
|
||||
Extensions can be packaged and distributed via entry points:
|
||||
|
||||
```toml
|
||||
# pyproject.toml of extension package
|
||||
[project.entry-points."clide.extensions"]
|
||||
my_extension = "my_package:MyExtension"
|
||||
```
|
||||
|
||||
### Available Hooks
|
||||
|
||||
| Hook | When Called |
|
||||
|------|-------------|
|
||||
| `clide_startup` | App initialization |
|
||||
| `clide_shutdown` | App cleanup |
|
||||
| `clide_on_file_changed` | File created/modified/deleted |
|
||||
| `clide_on_file_saved` | File saved in editor |
|
||||
|
||||
### References
|
||||
|
||||
- [Pluggy Documentation](https://pluggy.readthedocs.io/)
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Test Organization
|
||||
|
||||
```
|
||||
tests/
|
||||
├── unit/ # Isolated component tests
|
||||
├── integration/ # Component interaction tests
|
||||
└── snapshots/ # Visual regression tests
|
||||
```
|
||||
|
||||
### Async Testing
|
||||
|
||||
Configure pytest-asyncio in auto mode:
|
||||
|
||||
```toml
|
||||
[tool.pytest.ini_options]
|
||||
asyncio_mode = "auto"
|
||||
```
|
||||
|
||||
Tests can be async without decorators:
|
||||
|
||||
```python
|
||||
async def test_async_operation():
|
||||
result = await some_async_function()
|
||||
assert result == expected
|
||||
```
|
||||
|
||||
### Snapshot Testing
|
||||
|
||||
Visual regression testing with pytest-textual-snapshot:
|
||||
|
||||
```python
|
||||
def test_layout(snap_compare):
|
||||
assert snap_compare(ClideApp(), terminal_size=(120, 40))
|
||||
|
||||
def test_with_interaction(snap_compare):
|
||||
async def setup(pilot):
|
||||
await pilot.press("tab", "enter")
|
||||
|
||||
assert snap_compare(ClideApp(), run_before=setup)
|
||||
```
|
||||
|
||||
Update snapshots after intentional changes:
|
||||
|
||||
```bash
|
||||
pytest tests/snapshots/ --snapshot-update
|
||||
```
|
||||
|
||||
### Mocking
|
||||
|
||||
Use `AsyncMock` for async dependencies:
|
||||
|
||||
```python
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
async def test_with_mock():
|
||||
mock_service = AsyncMock(return_value={"status": "ok"})
|
||||
result = await mock_service()
|
||||
assert result["status"] == "ok"
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
- [pytest-asyncio](https://pytest-asyncio.readthedocs.io/)
|
||||
- [pytest-textual-snapshot](https://github.com/Textualize/pytest-textual-snapshot)
|
||||
- [Textual Testing Guide](https://textual.textualize.io/guide/testing/)
|
||||
|
||||
---
|
||||
|
||||
## Build and Distribution
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
make setup # Create venv, install deps
|
||||
make run # Run application
|
||||
make test # Run all tests
|
||||
make typecheck # Run mypy
|
||||
make lint # Run ruff
|
||||
make format # Format code
|
||||
```
|
||||
|
||||
### PyInstaller
|
||||
|
||||
Build standalone executables:
|
||||
|
||||
```bash
|
||||
pip install -e ".[build]"
|
||||
pyinstaller clide.spec --clean
|
||||
```
|
||||
|
||||
**Important**: PyInstaller cannot cross-compile. Build on each target platform.
|
||||
|
||||
### CI/CD
|
||||
|
||||
Multi-platform builds via Gitea Actions:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- run: pip install -e ".[build]"
|
||||
- run: pyinstaller clide.spec --clean
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
# ... same steps
|
||||
```
|
||||
|
||||
### Optimization
|
||||
|
||||
- Use `--onefile` for single executable
|
||||
- Apply `--strip` to reduce size
|
||||
- Use UPX compression for further reduction
|
||||
- Exclude unused modules with `--exclude-module`
|
||||
|
||||
### References
|
||||
|
||||
- [PyInstaller Documentation](https://pyinstaller.org/)
|
||||
- [Gitea Actions](https://docs.gitea.com/usage/actions/overview)
|
||||
@@ -1,342 +0,0 @@
|
||||
# Code Organization
|
||||
|
||||
Clide follows a layered architecture separating UI components from business logic, enabling testability and maintainability.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
clide/
|
||||
├── app.py # Main application, layout, keybindings
|
||||
├── cli.py # Typer entry point
|
||||
├── models/ # Pydantic data models
|
||||
├── services/ # Background services and utilities
|
||||
├── controllers/ # Business logic (no UI)
|
||||
├── widgets/
|
||||
│ ├── panels/ # Main layout containers
|
||||
│ └── components/ # Reusable UI pieces
|
||||
├── themes/ # Theme definitions and registry
|
||||
├── extensions/ # Plugin system (hookspecs, manager)
|
||||
├── templates/ # Bundled templates (skills, etc.)
|
||||
└── vendor/ # Vendored dependencies (pyte)
|
||||
```
|
||||
|
||||
## Layers
|
||||
|
||||
### Models (`clide/models/`)
|
||||
|
||||
Pure data structures using Pydantic with strict mode. Models are immutable and contain no business logic.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
**Key models:**
|
||||
- `git.py` — Git-related types (GitBranch, GitCommit, GitChange, GitStatus)
|
||||
- `config.py` — Application settings (ClideSettings, PanelConfig)
|
||||
- `problems.py` — Linter output (Problem, Severity)
|
||||
- `todos.py` — TODO items (TodoItem, ProjectTodoItem, TodoType)
|
||||
|
||||
### Services (`clide/services/`)
|
||||
|
||||
Stateless utilities that perform work without UI interaction. Services may be async or use background threads.
|
||||
|
||||
```python
|
||||
class GitService:
|
||||
"""Git operations via subprocess."""
|
||||
|
||||
def __init__(self, workdir: Path):
|
||||
self._workdir = workdir
|
||||
|
||||
async def get_status(self) -> GitStatus:
|
||||
"""Get current repository status."""
|
||||
...
|
||||
|
||||
async def get_branches(self) -> list[GitBranch]:
|
||||
"""List all branches."""
|
||||
...
|
||||
```
|
||||
|
||||
**Key services:**
|
||||
- `git_service.py` — Git CLI operations
|
||||
- `file_service.py` — File read/write operations
|
||||
- `todo_scanner.py` — Scans codebase for TODO/FIXME comments
|
||||
- `settings_service.py` — User settings persistence
|
||||
- `skill_installer.py` — Claude Code skill management
|
||||
- `file_watcher.py` — File system change monitoring
|
||||
- `syntax_service.py` — Tree-sitter syntax highlighting
|
||||
|
||||
### Controllers (`clide/controllers/`)
|
||||
|
||||
Bridge between services and UI. Controllers contain business logic, manage state, and emit Textual messages. Controllers have no direct UI rendering.
|
||||
|
||||
```python
|
||||
from clide.controllers.base import controller
|
||||
|
||||
@controller
|
||||
class GitController:
|
||||
"""Manages git state and operations."""
|
||||
|
||||
def __init__(self, workdir: Path):
|
||||
self._service = GitService(workdir)
|
||||
self._status: GitStatus | None = None
|
||||
|
||||
async def refresh_status(self) -> GitStatus:
|
||||
"""Refresh and cache git status."""
|
||||
self._status = await self._service.get_status()
|
||||
return self._status
|
||||
|
||||
def stage_file(self, path: str) -> None:
|
||||
"""Stage a file for commit."""
|
||||
...
|
||||
```
|
||||
|
||||
**Key controllers:**
|
||||
- `git.py` — Git operations, skill integration
|
||||
- `editor.py` — File editing state
|
||||
- `diff.py` — Diff viewing and management
|
||||
- `problems.py` — Linter integration
|
||||
- `todos.py` — TODO tracking
|
||||
- `jira.py` — Jira CLI integration
|
||||
|
||||
### Widgets (`clide/widgets/`)
|
||||
|
||||
UI components split into panels (layout containers) and components (reusable pieces).
|
||||
|
||||
#### Panels (`clide/widgets/panels/`)
|
||||
|
||||
Top-level layout containers that compose the application UI.
|
||||
|
||||
```python
|
||||
class SidebarPanel(Vertical):
|
||||
"""Left sidebar with Files, Git, and Tree tabs."""
|
||||
|
||||
class FileSelected(Message):
|
||||
"""Emitted when a file is selected."""
|
||||
def __init__(self, path: Path):
|
||||
self.path = path
|
||||
super().__init__()
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
with TabbedContent():
|
||||
with TabPane("Files"):
|
||||
yield FilesView(path=self._workdir)
|
||||
with TabPane("Git"):
|
||||
yield GitChangesView()
|
||||
with TabPane("Tree"):
|
||||
yield GitGraphView()
|
||||
yield BranchStatus()
|
||||
```
|
||||
|
||||
**Panels:**
|
||||
- `sidebar.py` — Left sidebar (files, git, graph)
|
||||
- `context.py` — Right sidebar (problems, todos, jira)
|
||||
- `workspace.py` — Center workspace (editor, diff, terminal)
|
||||
- `claude.py` — Claude Code terminal integration
|
||||
|
||||
#### Components (`clide/widgets/components/`)
|
||||
|
||||
Reusable UI pieces composed into panels.
|
||||
|
||||
**File browsing:**
|
||||
- `files_view.py` — Project file tree
|
||||
- `file_entry.py` — Single file/directory entry
|
||||
|
||||
**Git:**
|
||||
- `git_changes.py` — Staged/unstaged file lists
|
||||
- `git_graph.py` — Visual branch graph
|
||||
- `branch_status.py` — Branch indicator with popout selector
|
||||
|
||||
**Context:**
|
||||
- `problems_view.py` — Linter problems list
|
||||
- `todos_view.py` — TODO/FIXME list with sub-tabs
|
||||
- `jira_view.py` — Jira issue display
|
||||
|
||||
**Editor:**
|
||||
- `editor_pane.py` — Code editor with syntax highlighting
|
||||
- `diff_pane.py` — Side-by-side diff viewer
|
||||
- `terminal_pane.py` — Command execution terminal
|
||||
|
||||
## Communication Patterns
|
||||
|
||||
### Message Flow
|
||||
|
||||
Components communicate via Textual's message system. Messages bubble up through the widget tree.
|
||||
|
||||
```
|
||||
Component emits message
|
||||
│
|
||||
▼
|
||||
Parent panel receives and may re-emit
|
||||
│
|
||||
▼
|
||||
App handles and coordinates response
|
||||
│
|
||||
▼
|
||||
App calls controller methods
|
||||
│
|
||||
▼
|
||||
Controller updates state, may emit messages
|
||||
│
|
||||
▼
|
||||
UI updates reactively
|
||||
```
|
||||
|
||||
**Example: File selection**
|
||||
|
||||
```python
|
||||
# In FilesView (component)
|
||||
class FileSelected(Message):
|
||||
def __init__(self, path: Path):
|
||||
self.path = path
|
||||
super().__init__()
|
||||
|
||||
def on_tree_node_selected(self, event):
|
||||
if event.node.data.is_file:
|
||||
self.post_message(self.FileSelected(event.node.data.path))
|
||||
|
||||
# In SidebarPanel (panel)
|
||||
def on_files_view_file_selected(self, event: FilesView.FileSelected):
|
||||
# Re-emit for app to handle
|
||||
self.post_message(self.FileSelected(event.path))
|
||||
|
||||
# In ClideApp (app)
|
||||
def on_sidebar_panel_file_selected(self, event: SidebarPanel.FileSelected):
|
||||
self.editor_controller.open_file(event.path)
|
||||
self.show_workspace("editor")
|
||||
```
|
||||
|
||||
### Reactive Properties
|
||||
|
||||
State that affects UI uses Textual's reactive system:
|
||||
|
||||
```python
|
||||
class ClideApp(App):
|
||||
# Reactive state
|
||||
workspace_visible: reactive[bool] = reactive(False)
|
||||
problem_count: reactive[int] = reactive(0)
|
||||
current_branch: reactive[str] = reactive("main")
|
||||
|
||||
def watch_workspace_visible(self, visible: bool) -> None:
|
||||
"""React to workspace visibility changes."""
|
||||
workspace = self.query_one("#panel-workspace")
|
||||
workspace.display = visible
|
||||
|
||||
claude = self.query_one("#panel-claude")
|
||||
claude.styles.height = "40%" if visible else "100%"
|
||||
```
|
||||
|
||||
### Background Tasks
|
||||
|
||||
Long-running operations use the `@work` decorator to avoid blocking the UI:
|
||||
|
||||
```python
|
||||
from textual import work
|
||||
|
||||
class ClideApp(App):
|
||||
@work(thread=True)
|
||||
def refresh_git_status(self) -> None:
|
||||
"""Refresh git status in background."""
|
||||
status = self.git_controller.get_status_sync()
|
||||
self.call_from_thread(self._update_git_ui, status)
|
||||
|
||||
def _update_git_ui(self, status: GitStatus) -> None:
|
||||
"""Update UI with git status (runs on main thread)."""
|
||||
sidebar = self.query_one(SidebarPanel)
|
||||
sidebar.update_git_status(status.staged, status.unstaged)
|
||||
```
|
||||
|
||||
## Extension System
|
||||
|
||||
Clide uses Pluggy for extensibility. Extensions implement hooks defined in `hookspecs.py`.
|
||||
|
||||
```python
|
||||
# clide/extensions/hookspecs.py
|
||||
class ClideHookSpec:
|
||||
@hookspec
|
||||
def clide_startup(self, app: App) -> None:
|
||||
"""Called when app starts."""
|
||||
|
||||
@hookspec
|
||||
def clide_on_file_changed(self, event: FileEvent) -> None:
|
||||
"""Called when a file changes."""
|
||||
|
||||
# User extension
|
||||
class MyExtension:
|
||||
@hookimpl
|
||||
def clide_on_file_changed(self, event: FileEvent) -> None:
|
||||
if event.path.suffix == ".py":
|
||||
# Custom logic for Python files
|
||||
...
|
||||
```
|
||||
|
||||
**Available hooks:**
|
||||
- `clide_startup` — App initialization
|
||||
- `clide_shutdown` — App cleanup
|
||||
- `clide_on_file_changed` — File system changes
|
||||
- `clide_on_file_saved` — File saved in editor
|
||||
|
||||
## Skills System
|
||||
|
||||
Clide integrates with Claude Code skills for git operations. Skills are installed to the project's `.claude/skills/` directory.
|
||||
|
||||
```python
|
||||
# clide/services/skill_installer.py
|
||||
class SkillInstaller:
|
||||
def install(self, skill_name: str, scope: Literal["user", "project"] = "project"):
|
||||
"""Install a skill from bundled templates."""
|
||||
template_dir = TEMPLATES_DIR / skill_name
|
||||
target_dir = self.project_skills_dir / skill_name
|
||||
shutil.copytree(template_dir, target_dir)
|
||||
```
|
||||
|
||||
**Bundled skills** (`clide/templates/skills/`):
|
||||
- `commit` — Git commit workflow
|
||||
- `stash` — Git stash operations
|
||||
- `pull` — Git pull with rebase
|
||||
- `push` — Git push to remote
|
||||
- `branch` — Branch management
|
||||
|
||||
When a git action button is clicked, Clide ensures the skill is installed before sending the command to Claude.
|
||||
|
||||
## Testing
|
||||
|
||||
Tests mirror the source structure:
|
||||
|
||||
```
|
||||
tests/
|
||||
├── unit/
|
||||
│ ├── test_models.py
|
||||
│ ├── test_services.py
|
||||
│ ├── test_controllers.py
|
||||
│ ├── test_widgets.py
|
||||
│ └── test_app.py
|
||||
├── integration/
|
||||
│ └── test_files_view.py
|
||||
└── snapshots/
|
||||
└── test_app_snapshots.py
|
||||
```
|
||||
|
||||
**Unit tests** verify individual components in isolation.
|
||||
**Integration tests** verify component interactions.
|
||||
**Snapshot tests** catch visual regressions using pytest-textual-snapshot.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Application Settings
|
||||
|
||||
`ClideSettings` in `clide/models/config.py` defines app configuration loaded from environment or `.config/settings.toml`.
|
||||
|
||||
### User Settings
|
||||
|
||||
`UserSettings` persisted to `~/.clide/settings.json` stores user preferences:
|
||||
- Theme selection
|
||||
- Panel visibility defaults
|
||||
- Compact mode preference
|
||||
- Jira integration settings
|
||||
@@ -1,366 +0,0 @@
|
||||
# TUI IDE Specification
|
||||
|
||||
Design specification for Clide's terminal user interface. This document describes the layout, interactions, and design decisions.
|
||||
|
||||
## Design Principles
|
||||
|
||||
- **Claude-centric** — Claude Code is the primary workspace, always visible
|
||||
- **Contextual panels** — Editor/Diff/Terminal appear only when needed
|
||||
- **Alt-key shortcuts** — Keybindings use Alt to avoid conflicts with Claude Code input
|
||||
- **Responsive** — Works on 13" laptops to widescreen monitors
|
||||
- **State preservation** — Hiding panels preserves all state (never destroy widgets)
|
||||
|
||||
---
|
||||
|
||||
## Panel Structure
|
||||
|
||||
### Layout Overview
|
||||
|
||||
```
|
||||
┌─────────────────┬─────────────────────────┬──────────────────┐
|
||||
│ panel-sidebar │ panel-workspace (60%) │ panel-context │
|
||||
│ 20% │ [Editor][Diff][Terminal]│ 25% │
|
||||
│ │ (hidden when inactive) │ │
|
||||
│ [Files][Git] ├─────────────────────────┤ [Jira][TODOs] │
|
||||
│ [Tree] │ │ [Problems] │
|
||||
│ │ panel-claude │ │
|
||||
│ (content area) │ (40% when workspace │ (content area) │
|
||||
│ │ visible, else 100%) │ │
|
||||
├─────────────────┤ ├──────────────────┤
|
||||
│ branch-status │ │ │
|
||||
│ ⎇ main ▾ │ │ │
|
||||
│ staged: 2 │ │ │
|
||||
└─────────────────┴─────────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
### Panel IDs
|
||||
|
||||
```python
|
||||
PANELS = {
|
||||
# Left sidebar
|
||||
"sidebar": "panel-sidebar",
|
||||
|
||||
# Center
|
||||
"claude": "panel-claude",
|
||||
"workspace": "panel-workspace",
|
||||
|
||||
# Right context
|
||||
"context": "panel-context",
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Left Sidebar
|
||||
|
||||
### Tabs
|
||||
|
||||
| Tab | Content | Purpose |
|
||||
|-----|---------|---------|
|
||||
| Files | Project file tree | Navigate and open files |
|
||||
| Git | Staged/Unstaged changes | Review and manage changes |
|
||||
| Tree | Branch graph | Visualize git history |
|
||||
|
||||
### Git Tab
|
||||
|
||||
Two collapsible sections showing staged and unstaged changes.
|
||||
|
||||
**File status indicators:**
|
||||
- `+` Added
|
||||
- `~` Modified
|
||||
- `-` Deleted
|
||||
- `?` Untracked
|
||||
- `→` Renamed
|
||||
|
||||
**Action buttons:**
|
||||
- **Commit** — Delegate to Claude with `/commit` skill
|
||||
- **Stash** — Delegate to Claude with `/stash` skill
|
||||
- **Pull** — Delegate to Claude with `/pull` skill
|
||||
- **Push** — Delegate to Claude with `/push` skill
|
||||
|
||||
### Tree Tab
|
||||
|
||||
Visual git graph using box-drawing characters:
|
||||
|
||||
```
|
||||
● main: Latest commit message
|
||||
│
|
||||
├─● feature: Feature work
|
||||
│
|
||||
●─┴ Merge branch 'feature'
|
||||
◆ Tagged release v1.0
|
||||
```
|
||||
|
||||
**Symbols:**
|
||||
- `●` Regular commit
|
||||
- `◆` Merge commit
|
||||
- `│` Branch line
|
||||
- `├` Branch point
|
||||
- `┴` Merge point
|
||||
|
||||
### Branch Status Bar
|
||||
|
||||
Fixed at bottom of sidebar. Shows current branch and git stats.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ ⎇ main ▾ staged: 2 unstaged: 5│
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
Click to expand branch selector:
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Recent branches │
|
||||
│ ● main │
|
||||
│ ○ feature/xyz │
|
||||
│ ○ develop │
|
||||
├─────────────────┤
|
||||
│[Checkout] [New] │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Center Column
|
||||
|
||||
### Claude Panel
|
||||
|
||||
The primary workspace. Always visible.
|
||||
|
||||
**Default state:** 100% height of center column
|
||||
**With workspace:** 40% height (bottom)
|
||||
|
||||
**Content:**
|
||||
- Full PTY terminal running Claude Code CLI
|
||||
- Scrollback history (1000 lines)
|
||||
- Input at bottom
|
||||
|
||||
### Workspace Panel
|
||||
|
||||
Tabbed container for Editor, Diff, and Terminal. Hidden by default.
|
||||
|
||||
**Visibility principle:** Hiding is not closing. All panels retain state:
|
||||
- Editor: Open file, cursor position, scroll, unsaved changes
|
||||
- Diff: Current diff content, scroll position
|
||||
- Terminal: Command history, output buffer
|
||||
|
||||
**Visibility triggers:**
|
||||
|
||||
| Trigger | Result |
|
||||
|---------|--------|
|
||||
| Click file in sidebar | Show workspace, focus Editor |
|
||||
| Click problem/TODO | Show workspace, focus Editor at line |
|
||||
| Press `` Alt+` `` | Show workspace, focus Terminal |
|
||||
| Close all content | Hide workspace, Claude reclaims space |
|
||||
|
||||
#### Editor Tab
|
||||
|
||||
Code editor with:
|
||||
- Syntax highlighting (tree-sitter based)
|
||||
- Line numbers
|
||||
- Current line highlighting
|
||||
|
||||
#### Diff Tab
|
||||
|
||||
Side-by-side diff viewer for:
|
||||
- Git changes (staged and unstaged)
|
||||
- Claude-proposed edits
|
||||
|
||||
#### Terminal Tab
|
||||
|
||||
Command execution terminal:
|
||||
- Working directory tied to project root
|
||||
- Output preserved when panel hidden
|
||||
|
||||
---
|
||||
|
||||
## Right Context Panel
|
||||
|
||||
### Tabs
|
||||
|
||||
| Tab | Badge | Content |
|
||||
|-----|-------|---------|
|
||||
| Jira | — | Jira issue display |
|
||||
| TODOs | Count | TODO/FIXME from code and TODO.md |
|
||||
| Problems | Count | Linter errors and warnings |
|
||||
|
||||
Tab badges update reactively as counts change.
|
||||
|
||||
### Jira Tab
|
||||
|
||||
Displays Jira issues via CLI integration. Manual refresh button.
|
||||
|
||||
### TODOs Tab
|
||||
|
||||
Two sub-tabs:
|
||||
|
||||
**Project tab:** Items from `TODO.md` (checkbox format)
|
||||
**Comments tab:** TODO/FIXME/HACK/XXX comments in code
|
||||
|
||||
Click any item to jump to source location.
|
||||
|
||||
### Problems Tab
|
||||
|
||||
Linter output showing:
|
||||
- File path
|
||||
- Line number
|
||||
- Severity (error/warning)
|
||||
- Message
|
||||
|
||||
Click to navigate to source.
|
||||
|
||||
---
|
||||
|
||||
## Responsiveness
|
||||
|
||||
### CSS Strategy
|
||||
|
||||
```css
|
||||
/* Default layout */
|
||||
#panel-sidebar { width: 20%; min-width: 25; }
|
||||
#panel-context { width: 25%; min-width: 30; }
|
||||
#panel-claude { width: 1fr; }
|
||||
```
|
||||
|
||||
### Compact Mode
|
||||
|
||||
Toggle with `Alt+C`. Hides both sidebars:
|
||||
|
||||
```css
|
||||
.compact #panel-sidebar { display: none; }
|
||||
.compact #panel-context { display: none; }
|
||||
```
|
||||
|
||||
All panel state preserved when hidden.
|
||||
|
||||
---
|
||||
|
||||
## Keybindings
|
||||
|
||||
All shortcuts use `Alt` modifier to avoid conflicts with Claude Code input.
|
||||
|
||||
### Panel Navigation
|
||||
|
||||
| Action | Binding |
|
||||
|--------|---------|
|
||||
| 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` |
|
||||
|
||||
### Application
|
||||
|
||||
| Action | Binding |
|
||||
|--------|---------|
|
||||
| Command palette | `Alt+P` |
|
||||
| Quick open file | `Alt+O` |
|
||||
| Select theme | `Alt+T` |
|
||||
| Quit | `Alt+Q` |
|
||||
|
||||
### Git
|
||||
|
||||
| Action | Binding |
|
||||
|--------|---------|
|
||||
| Open Git panel | `Alt+G` |
|
||||
|
||||
### Editor
|
||||
|
||||
| Action | Binding |
|
||||
|--------|---------|
|
||||
| Save | `Alt+S` |
|
||||
| Go to line | `Alt+L` |
|
||||
| Go to problems | `Alt+M` |
|
||||
|
||||
---
|
||||
|
||||
## Panel Communication
|
||||
|
||||
### File Navigation Flow
|
||||
|
||||
```
|
||||
Sidebar file click
|
||||
│
|
||||
▼
|
||||
Workspace appears (if hidden)
|
||||
│
|
||||
▼
|
||||
Editor tab focused
|
||||
│
|
||||
▼
|
||||
File loaded in Editor
|
||||
```
|
||||
|
||||
### Problem/TODO Navigation Flow
|
||||
|
||||
```
|
||||
Click problem/todo item
|
||||
│
|
||||
▼
|
||||
Workspace appears (if hidden)
|
||||
│
|
||||
▼
|
||||
Editor tab focused
|
||||
│
|
||||
▼
|
||||
File opened at specific line
|
||||
│
|
||||
▼
|
||||
Line scrolled into view
|
||||
```
|
||||
|
||||
### Git Action Flow
|
||||
|
||||
```
|
||||
Click git action button (Commit, Stash, etc.)
|
||||
│
|
||||
▼
|
||||
Ensure skill installed (async, with notification)
|
||||
│
|
||||
▼
|
||||
Send /command to Claude
|
||||
│
|
||||
▼
|
||||
Claude executes git workflow
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## State Management
|
||||
|
||||
### Reactive Properties
|
||||
|
||||
```python
|
||||
class ClideApp(App):
|
||||
workspace_visible: reactive[bool] = reactive(False)
|
||||
problem_count: reactive[int] = reactive(0)
|
||||
todo_count: reactive[int] = reactive(0)
|
||||
current_branch: reactive[str] = reactive("main")
|
||||
compact_mode: reactive[bool] = reactive(False)
|
||||
```
|
||||
|
||||
### State Preservation
|
||||
|
||||
| Panel | Preserved State |
|
||||
|-------|-----------------|
|
||||
| Editor | Open file, cursor, scroll, unsaved changes |
|
||||
| Diff | Current diff, scroll position |
|
||||
| Terminal | Session, history, output buffer |
|
||||
| Sidebar tabs | Scroll, expanded sections, selection |
|
||||
| Context tabs | Scroll, selected item |
|
||||
|
||||
---
|
||||
|
||||
## Themes
|
||||
|
||||
22 built-in themes with custom theme support.
|
||||
|
||||
**Default:** summer-night (dark theme)
|
||||
|
||||
Theme selection persists in user settings (`~/.clide/settings.json`).
|
||||
|
||||
Custom themes can be added to `~/.clide/themes/` as TOML files.
|
||||
@@ -1,398 +0,0 @@
|
||||
# Clide User Manual
|
||||
|
||||
Clide is a terminal-based IDE that puts Claude Code at the center of your development workflow. This manual covers installation, daily usage, and customization.
|
||||
|
||||
## Installation
|
||||
|
||||
### Requirements
|
||||
|
||||
- Python 3.12 or later
|
||||
- Git
|
||||
- Claude Code CLI installed and authenticated
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone <repo-url>
|
||||
cd clide
|
||||
|
||||
# Install dependencies and create virtual environment
|
||||
make setup
|
||||
|
||||
# Run Clide
|
||||
make run
|
||||
```
|
||||
|
||||
Or install directly:
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
clide
|
||||
```
|
||||
|
||||
### First Run
|
||||
|
||||
On first launch, Clide creates a configuration directory at `~/.clide/` for user settings. Project-specific settings are stored in `.clide/` within your project.
|
||||
|
||||
## Interface Overview
|
||||
|
||||
```
|
||||
┌─────────────────┬─────────────────────────┬──────────────────┐
|
||||
│ Sidebar │ Workspace │ Context │
|
||||
│ │ [Editor][Diff][Terminal]│ │
|
||||
│ [Files][Git] │ (appears when needed) │ [Jira][TODOs] │
|
||||
│ [Tree] ├─────────────────────────┤ [Problems] │
|
||||
│ │ │ │
|
||||
│ │ Claude │ │
|
||||
│ │ (always visible) │ │
|
||||
│ │ │ │
|
||||
├─────────────────┤ ├──────────────────┤
|
||||
│ ⎇ main ▾ │ │ │
|
||||
│ staged: 2 │ │ │
|
||||
└─────────────────┴─────────────────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
### Panels
|
||||
|
||||
**Left Sidebar** — File browser, git changes, and branch graph
|
||||
|
||||
**Center** — Claude Code (always visible) and workspace panels (editor, diff, terminal) that appear when needed
|
||||
|
||||
**Right Context** — Jira integration, TODO list, and problems from linters
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
All shortcuts use `Alt` as the modifier to avoid conflicts with Claude Code input.
|
||||
|
||||
### Panel Navigation
|
||||
|
||||
| Action | Shortcut |
|
||||
|--------|----------|
|
||||
| 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` |
|
||||
|
||||
### File Operations
|
||||
|
||||
| Action | Shortcut |
|
||||
|--------|----------|
|
||||
| Quick open file | `Alt+O` |
|
||||
| Save file | `Alt+S` |
|
||||
| Go to line | `Alt+L` |
|
||||
|
||||
### Git Operations
|
||||
|
||||
| Action | Shortcut |
|
||||
|--------|----------|
|
||||
| Open Git panel | `Alt+G` |
|
||||
| Open Problems panel | `Alt+M` |
|
||||
|
||||
### Application
|
||||
|
||||
| Action | Shortcut |
|
||||
|--------|----------|
|
||||
| Command palette | `Alt+P` |
|
||||
| Select theme | `Alt+T` |
|
||||
| Quit | `Alt+Q` |
|
||||
|
||||
## Working with Claude
|
||||
|
||||
Claude Code runs in the center panel and is always visible. Type your prompts directly and Claude will respond with code suggestions, explanations, and file operations.
|
||||
|
||||
### Git Integration
|
||||
|
||||
The sidebar includes buttons for common git operations that delegate to Claude:
|
||||
|
||||
- **Commit** — Claude reviews staged changes and creates a well-formatted commit
|
||||
- **Stash** — Claude stashes your working changes
|
||||
- **Pull** — Claude pulls with rebase and helps resolve conflicts
|
||||
- **Push** — Claude pushes to remote, setting upstream if needed
|
||||
|
||||
On first use, Clide installs the corresponding skill to your project's `.claude/skills/` directory. These skills guide Claude through each operation following best practices.
|
||||
|
||||
### Branch Status
|
||||
|
||||
The branch status bar at the bottom of the sidebar shows:
|
||||
|
||||
- Current branch name
|
||||
- Staged and unstaged file counts
|
||||
|
||||
Click the branch name to open the branch selector:
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Recent branches │
|
||||
│ ● main │
|
||||
│ ○ feature/xyz │
|
||||
│ ○ develop │
|
||||
├─────────────────┤
|
||||
│[Checkout] [New] │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Left Sidebar
|
||||
|
||||
### Files Tab
|
||||
|
||||
Browse your project structure. Click a file to open it in the editor.
|
||||
|
||||
- Directories expand/collapse on click
|
||||
- Hidden files (starting with `.`) are shown but dimmed
|
||||
- Noisy directories (`.git`, `__pycache__`, `node_modules`) are filtered
|
||||
|
||||
### Git Tab
|
||||
|
||||
View staged and unstaged changes:
|
||||
|
||||
```
|
||||
Staged (2)
|
||||
+ src/new_file.py
|
||||
~ src/modified.py
|
||||
|
||||
Unstaged (3)
|
||||
~ README.md
|
||||
? untracked.txt
|
||||
- deleted.py
|
||||
```
|
||||
|
||||
**Status indicators:**
|
||||
- `+` Added
|
||||
- `~` Modified
|
||||
- `-` Deleted
|
||||
- `?` Untracked
|
||||
- `→` Renamed
|
||||
|
||||
Click a file to view its diff. Use the action buttons to commit, stash, pull, or push via Claude.
|
||||
|
||||
### Tree Tab
|
||||
|
||||
Visual git graph showing branch history:
|
||||
|
||||
```
|
||||
● main: Latest commit message
|
||||
│
|
||||
├─● feature: Feature work
|
||||
│
|
||||
●─┴ Merge branch 'feature'
|
||||
```
|
||||
|
||||
**Commit types:**
|
||||
- `●` Regular commit
|
||||
- `◆` Merge commit
|
||||
|
||||
## Right Context Panel
|
||||
|
||||
### Jira Tab
|
||||
|
||||
Displays Jira issues when configured. Click the refresh button to update.
|
||||
|
||||
Configure Jira integration in settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"jira_enabled": true,
|
||||
"jira_cli_path": "jira"
|
||||
}
|
||||
```
|
||||
|
||||
### TODOs Tab
|
||||
|
||||
Scans your codebase for TODO comments and project tasks.
|
||||
|
||||
**Sub-tabs:**
|
||||
|
||||
- **Project** — Items from `TODO.md` (checkbox format)
|
||||
- **Comments** — TODO/FIXME/HACK/XXX comments in code
|
||||
|
||||
Click an item to jump to that location in the editor.
|
||||
|
||||
**Supported comment markers:**
|
||||
- `TODO` — Tasks to complete
|
||||
- `FIXME` — Bugs to fix
|
||||
- `HACK` — Temporary solutions
|
||||
- `XXX` — Dangerous or problematic code
|
||||
- `NOTE` — Important notes
|
||||
- `BUG` — Known bugs
|
||||
|
||||
### Problems Tab
|
||||
|
||||
Displays linter errors and warnings. Click a problem to jump to the source location.
|
||||
|
||||
## Workspace Panels
|
||||
|
||||
The workspace appears when you need to view or edit files. It contains three tabs:
|
||||
|
||||
### Editor
|
||||
|
||||
Full-featured code editor with:
|
||||
|
||||
- Syntax highlighting (Python, JavaScript, TypeScript, HTML, CSS, JSON, YAML, Markdown, and more)
|
||||
- Line numbers
|
||||
- Current line highlighting
|
||||
|
||||
### Diff
|
||||
|
||||
Side-by-side diff viewer for reviewing changes. Used when:
|
||||
|
||||
- Viewing git changes
|
||||
- Reviewing Claude's proposed edits
|
||||
|
||||
### Terminal
|
||||
|
||||
Command-line terminal for running commands. Output is preserved when the panel is hidden.
|
||||
|
||||
## Themes
|
||||
|
||||
Clide includes 22 built-in themes. Press `Alt+T` to open the theme selector.
|
||||
|
||||
**Theme categories:**
|
||||
|
||||
| Category | Themes |
|
||||
|----------|--------|
|
||||
| Core | summer-night (default), summer-day |
|
||||
| Popular | one-dark, one-dark-pro, one-light, dracula, nord, gruvbox-dark, gruvbox-light |
|
||||
| Seasonal | winter-is-coming, monokai-winter, fall, dark-autumn |
|
||||
| Halloween | all-hallows-eve, halloween |
|
||||
| Christmas | christmas, santa-baby |
|
||||
| Hacker | pro-hacker, hacker-style |
|
||||
| Other | gamma, one-dark-teal, houston |
|
||||
|
||||
Your theme choice is saved and persists across sessions.
|
||||
|
||||
### Custom Themes
|
||||
|
||||
Create custom themes in `~/.clide/themes/`:
|
||||
|
||||
```toml
|
||||
# ~/.clide/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"
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### User Settings
|
||||
|
||||
Settings are stored in `~/.clide/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"theme": "summer-night",
|
||||
"compact_mode": false,
|
||||
"jira_enabled": false,
|
||||
"jira_cli_path": "jira"
|
||||
}
|
||||
```
|
||||
|
||||
### Project Settings
|
||||
|
||||
Project-specific settings in `.clide/`:
|
||||
|
||||
```
|
||||
.clide/
|
||||
├── settings.json # Project overrides
|
||||
└── skills/ # Installed Claude skills
|
||||
├── commit/
|
||||
├── stash/
|
||||
└── ...
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Override settings with environment variables prefixed with `CLIDE_`:
|
||||
|
||||
```bash
|
||||
CLIDE_THEME=dracula clide
|
||||
```
|
||||
|
||||
## Compact Mode
|
||||
|
||||
Press `Alt+C` to toggle compact mode, which hides both sidebars for focused work. All panel state is preserved—nothing is lost when hiding panels.
|
||||
|
||||
## Project TODOs
|
||||
|
||||
Clide integrates with a `TODO.md` file in your project root. Format:
|
||||
|
||||
```markdown
|
||||
# TODO
|
||||
|
||||
## Features
|
||||
|
||||
- [ ] Implement user authentication
|
||||
- [ ] Add search functionality
|
||||
- [x] Set up database connection
|
||||
|
||||
## Bugs
|
||||
|
||||
- [ ] Fix login redirect
|
||||
```
|
||||
|
||||
Items appear in the TODOs panel, grouped by section. Click to jump to that line. Check off items directly in the file.
|
||||
|
||||
If no `TODO.md` exists, click "Create TODO.md" in the TODOs panel to generate a template.
|
||||
|
||||
## Tips
|
||||
|
||||
### Efficient Navigation
|
||||
|
||||
1. Use `Alt+1/2/3` to quickly switch between Claude, Editor, and Terminal
|
||||
2. Click items in Problems or TODOs to jump directly to source
|
||||
3. Use compact mode (`Alt+C`) when you need more space for Claude
|
||||
|
||||
### Git Workflow
|
||||
|
||||
1. Make changes to your code
|
||||
2. Review changes in the Git tab
|
||||
3. Click "Commit" to have Claude create a well-formatted commit
|
||||
4. Use "Push" when ready to share
|
||||
|
||||
### Working with Claude
|
||||
|
||||
- Claude sees your project context automatically
|
||||
- Use the git action buttons for consistent commit messages
|
||||
- Click files in the sidebar to show Claude what you're working on
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Claude Code not starting
|
||||
|
||||
Ensure Claude Code CLI is installed and authenticated:
|
||||
|
||||
```bash
|
||||
claude --version
|
||||
claude auth status
|
||||
```
|
||||
|
||||
### Theme not applying
|
||||
|
||||
Check that the theme name in settings matches exactly. Theme names are case-sensitive.
|
||||
|
||||
### Skills not working
|
||||
|
||||
Skills are installed to `.claude/skills/` in your project. If a skill fails:
|
||||
|
||||
1. Check that the skill folder exists
|
||||
2. Verify `SKILL.md` is present
|
||||
3. Try removing and re-triggering the action
|
||||
|
||||
### Panels not updating
|
||||
|
||||
Try refreshing with the relevant shortcut or clicking the refresh button. File changes should update automatically via file watching.
|
||||
@@ -1,173 +0,0 @@
|
||||
# Web Deployment
|
||||
|
||||
Clide runs in a browser via a two-layer stack:
|
||||
|
||||
```
|
||||
Browser (code.schweitz.net)
|
||||
└─ clide-web (FastAPI + uvicorn, port 8888)
|
||||
└─ tmux (session persistence)
|
||||
└─ clide (Textual TUI)
|
||||
└─ claude code (embedded PTY)
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
| Layer | Purpose | Config |
|
||||
|-------|---------|--------|
|
||||
| **clide-web** | FastAPI server: HTML page, WebSocket ↔ PTY bridge, REST API | systemd service on port 8888 |
|
||||
| **tmux** | Session persistence (detach/reattach on browser disconnect) | Managed by clide-web |
|
||||
| **clide** | TUI IDE wrapper around Claude Code | Spawned by tmux |
|
||||
|
||||
## Installation
|
||||
|
||||
From the clide project root:
|
||||
|
||||
```bash
|
||||
sudo bash deploy/install-clide-web.sh
|
||||
```
|
||||
|
||||
This:
|
||||
1. Installs `clide-web` Python package into the clide venv
|
||||
2. Installs `clide-web.service` systemd unit
|
||||
3. Enables and starts the service
|
||||
|
||||
### First-Run Setup
|
||||
|
||||
After installing, run the setup wizard to configure projects directory and clide binary path:
|
||||
|
||||
```bash
|
||||
clide-web-setup
|
||||
```
|
||||
|
||||
Settings are stored in `~/.clide/clide.db` as UserPreference records.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.12+ with clide venv set up (`make setup`)
|
||||
- `tmux` installed (`sudo dnf install tmux` / `sudo apt install tmux`)
|
||||
|
||||
### Reverse Proxy
|
||||
|
||||
For external access, configure your reverse proxy (e.g., Nginx Proxy Manager) to:
|
||||
- Proxy `code.schweitz.net` → `localhost:8888`
|
||||
- Enable WebSocket support
|
||||
|
||||
## Architecture
|
||||
|
||||
### clide-web
|
||||
|
||||
FastAPI application serving:
|
||||
- `GET /` — HTML page with xterm.js terminal + toolbar
|
||||
- `GET /projects/{name}` — Project terminal page
|
||||
- `WS /projects/{name}/ws` — WebSocket terminal bridge
|
||||
- `GET /api/projects` — List available git repos
|
||||
- `GET /api/sessions` — List active tmux sessions
|
||||
- `GET /health` — Health check for reverse proxy
|
||||
|
||||
**WebSocket Protocol:**
|
||||
|
||||
| Prefix | Direction | Purpose |
|
||||
|--------|-----------|---------|
|
||||
| `0` | both | Terminal data |
|
||||
| `1` | both | Control message (JSON) |
|
||||
| `2` | client→server | Resize: `cols,rows` |
|
||||
|
||||
### tmux
|
||||
|
||||
Managed programmatically by clide-web. One session per project (`clide-<project>`).
|
||||
|
||||
- Browser disconnect → tmux session persists, reconnect shows current state
|
||||
- Clide exit (Alt+Q) → `pane-died` hook auto-respawns a fresh Clide instance
|
||||
- Status bar hidden for clide-web sessions only (user's other tmux sessions unaffected)
|
||||
- Full environment inherited (HOME, PATH, etc.)
|
||||
|
||||
### Keybindings
|
||||
|
||||
All keys pass through directly to Clide — no intermediate layer captures keys.
|
||||
No keybinding conflicts (unlike the previous Zellij-based setup).
|
||||
|
||||
### Database
|
||||
|
||||
SQLite database at `~/.clide/clide.db` shared between clide and clide-web.
|
||||
Uses SQLModel (Pydantic-native ORM by FastAPI's creator).
|
||||
|
||||
Tables:
|
||||
- **Project** — name, path, theme, last_accessed
|
||||
- **Session** — tmux session name, status, last_activity
|
||||
- **UserPreference** — key/value settings (projects_dir, clide_bin, port, etc.)
|
||||
- **ConnectionLog** — client IP, connect/disconnect timestamps
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings priority: environment variables > database preferences > defaults.
|
||||
|
||||
Environment variables (prefix `CLIDE_WEB_`):
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `CLIDE_WEB_HOST` | `0.0.0.0` | Server bind address |
|
||||
| `CLIDE_WEB_PORT` | `8888` | Server port |
|
||||
| `CLIDE_WEB_PROJECTS_DIR` | `/mnt/media/Projects` | Directory containing git repos |
|
||||
| `CLIDE_WEB_CLIDE_BIN` | `clide` | Path to clide binary |
|
||||
| `CLIDE_WEB_DB_PATH` | `~/.clide/clide.db` | SQLite database path |
|
||||
|
||||
## URL Patterns
|
||||
|
||||
- `code.schweitz.net` — auto-selects first project
|
||||
- `code.schweitz.net/projects/clide` — opens/attaches to the clide project
|
||||
|
||||
The toolbar dropdown allows switching projects. URL updates via `history.pushState`.
|
||||
|
||||
## Operations
|
||||
|
||||
### Service Management
|
||||
|
||||
```bash
|
||||
# Using make targets (from clide-web/ directory)
|
||||
make start-server # Start the systemd service
|
||||
make stop-server # Stop the systemd service
|
||||
make restart-server # Restart the systemd service
|
||||
make status-server # Show service status
|
||||
make logs-server # Tail service logs
|
||||
|
||||
# Or directly with systemctl
|
||||
systemctl status clide-web
|
||||
sudo systemctl restart clide-web
|
||||
journalctl -u clide-web -f
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
```bash
|
||||
# List sessions
|
||||
tmux list-sessions
|
||||
|
||||
# Kill a specific session
|
||||
tmux kill-session -t clide-myproject
|
||||
|
||||
# Kill all clide sessions
|
||||
tmux list-sessions | grep ^clide- | cut -d: -f1 | xargs -I{} tmux kill-session -t {}
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
clide-web/ # Python package
|
||||
├── 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 configuration
|
||||
│ └── static/
|
||||
│ ├── index.html # HTML page (toolbar + xterm.js)
|
||||
│ └── vendor/ # Vendored xterm.js (no CDN dependencies)
|
||||
├── pyproject.toml
|
||||
└── Makefile
|
||||
deploy/
|
||||
├── install-clide-web.sh # Installation script (run with sudo)
|
||||
└── clide-web.service # systemd unit file
|
||||
clide/
|
||||
├── models/db.py # SQLModel table definitions (shared)
|
||||
└── services/database.py # SQLite engine and session factory
|
||||
```
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Reference in New Issue
Block a user