Set up the TUI IDE wrapper for Claude Code CLI with: - Core app structure using Textual framework - Panel architecture (sidebar, workspace, claude, context) - Theme system with 22 built-in themes (Summer Night default) - Pydantic models for configuration and data - Makefile for development commands - Project documentation and specs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
TUI IDE Specification
A terminal-based IDE built with Textual, designed to wrap Claude Code and integrate project management tooling (Jira/Confluence via CLI).
Design Principles
- Claude-centric: Claude Code is the primary workspace, always visible
- Contextual panels: Editor/Diff/Terminal appear only when needed
- VSCode-familiar: Keybindings and interaction patterns follow VSCode conventions
- Responsive: Works on 13" laptop and widescreen monitors
- No vim magic: Standard keyboard navigation, no modal editing
Panel Structure
Layout Overview
┌─────────────────┬─────────────────────────┬──────────────────┐
│ panel-sidebar │ panel-workspace (60%) │ panel-context │
│ │ [Editor][Diff][Terminal]│ │
│ [Files][Git] │ (hidden when inactive) │ (content area) │
│ [Tree] ├─────────────────────────┤ │
│ │ │ │
│ (content area) │ panel-claude │ │
│ │ (40% when workspace │ │
│ │ visible, else 100%) │ │
├─────────────────┤ ├──────────────────┤
│ branch-status │ │[⚠ 3][✓12][Jira] │
│ ⎇ main ▾ │ │ context-tabs │
└─────────────────┴─────────────────────────┴──────────────────┘
Panel Definitions
PANELS = {
# Left sidebar
"sidebar": "panel-sidebar",
"sidebar-files": "panel-sidebar-files",
"sidebar-git": "panel-sidebar-git",
"sidebar-tree": "panel-sidebar-tree",
"branch-status": "panel-branch-status",
# Center
"claude": "panel-claude",
"workspace": "panel-workspace",
"editor": "panel-editor",
"diff": "panel-diff",
"terminal": "panel-terminal",
# Right context
"context": "panel-context",
"context-jira": "panel-context-jira",
"context-problems": "panel-context-problems",
"context-todos": "panel-context-todos",
}
Left Sidebar (panel-sidebar)
Tabs
| Tab | Content | Widget |
|---|---|---|
| Files | Project file tree | DirectoryTree |
| Git | Staged/Unstaged changes | GitChangesView (custom) |
| Tree | Merge/branch graph | GitGraphView (custom) |
Git Tab Details
Two collapsible sections:
- Staged: Files in index, ready to commit
- Unstaged: Modified/untracked files
Each file item shows:
- Status icon:
+added,~modified,-deleted,?untracked,→renamed - File path (relative)
Interactions:
- Click file → opens in Editor panel
- Double-click or keybind → stage/unstage file
- Right-click or keybind → show context menu (discard, diff, etc.)
Tree Tab Details
Renders git log --graph --oneline --decorate --all with visual styling.
Polish item: Consider custom rendering with box-drawing characters for a cleaner look:
●──┬── main: Latest commit message
│ ●── feature: Feature work
●──┴── Merge branch 'feature'
◆───── Tagged release v1.0
Use canvas or rich text with:
GRAPH_CHARS = {
'commit': '●',
'merge': '◆',
'line': '│',
'branch': '├──',
'join': '┴──',
}
Branch Status Bar
Fixed at bottom of sidebar. Shows current branch with popout toggle.
┌─────────────────┐
│ ⎇ main ▾ │ ← Click or keybind to expand
└─────────────────┘
│
▼ (popout overlay)
┌─────────────────┐
│ Recent branches │
│ ○ main │
│ ○ feature/xyz │
│ ○ develop │
├─────────────────┤
│ [Checkout] [New]│
└─────────────────┘
Center Column
Claude Panel (panel-claude)
The primary workspace. Displays Claude Code interaction.
Default state: 100% height of center column With workspace: 40% height (bottom)
Content:
- Streaming markdown responses (use
MarkdownorRichLogwidget) - Visual distinction between:
- Claude's responses
- Tool calls / file operations
- User input
- Input area at bottom
Workspace Panel (panel-workspace)
Tabbed container for Editor, Diff, and Terminal. Hidden by default.
Important: Hiding is not closing. All panels retain state when hidden:
- Editor: Open files, cursor position, scroll position, unsaved changes
- Diff: Current diff content, scroll position
- Terminal: Active session, command history, output buffer
Use display: none for visibility, never destroy/recreate widgets.
Visibility triggers:
| Trigger | Result |
|---|---|
| Click file in sidebar | Show workspace, focus Editor tab |
| Claude proposes changes | Show workspace, focus Diff tab |
User presses Ctrl+` |
Show workspace, focus Terminal tab |
| User runs command | Show workspace, focus Terminal tab |
| Close all tabs / Escape | Hide workspace, Claude reclaims space |
Height: 60% of center column when visible
Editor Tab
TextAreawidget with syntax highlighting- Language detection from file extension
- Theme: Follow terminal theme or user preference
Diff Tab
- Side-by-side or unified diff view
- Syntax highlighting for changed content
- Accept/Reject buttons for Claude-proposed changes
Terminal Tab
- Proper PTY integration for full terminal emulation
- Or simpler command runner with output display (decide based on complexity)
- Working directory tied to project root
Right Sidebar (panel-context)
Content Area
Switches based on selected bottom tab. Shows one of:
- Jira view (default)
- Problems view
- TODOs view
Bottom Tab Bar (context-tabs)
┌──────────────────┐
│ [⚠ 3][✓12][Jira]│
└──────────────────┘
Tabs show inline counts that update reactively.
| Tab | Icon | Content |
|---|---|---|
| Problems | ⚠ | Linter errors, warnings (count badge) |
| TODOs | ✓ | TODO/FIXME comments from codebase (count badge) |
| Jira | Jira | Output from your CLI tool (default) |
Jira View
Renders markdown output from your CLI tool. Refreshes on:
- Panel focus
- Manual refresh keybind
- Configurable interval
Problems View
Aggregates from linters (eslint, ruff, etc.). Shows:
- File path
- Line number
- Severity icon
- Message
Click → opens file in Editor at that line.
TODOs View
Grep results for TODO, FIXME, HACK, XXX. Shows:
- File path
- Line number
- Comment text
Click → opens file in Editor at that line.
Responsiveness
CSS Breakpoints
/* Widescreen (default) */
#panel-sidebar { width: 20%; min-width: 25; }
#panel-context { width: 25%; min-width: 30; }
#panel-claude { width: 1fr; }
/* Medium terminals */
@media (width < 120) {
#panel-sidebar { width: 18%; }
#panel-context { width: 22%; }
}
/* Narrow terminals (laptop, split screen) */
@media (width < 100) {
#panel-sidebar { display: none; }
#panel-context { width: 25%; }
}
@media (width < 80) {
#panel-context { display: none; }
#panel-claude { width: 100%; }
}
Compact Mode
Toggle with Ctrl+Shift+C. Hides both sidebars, maximizes Claude + workspace.
.compact #panel-sidebar { display: none; }
.compact #panel-context { display: none; }
Fullscreen Mode
Any panel can go fullscreen with F11 (when focused). Press Escape to exit.
.fullscreen {
dock: top;
width: 100%;
height: 100%;
layer: fullscreen;
}
Keybindings
Following VSCode conventions where possible.
Global
| Action | Binding |
|---|---|
| Command palette | Ctrl+Shift+P |
| Quick open file | Ctrl+P |
| Toggle left sidebar | Ctrl+B |
| Toggle right sidebar | Ctrl+Shift+B |
| Toggle terminal | Ctrl+` |
| Toggle compact mode | Ctrl+Shift+C |
| Fullscreen focused panel | F11 |
| Exit fullscreen | Escape |
Navigation
| Action | Binding |
|---|---|
| Focus Claude panel | Ctrl+1 |
| Focus Editor | Ctrl+2 |
| Focus Terminal | Ctrl+3 |
| Focus sidebar | Ctrl+0 |
| Next tab (in tabbed panels) | Ctrl+Tab |
| Previous tab | Ctrl+Shift+Tab |
| Close current tab/editor | Ctrl+W |
Git
| Action | Binding |
|---|---|
| Open Git panel | Ctrl+Shift+G |
| Stage file | Ctrl+Enter (in git view) |
| Unstage file | Ctrl+Backspace (in git view) |
Search & Problems
| Action | Binding |
|---|---|
| Find in file | Ctrl+F |
| Find in project | Ctrl+Shift+F |
| Go to problems | Ctrl+Shift+M |
| Next problem | F8 |
| Previous problem | Shift+F8 |
Editor
| Action | Binding |
|---|---|
| Save | Ctrl+S |
| Undo | Ctrl+Z |
| Redo | Ctrl+Shift+Z |
| Go to line | Ctrl+G |
Panel Communication
Panels should feel connected, like a normal IDE.
File Navigation
Sidebar file click
│
▼
Workspace appears (if hidden)
│
▼
Editor tab focused
│
▼
File loaded in Editor
Problems/TODOs Navigation
Click problem/todo item
│
▼
Workspace appears (if hidden)
│
▼
Editor tab focused
│
▼
File opened at specific line
│
▼
Line highlighted/scrolled into view
Claude Diff Flow
Claude proposes file changes
│
▼
Workspace appears
│
▼
Diff tab focused
│
▼
Changes displayed with Accept/Reject
│
├─► Accept: Apply changes, optionally close diff
│
└─► Reject: Discard, close diff
Git File Actions
Click file in Git tab
│
▼
Workspace appears
│
▼
Diff tab shows unstaged changes
│
▼
Stage/unstage from diff view
Implementation Notes
Recommended Textual Widgets
| Component | Widget |
|---|---|
| File browser | DirectoryTree |
| Claude output | Markdown or RichLog (for streaming) |
| Editor | TextArea (syntax highlighting built-in) |
| Tabbed panels | TabbedContent, TabPane |
| Panel switching | ContentSwitcher |
| Problems/TODOs list | ListView with ListItem |
| Git graph | RichLog or custom canvas widget |
| Command palette | CommandPalette (built-in) |
Background Tasks
Use Textual's @work decorator for:
- Git status refresh
- Linter execution
- TODO scanning
- Jira CLI calls
@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)
State Management
Core principle: Hiding is not closing. All panels persist state when hidden.
class IDEApp(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)
Panel visibility pattern — toggle display, don't destroy:
def toggle_workspace(self, visible: bool) -> None:
workspace = self.query_one("#panel-workspace")
workspace.display = visible # Retains all child state
# Adjust Claude panel height
claude = self.query_one("#panel-claude")
claude.styles.height = "40%" if visible else "100%"
State to preserve per panel:
| Panel | Preserved State |
|---|---|
| Editor | Open files, cursor positions, scroll, unsaved changes, undo history |
| Diff | Current diff content, scroll position, accept/reject state |
| Terminal | PTY session, command history, output buffer, working directory |
| Sidebar tabs | Scroll position, expanded/collapsed sections, selection |
| Context tabs | Scroll position, selected item |
| Git views | Expanded sections, selected files |
Future Considerations
- Session persistence: Remember open files, panel sizes, last git state
- Multiple projects: Workspace switcher
- Claude history: Browse past conversations
- Custom themes: User-selectable color schemes
- Plugin system: User-defined panels/integrations