Version: 1.1
Last Updated: 2026-01-25
Target GPUI Version: Zed v0.220.3
Dependency Strategy: See docs/architecture/zed-reuse-strategy.md
TerminalG is a GPU-accelerated terminal application built on GPUI, combining terminal emulation with integrated artifact viewing in a unified workspace interface.
┌─────────────────────────────────────────────────────────────┐
│ TerminalGApp (GPUI App) │
│ - App-wide settings management │
│ - Workspace configuration system │
│ - Theme system │
│ - Global keybindings │
└──────────────────────┬──────────────────────────────────────┘
│
│ spawns
▼
┌─────────────────────────────────────────────────────────────┐
│ WorkspaceView (Root Container) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Workspace Tabs (Top) - [Work][Project][Test] │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌───────────┬─────────────────────┬───────────────────┐ │
│ │ │ │ │ │
│ │ File/ │ TerminalPane │ DocumentViewer │ │
│ │ Folder │ (Zed-based) │ Pane │ │
│ │ Browser │ │ │ │
│ │ │ [Tab1][Tab2][+] │ [Doc1][Doc2][+] │ │
│ │ │ │ │ │
│ │ [Hide] │ [Hide] │ [Hide] │ │
│ └───────────┴─────────────────────┴───────────────────┘ │
│ - Workspace config (per-workspace state persistence) │
│ - Pane visibility management │
│ - Tab management (terminals and documents) │
└─────────────────────────────────────────────────────────────┘
Key Features:
- Three co-equal panes (file browser, terminal, document viewer)
- Workspace tabs at top (switch entire context)
- Terminal tabs at bottom (multiple terminals per workspace)
- Document tabs at bottom (multiple documents per workspace)
- Independent pane visibility (hide/show with button)
| Component | Technology | Version | Source | License | Rationale |
|---|---|---|---|---|---|
| UI Framework | GPUI | v0.220.3 | git (Zed tag) | Apache-2.0 | GPU-accelerated, proven in Zed |
| Terminal Emulation | alacritty_terminal | 0.12+ | crates.io | Apache-2.0 | Battle-tested VTE implementation |
| Async Runtime | smol | 2.0 | crates.io | Apache-2.0 | Lightweight, used by Zed |
| Configuration | serde/serde_json | 1.0 | crates.io | MIT | Standard Rust serialization |
| Logging | tracing | 0.1 | crates.io | MIT | Structured logging |
| File Watching | notify | 6.0 | crates.io | MIT | Settings hot-reload |
Phase 2+ Dependencies (Zed Crates):
| Component | Zed Crate | Version | License | Phase |
|---|---|---|---|---|
| Terminal Core | terminal |
v0.220.3 | GPL-3.0 | Phase 2 |
| Settings | settings |
v0.220.3 | GPL-3.0 | Phase 2 |
| Theme | theme |
v0.220.3 | GPL-3.0 | Phase 2 |
| UI Components | ui |
v0.220.3 | GPL-3.0 | Phase 2 |
| Markdown | markdown |
v0.220.3 | GPL-3.0 | Phase 4 |
Note: Phase 2+ dependencies require TerminalG to be GPL-3.0 licensed. See docs/architecture/zed-reuse-strategy.md for details and future flexibility options.
GPUI Version Management:
- Use git dependency with tag pinning
- Pin to stable Zed releases (currently v0.220.3)
- Upgrade deliberately after testing
- See
docs/architecture/gpui-integration.mdfor details
Rust Version:
- Minimum: 1.93.0
- Target: Latest stable
Responsibilities:
- GPUI app initialization
- Settings loading and watching
- Theme management
- Global action registration
- Window lifecycle management
Key Patterns:
struct TerminalGApp {
settings: Arc<Settings>,
theme: Arc<Theme>,
}
impl TerminalGApp {
fn new(cx: &mut AppContext) -> Self {
// Load settings, register actions, initialize theme
}
fn open_workspace(&mut self, cx: &mut AppContext) {
cx.open_window(|cx| WorkspaceView::new(cx))
}
}Files: src/main.rs
Responsibilities:
- Workspace-level tab management (top tabs)
- Three-pane layout management
- Pane visibility controls (hide/show buttons)
- Terminal tab management (bottom of terminal pane)
- Document tab management (bottom of document viewer pane)
- Workspace configuration persistence
- Pane layout and resizing
- Status bar rendering
- Keyboard shortcuts
State:
struct WorkspaceView {
workspace_id: String,
active_workspace_tab: usize,
workspace_tabs: Vec<WorkspaceTab>,
// Pane visibility
file_browser_visible: bool,
terminal_visible: bool,
document_viewer_visible: bool,
// Pane sizes
pane_ratios: [f32; 3], // Ratios for 3 panes
// Terminal tabs
terminal_tabs: Vec<TerminalTab>,
active_terminal_tab: usize,
// Document tabs
document_tabs: Vec<DocumentTab>,
active_document_tab: usize,
// Workspace config
config: WorkspaceConfig,
}Files: src/ui/workspace.rs, src/ui/workspace_config.rs
Integration Strategy: Use Zed's terminal crate as a git dependency, write custom view.
Approach:
- Use Zed's
terminalcrate as git dependency (NOT copied/vendored) - Terminal crate wraps
alacritty_terminalwith PTY management - Write custom
TerminalPaneview for TerminalG's artifact-focused UI - Use Zed's
settingsandthemecrates (required by terminal crate) - Add URL recognition and clicking on top
See: docs/architecture/zed-reuse-strategy.md Section 3.5 for complete dependency strategy.
Responsibilities:
- PTY lifecycle management (all platforms: macOS, Linux, Windows)
- Terminal grid rendering (alacritty_terminal → GPUI)
- Input handling (keyboard, mouse → PTY)
- Output processing (PTY → terminal grid → render)
- Scrollback buffer management
- Copy/paste, selection
- Search in terminal output
- Shell integration
- Working directory tracking
- NEW: URL recognition and clicking
Critical Integration:
PTY Threading Model:
- Spawn smol task for PTY read loop
- Use mpsc channel to send output to GPUI thread
- Call
cx.notify()to trigger re-render
cx.spawn(|view, mut cx| async move {
let mut pty_reader = pty.reader();
loop {
let data = pty_reader.read().await?;
cx.update(|view, cx| {
view.terminal.process_bytes(&data);
cx.notify();
})?;
}
}).detach();Grid Rendering:
- Iterate alacritty_terminal grid cells
- Group consecutive cells with same style
- Render as styled GPUI
div()elements
Files: src/terminal/pane.rs, src/terminal/pty.rs
Details: See docs/architecture/gpui-integration.md Section 2.3
Responsibilities:
- Display file/folder tree (rooted at workspace folder)
- Handle directory expand/collapse
- File selection (open in document viewer)
- File operations (refresh, navigate)
- Sync with workspace configuration
State:
struct FileBrowserPane {
root_path: PathBuf,
tree: FileTree,
selected: Option<PathBuf>,
expanded_dirs: HashSet<PathBuf>,
}Files: src/ui/file_browser.rs
Responsibilities:
- Manage multiple document tabs (bottom tabs)
- Switch between viewer types based on file type
- Detect file types automatically
- Coordinate viewer lifecycle
- Integration with file browser (open file → new tab)
Viewer Types:
- MarkdownViewer: Parse and render markdown (Phase 4)
- MarkdownEditor: Edit markdown with preview (Phase 5)
- ImageViewer: Display images with zoom/pan
- HTMLViewer: (Future) Inline HTML rendering
State:
struct DocumentViewerPane {
tabs: Vec<DocumentTab>,
active_tab: usize,
}
struct DocumentTab {
path: PathBuf,
viewer_type: ViewerType,
viewer: Box<dyn Viewer>,
}Files: src/viewer/pane.rs, src/viewer/markdown.rs, src/viewer/image.rs, src/viewer/editor.rs
Responsibilities:
- Load/save workspace configuration
- Auto-save on state changes
- Manage workspace-specific settings
Workspace Configuration:
struct WorkspaceConfig {
workspace_id: String,
root_path: PathBuf,
// Pane visibility
file_browser_visible: bool,
terminal_visible: bool,
document_viewer_visible: bool,
// Pane sizes
pane_ratios: [f32; 3],
// Open terminals
terminal_tabs: Vec<TerminalTabConfig>,
// Open documents
document_tabs: Vec<DocumentTabConfig>,
// Active selections
active_terminal_tab: usize,
active_document_tab: usize,
}Storage:
- Workspace-local:
.terminalg/workspace.jsonor.terminalg/workspace-<name>.jsonin project repos - Workspace root is the folder containing
.terminalg/ - Loading/switching workspaces sets process working directory to the workspace root
- Repo-local config is user-decided for committing
Indexing:
- App settings track all available workspaces and their config paths for this machine
Files: src/ui/workspace_config.rs
settings.json (disk)
↓ load
SettingsStore (Rust struct)
↓ cx.set_global()
GPUI Global State
↓ cx.global::<Settings>()
Views (apply settings)
Hot Reload:
- File watcher detects settings.json change
- SettingsStore reloads from disk
- Update global state
- Call
cx.notify()on all views - Views apply new settings on next render
Schema Versioning:
- Settings include a schema version field
- On load, migrate older versions to the latest schema
- Unknown fields are ignored for forward compatibility
- Migration failures surface a user-facing error and fall back to defaults
Details: See docs/architecture/settings-system.md
Theme::by_name(settings.ui.theme)
↓
Theme struct (colors)
↓ cx.set_global()
GPUI Global State
↓ cx.global::<Theme>()
Views (apply theme colors)
Theme Switching:
- Update settings.json theme field
- Settings reload (see above)
- Load new theme by name
- Update global theme
- Re-render all views with new colors
Files: src/theme/mod.rs
User Keyboard Input
↓
GPUI Event Handler
↓
PTY Write (non-blocking)
↓
Shell Process
↓
PTY Read (async task)
↓
alacritty_terminal.process_bytes()
↓
Terminal Grid Update
↓
cx.notify() → GPUI Re-render
↓
TerminalPane.render() → Grid → GPUI Elements
Details: See docs/architecture/gpui-integration.md Section 2.2
src/
├── main.rs # App entry point, TerminalGApp
├── settings/
│ ├── mod.rs # SettingsStore, load/save/reload
│ ├── terminal.rs # TerminalSettings struct
│ └── ui.rs # UiSettings struct
├── theme/
│ └── mod.rs # Color, Theme, built-in themes
├── terminal/
│ ├── mod.rs # Terminal component
│ ├── pane.rs # TerminalPane (GPUI view)
│ ├── pty.rs # PTY management
│ └── grid_renderer.rs # alacritty → GPUI rendering
├── ui/
│ ├── mod.rs # UI module exports
│ ├── workspace.rs # WorkspaceView
│ ├── tab_bar.rs # Tab management UI
│ ├── file_browser.rs # FileBrowserPane
│ └── status_bar.rs # Status bar component
└── viewer/
├── mod.rs # Viewer module exports
├── pane.rs # ViewerPane (switches viewers)
├── markdown.rs # MarkdownViewer
└── image.rs # ImageViewer
Stored in GPUI's global context, accessible from any view:
cx.set_global(settings); // Store
let settings = cx.global::<SettingsStore>(); // RetrieveGlobal State:
SettingsStore- Application settingsTheme- Current theme colors
Each view manages its own state:
WorkspaceView:
- Active tab index
- Tab list
- Pane sizes
TerminalPane:
- PTY handle
- Terminal grid (alacritty_terminal::Term)
- Scrollback position
ViewerPane:
- Active viewer type
- Current file path
- Viewer-specific state (zoom level, scroll position)
Terminal:
div().on_key_down(cx.listener(|this, event, cx| {
this.handle_key_input(event, cx);
}))Global Shortcuts:
- Registered in TerminalGApp
- Handled before view-specific handlers
- Examples: Cmd+T (new tab), Cmd+W (close tab)
Clickable URLs:
- Detect URLs in terminal output (regex)
- Store URL → screen region mapping
- Handle click events on regions
- Open URL in browser or copy to clipboard
Pane Resizing:
- Render drag handle between panes
- Handle mouse down/move/up events
- Update pane size state
- Persist to settings
Runs:
- All GPUI rendering
- Event handling
- View updates
- UI logic
Must not block: Keep operations fast (<16ms for 60 FPS)
PTY Reading:
- Spawned as smol task
- Reads from PTY asynchronously
- Sends data to GPUI thread via channel
File Watching:
- Spawned as smol task
- Watches settings.json for changes
- Triggers reload on GPUI thread
File Operations:
- Directory scanning (file browser)
- File loading (markdown, images)
Pattern:
cx.spawn(|view, mut cx| async move {
let result = expensive_operation().await;
cx.update(|view, cx| {
view.apply_result(result);
cx.notify();
})
}).detach();User-Facing Errors:
- Display in UI (status bar, modal, inline)
- Log with
tracing::error!() - Provide actionable messages
Internal Errors:
- Log with
tracing::warn!()ortracing::error!() - Graceful degradation where possible
- Fail safely (don't crash UI)
PTY Failure:
- Show error in terminal pane
- Allow user to retry or close tab
Settings Load Failure:
- Use defaults
- Warn user in UI
- Continue operation
File Load Failure:
- Show error in viewer pane
- Provide option to retry or select different file
Optimization:
- Dirty region tracking (only re-render changed cells)
- Batch rendering (group cells with same style)
- Texture caching where possible (GPUI)
Target:
- Handle 100k lines of scrollback
- Render at 60 FPS even with rapid output
Optimization:
- Lazy loading (only scan visible directories)
- Virtual scrolling for large directories
- Cache directory contents
Markdown:
- Parse once, cache result
- Re-parse only on file change
Images:
- Load and decode off-thread
- Cache decoded image data
- Implement progressive loading for large images
PTY: POSIX via Zed terminal (openpty, fork, execv)
Config Path: ~/Library/Application Support/terminalg/
Shell: Default to $SHELL or /bin/zsh
PTY: POSIX via Zed terminal (same as macOS)
Config Path: ~/.config/terminalg/
Shell: Default to $SHELL or /bin/bash
Display: Support X11 and Wayland (GPUI handles)
PTY: ConPTY API via Zed terminal (Windows 10+)
Config Path: %APPDATA%\terminalg\
Shell: Default to PowerShell or cmd.exe
Note: Phase 1 validates the bootstrap app window on macOS, Linux, and Windows. Full cross-platform terminal functionality arrives in Phase 2 after Zed terminal integration. Primary development and testing on macOS, CI/testing on all platforms.
- Do not log sensitive terminal output
- Respect terminal privacy modes
- Do not persist terminal history by default
- Validate file paths (prevent directory traversal)
- Respect file permissions
- Handle symlinks safely
- Validate settings on load (schema validation)
- Use safe defaults for invalid values
- Sanitize user-provided paths
- Settings serialization/deserialization
- Theme color calculations
- URL regex matching
- File path validation
- PTY creation and communication
- Terminal grid rendering
- Settings load/save/reload
- File browser directory scanning
- Full UI workflows
- Cross-platform compatibility
- Performance under load
- Edge cases (large files, rapid output)
# Development
cargo build
# Release (optimized)
cargo build --releasemacOS:
- Create app bundle (.app)
- Sign and notarize (future)
- DMG distribution (future)
Linux:
- Distribute binary + .desktop file
- Package for distributions (deb, rpm) (future)
Windows:
- MSI installer (future)
Levels:
TRACE- Detailed execution flowDEBUG- Debugging informationINFO- General informational messagesWARN- Unexpected but handled situationsERROR- Error conditions
Usage:
RUST_LOG=info cargo run
RUST_LOG=terminalg=debug cargo run- Use GPUI's built-in profiler (when available)
cargo flamegraphfor CPU profiling- Memory profiling with
valgrind(Linux)
- Leverage Zed's Patterns - Use proven approaches from Zed codebase
- Performance First - GPU acceleration, efficient rendering
- User Control - Configurable, keyboard-driven
- Fail Gracefully - Never crash, always provide feedback
- Platform Native - Respect platform conventions
- Minimal Dependencies - Only add what's necessary
- Clear Separation - Modular components, clear boundaries
- Define plugin API boundary
- Sandboxed plugin execution
- Plugin discovery and loading
- Protocol definition
- Message routing between components
- Structured data parsing from terminal output
- Connect to remote terminal sessions
- Sync state across machines
- Collaborative features
- Zed Reuse Strategy:
docs/architecture/zed-reuse-strategy.md- Source of truth for Zed crate dependencies - GPUI Integration:
docs/architecture/gpui-integration.md - Settings System:
docs/architecture/settings-system.md(Phase 1 only - see zed-reuse-strategy.md for Phase 2 migration)
- GPUI Source: https://github.com/zed-industries/zed/tree/v0.220.3/crates/gpui
- Zed Terminal: https://github.com/zed-industries/zed/tree/v0.220.3/crates/terminal
- alacritty_terminal: https://docs.rs/alacritty_terminal
- Zed Architecture: Study Zed's workspace and terminal implementations
- Requirements:
docs/REQUIREMENTS.md - Master Plan:
docs/MASTER-PLAN.md
Document Status: ✅ Approved Next Review: After Phase 2 complete (update with terminal architecture lessons)