Skip to main content

Content Panes

The workspace content area is populated by content panes, each responsible for rendering a specific type of view. WorkspaceView orchestrates which pane is visible based on the active session’s kind field or whether it’s a terminal/agent session.

TerminalPane

File: src/components/TerminalPane.tsx TerminalPane renders an interactive terminal using xterm.js and manages the PTY session lifecycle. It’s the most complex pane due to WebGL context pooling, resize handling, and keyboard interception.

Props

xterm.js Extensions

TerminalPane loads several xterm.js addons:

Theme

Terminal colors are pulled from CSS custom properties (—tempest-terminal-*) and injected as an xterm.js theme object:
On theme change, the theme object is re-injected without recreating the terminal:

PTY Attachment

On mount, TerminalPane:
  1. Creates the xterm.js Terminal instance
  2. Calls sessionManager.attach(sessionId, onData) to subscribe to PTY output
  3. Replays any buffered data (from before the pane mounted)
  4. Registers an onData handler to write incoming bytes to the terminal
The SessionManager owns the Channel subscription and manages the replay buffer, so TerminalPane is just a thin renderer.

Keyboard Handling

Custom key event handler intercepts specific keys:

Resize Handling

On every resize event, the terminal is re-fitted and the PTY is notified of the new dimensions:
Debouncing with setTimeout(..., 16) prevents excessive PTY resize calls during animated resizes.

WebGL Context Pooling

xterm.js can render with WebGL for performance, but WebGL contexts are limited (6 per OS). TerminalPane manages this via webglPool:
When a tab is hidden (not active, not in split layout), its context is released. When shown, it’s re-acquired. This prevents context exhaustion and allows multiple terminals to be open (up to 6 visible at once). Ctrl+F opens an in-terminal search bar powered by xterm.js’s SearchAddon:
Enter/Shift+Enter navigate next/prev matches. Escape closes the bar.

User Input Tracking

When the user types (PTY onData fires), TerminalPane signals the SessionManager:
This arm/reset mechanism is critical for accurate work-done detection in agent sessions.

Title Change Monitoring

For agent sessions, TerminalPane monitors OSC 0/2 title changes (agents send status glyphs):

DiffPane

File: src/components/DiffPane.tsx DiffPane provides a full Git UI: staging/unstaging files, viewing diffs, committing, pushing, and branch management. It calls Rust backend commands for all git operations.

Props

Data Model

Layout

Two-column layout: files on left, diff on right. Left column:
  • Staged files section (with “Unstage All” button)
  • Unstaged files section (with “Stage All” button)
  • Commit form (title + description + co-author toggle + commit button)
Right column:
  • Selected file’s unified diff with line numbers
  • Header showing file path and section (staged/unstaged)
  • Loading state during diff fetch

Loading File List

On mount and whenever gitRevision changes, load the repo status:
The .tempest-pid file (used internally) is filtered out.

Staging Operations

Per-File Diff Rendering

Unified diff format: each line is colored by kind (added/removed/context/hunk header). Line numbers are shown for old and new versions.

Committing

Commit is disabled if no staged files or missing title. Co-author toggle subscribes to the Attribution store.

Branch Operations

Branch menu: Click the pill showing current branch to open a dropdown menu:
  • List all branches
  • Switch branch (disabled for current)
  • Delete branch button (shows trash icon for non-current)
Push operations:
PR Link Builder: Detects host (GitHub/GitLab/Bitbucket) and opens the appropriate PR creation URL:

Two-Way Sync

When a file is selected and staged/unstaged (via button), the pane automatically re-loads and follows the file to its new section. This keeps the UI in sync with the git state.

CodeMirrorPane

File: src/components/CodeMirrorPane.tsx CodeMirrorPane provides a read/edit interface for files with syntax highlighting. It doubles as a Markdown previewer.

Props

Supported Languages

Uses CodeMirror language extensions from @codemirror/lang-* packages.

Editor Setup

Theme

Both editor colors and syntax highlighting use CSS custom properties (—tempest-*):
On theme change, compartments reconfigure without recreating the editor:

Saving

Ctrl+S writes the file:
A dot appears next to the filename while unsaved.

Markdown Preview

For .md and .markdown files, a toggle appears to switch between “Raw” (editor) and “Preview” modes:
Preview uses ReactMarkdown with remark-gfm (tables, strikethrough, etc.) and rehype-highlight for code block syntax highlighting. GitHub markdown CSS is injected based on theme (dark or light) as a raw inline <style> tag:
This ensures GitHub’s .markdown-body CSS rules don’t conflict between light and dark instances.

Image Resolution

Relative image paths in Markdown are resolved relative to the file’s directory:

PreviewPane

File: src/components/PreviewPane.tsx PreviewPane embeds a native webview (via Tauri) to display a live dev server preview. It handles URL navigation, history, viewport presets (mobile/tablet/desktop), and bounds synchronization.

Props

Initialization

First time (no URL set), show the UrlPicker:
Normalizes input (port number auto-prefixed with http://localhost:) and triggers navigation.

Webview Embedding

Once a URL is set, invoke Tauri to embed a native webview:
The webview is anchored over a transparent div (preview-content). Bounds are computed from the div’s getBoundingClientRect().

Bounds Synchronization

On resize/scroll, update webview bounds:
Watched by ResizeObserver and window resize/scroll listeners. Similar to a browser, maintain a history stack:
Back/forward buttons are disabled when at the beginning/end.

Viewport Presets

Three presets and free-form dimensions:
Clicking a preset button sets viewW and viewH. Clicking a dimension value opens an inline editor. Setting to null or “auto” returns to full-width/full-height. The frame div is positioned with constrained dimensions:

URL Polling

Every 500ms, poll the webview to detect SPA navigation (pushState):
This allows navigation within SPAs to update the URL bar without reloading the frame.

Reload vs. Navigation

  • Reload: Same URL, increment reloadKey to force re-embed
  • Navigate: Different URL, call pushEntry() to add to history and re-embed
  • Same-URL reload from URL bar: Detected by sameUrl(), triggers reload

TopBar

File: src/components/TopBar.tsx TopBar is the minimal window chrome at the very top of the app. It includes the Tempest mark, window controls (minimize/maximize/close), and a disabled zen-mode button.

Components

Uses Tauri’s getCurrentWindow() to call native window methods. The data-tauri-drag-region attribute on the drag div tells Tauri that clicks on it are meant to drag the window (not interact with content). Zen mode (future feature) would switch the app to focus on a single project with a flat worktree view and hidden sidebars. The button is disabled for now.