|
| 1 | +# TUI Alternate Screen and Terminal Multiplexers |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document explains the design decision behind Codex's alternate screen handling, particularly in terminal multiplexers like Zellij. This addresses a fundamental conflict between fullscreen TUI behavior and terminal scrollback history preservation. |
| 6 | + |
| 7 | +## The Problem |
| 8 | + |
| 9 | +### Fullscreen TUI Benefits |
| 10 | + |
| 11 | +Codex's TUI uses the terminal's **alternate screen buffer** to provide a clean fullscreen experience. This approach: |
| 12 | +- Uses the entire viewport without polluting the terminal's scrollback history |
| 13 | +- Provides a dedicated environment for the chat interface |
| 14 | +- Mirrors the behavior of other terminal applications (vim, tmux, etc.) |
| 15 | + |
| 16 | +### The Zellij Conflict |
| 17 | + |
| 18 | +Terminal multiplexers like **Zellij** strictly follow the xterm specification, which defines that alternate screen buffers should **not** have scrollback. This is intentional design, not a bug: |
| 19 | + |
| 20 | +- **Zellij PR:** https://github.com/zellij-org/zellij/pull/1032 |
| 21 | +- **Rationale:** The xterm spec explicitly states that alternate screen mode disallows scrollback |
| 22 | +- **Configurability:** This is not configurable in Zellij—there is no option to enable scrollback in alternate screen mode |
| 23 | + |
| 24 | +When using Codex's TUI in Zellij, users cannot scroll back through the conversation history because: |
| 25 | +1. The TUI runs in alternate screen mode (fullscreen) |
| 26 | +2. Zellij disables scrollback in alternate screen buffers (per xterm spec) |
| 27 | +3. The entire conversation becomes inaccessible via normal terminal scrolling |
| 28 | + |
| 29 | +## The Solution |
| 30 | + |
| 31 | +Codex implements a **pragmatic workaround** with three modes, controlled by `tui.alternate_screen` in `config.toml`: |
| 32 | + |
| 33 | +### 1. `auto` (default) |
| 34 | +- **Behavior:** Automatically detect the terminal multiplexer |
| 35 | +- **In Zellij:** Disable alternate screen mode (inline mode, preserves scrollback) |
| 36 | +- **Elsewhere:** Enable alternate screen mode (fullscreen experience) |
| 37 | +- **Rationale:** Provides the best UX in each environment |
| 38 | + |
| 39 | +### 2. `always` |
| 40 | +- **Behavior:** Always use alternate screen mode (original behavior) |
| 41 | +- **Use case:** Users who prefer fullscreen and don't use Zellij, or who have found a workaround |
| 42 | + |
| 43 | +### 3. `never` |
| 44 | +- **Behavior:** Never use alternate screen mode (inline mode) |
| 45 | +- **Use case:** Users who always want scrollback history preserved |
| 46 | +- **Trade-off:** Pollutes the terminal scrollback with TUI output |
| 47 | + |
| 48 | +## Runtime Override |
| 49 | + |
| 50 | +The `--no-alt-screen` CLI flag can override the config setting at runtime: |
| 51 | + |
| 52 | +```bash |
| 53 | +codex --no-alt-screen |
| 54 | +``` |
| 55 | + |
| 56 | +This runs the TUI in inline mode regardless of the configuration, useful for: |
| 57 | +- One-off sessions where scrollback is critical |
| 58 | +- Debugging terminal-related issues |
| 59 | +- Testing alternate screen behavior |
| 60 | + |
| 61 | +## Implementation Details |
| 62 | + |
| 63 | +### Auto-Detection |
| 64 | + |
| 65 | +The `auto` mode detects Zellij by checking the `ZELLIJ` environment variable: |
| 66 | + |
| 67 | +```rust |
| 68 | +let terminal_info = codex_core::terminal::terminal_info(); |
| 69 | +!matches!(terminal_info.multiplexer, Some(Multiplexer::Zellij { .. })) |
| 70 | +``` |
| 71 | + |
| 72 | +This detection happens in the helper function `determine_alt_screen_mode()` in `codex-rs/tui/src/lib.rs`. |
| 73 | + |
| 74 | +### Configuration Schema |
| 75 | + |
| 76 | +The `AltScreenMode` enum is defined in `codex-rs/protocol/src/config_types.rs` and serializes to lowercase TOML: |
| 77 | + |
| 78 | +```toml |
| 79 | +[tui] |
| 80 | +# Options: auto, always, never |
| 81 | +alternate_screen = "auto" |
| 82 | +``` |
| 83 | + |
| 84 | +### Why Not Just Disable Alternate Screen in Zellij Permanently? |
| 85 | + |
| 86 | +We use `auto` detection instead of always disabling in Zellij because: |
| 87 | +1. Many Zellij users don't care about scrollback and prefer the fullscreen experience |
| 88 | +2. Some users may use tmux inside Zellij, creating a chain of multiplexers |
| 89 | +3. Provides user choice without requiring manual configuration |
| 90 | + |
| 91 | +## Related Issues and References |
| 92 | + |
| 93 | +- **Original Issue:** [GitHub #2558](https://github.com/openai/codex/issues/2558) - "No scrollback in Zellij" |
| 94 | +- **Implementation PR:** [GitHub #8555](https://github.com/openai/codex/pull/8555) |
| 95 | +- **Zellij PR:** https://github.com/zellij-org/zellij/pull/1032 (why scrollback is disabled) |
| 96 | +- **xterm Spec:** Alternate screen buffers should not have scrollback |
| 97 | + |
| 98 | +## Future Considerations |
| 99 | + |
| 100 | +### Alternative Approaches Considered |
| 101 | + |
| 102 | +1. **Implement custom scrollback in TUI:** Would require significant architectural changes to buffer and render all historical output |
| 103 | +2. **Request Zellij to add a config option:** Not viable—Zellij maintainers explicitly chose this behavior to follow the spec |
| 104 | +3. **Disable alternate screen unconditionally:** Would degrade UX for non-Zellij users |
| 105 | + |
| 106 | +### Transcript Pager |
| 107 | + |
| 108 | +Codex's transcript pager (opened with Ctrl+T) provides an alternative way to review conversation history, even in fullscreen mode. However, this is not as seamless as natural scrollback. |
| 109 | + |
| 110 | +## For Developers |
| 111 | + |
| 112 | +When modifying TUI code, remember: |
| 113 | +- The `determine_alt_screen_mode()` function encapsulates all the logic |
| 114 | +- Configuration is in `config.tui_alternate_screen` |
| 115 | +- CLI flag is in `cli.no_alt_screen` |
| 116 | +- The behavior is applied via `tui.set_alt_screen_enabled()` |
| 117 | + |
| 118 | +If you encounter issues with terminal state after running Codex, you can restore your terminal with: |
| 119 | +```bash |
| 120 | +reset |
| 121 | +``` |
0 commit comments