Skip to content

Commit b079102

Browse files
committed
test: include alternate_screen in tui default
1 parent 63630ab commit b079102

File tree

6 files changed

+189
-22
lines changed

6 files changed

+189
-22
lines changed

codex-rs/core/src/config/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,7 @@ persistence = "none"
16321632
scroll_wheel_tick_detect_max_ms: None,
16331633
scroll_wheel_like_max_duration_ms: None,
16341634
scroll_invert: false,
1635+
alternate_screen: AltScreenMode::Auto,
16351636
}
16361637
);
16371638
}

codex-rs/protocol/src/config_types.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,27 @@ pub enum TrustLevel {
8383

8484
/// Controls whether the TUI uses the terminal's alternate screen buffer.
8585
///
86-
/// Using alternate screen provides a cleaner fullscreen experience but prevents
87-
/// scrollback in terminal multiplexers like Zellij that follow the xterm spec
88-
/// (which says alternate screen should not have scrollback).
89-
#[derive(Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
86+
/// **Background:** The alternate screen buffer provides a cleaner fullscreen experience
87+
/// without polluting the terminal's scrollback history. However, it conflicts with terminal
88+
/// multiplexers like Zellij that strictly follow the xterm specification, which defines
89+
/// that alternate screen buffers should not have scrollback.
90+
///
91+
/// **Zellij's behavior:** Zellij intentionally disables scrollback in alternate screen mode
92+
/// (see https://github.com/zellij-org/zellij/pull/1032) to comply with the xterm spec. This
93+
/// is by design and not configurable in Zellij—there is no option to enable scrollback in
94+
/// alternate screen mode.
95+
///
96+
/// **Solution:** This setting provides a pragmatic workaround:
97+
/// - `auto` (default): Automatically detect the terminal multiplexer. If running in Zellij,
98+
/// disable alternate screen to preserve scrollback. Enable it everywhere else.
99+
/// - `always`: Always use alternate screen mode (original behavior before this fix).
100+
/// - `never`: Never use alternate screen mode. Runs in inline mode, preserving scrollback
101+
/// in all multiplexers.
102+
///
103+
/// The CLI flag `--no-alt-screen` can override this setting at runtime.
104+
#[derive(
105+
Debug, Serialize, Deserialize, Default, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS,
106+
)]
90107
#[serde(rename_all = "lowercase")]
91108
#[strum(serialize_all = "lowercase")]
92109
pub enum AltScreenMode {

codex-rs/tui/src/cli.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ pub struct Cli {
8585
#[arg(long = "add-dir", value_name = "DIR", value_hint = ValueHint::DirPath)]
8686
pub add_dir: Vec<PathBuf>,
8787

88-
/// Disable alternate screen mode for better scrollback in terminal multiplexers like Zellij.
89-
/// This runs the TUI in inline mode, preserving terminal scrollback history.
88+
/// Disable alternate screen mode
89+
///
90+
/// Runs the TUI in inline mode, preserving terminal scrollback history. This is useful
91+
/// in terminal multiplexers like Zellij that follow the xterm spec strictly and disable
92+
/// scrollback in alternate screen buffers.
9093
#[arg(long = "no-alt-screen", default_value_t = false)]
9194
pub no_alt_screen: bool,
9295

codex-rs/tui/src/lib.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -502,19 +502,7 @@ async fn run_ratatui_app(
502502
..
503503
} = cli;
504504

505-
// Determine whether to use alternate screen based on CLI flag and config.
506-
let use_alt_screen = if no_alt_screen {
507-
false
508-
} else {
509-
match config.tui_alternate_screen {
510-
AltScreenMode::Always => true,
511-
AltScreenMode::Never => false,
512-
AltScreenMode::Auto => {
513-
let terminal_info = codex_core::terminal::terminal_info();
514-
!matches!(terminal_info.multiplexer, Some(Multiplexer::Zellij { .. }))
515-
}
516-
}
517-
};
505+
let use_alt_screen = determine_alt_screen_mode(no_alt_screen, config.tui_alternate_screen);
518506
tui.set_alt_screen_enabled(use_alt_screen);
519507

520508
let app_result = App::run(
@@ -549,6 +537,37 @@ fn restore() {
549537
}
550538
}
551539

540+
/// Determine whether to use the terminal's alternate screen buffer.
541+
///
542+
/// The alternate screen buffer provides a cleaner fullscreen experience without polluting
543+
/// the terminal's scrollback history. However, it conflicts with terminal multiplexers like
544+
/// Zellij that strictly follow the xterm spec, which disallows scrollback in alternate screen
545+
/// buffers. Zellij intentionally disables scrollback in alternate screen mode (see
546+
/// https://github.com/zellij-org/zellij/pull/1032) and offers no configuration option to
547+
/// change this behavior.
548+
///
549+
/// This function implements a pragmatic workaround:
550+
/// - If `--no-alt-screen` is explicitly passed, always disable alternate screen
551+
/// - Otherwise, respect the `tui.alternate_screen` config setting:
552+
/// - `always`: Use alternate screen everywhere (original behavior)
553+
/// - `never`: Inline mode only, preserves scrollback
554+
/// - `auto` (default): Auto-detect the terminal multiplexer and disable alternate screen
555+
/// only in Zellij, enabling it everywhere else
556+
fn determine_alt_screen_mode(no_alt_screen: bool, tui_alternate_screen: AltScreenMode) -> bool {
557+
if no_alt_screen {
558+
false
559+
} else {
560+
match tui_alternate_screen {
561+
AltScreenMode::Always => true,
562+
AltScreenMode::Never => false,
563+
AltScreenMode::Auto => {
564+
let terminal_info = codex_core::terminal::terminal_info();
565+
!matches!(terminal_info.multiplexer, Some(Multiplexer::Zellij { .. }))
566+
}
567+
}
568+
}
569+
}
570+
552571
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
553572
pub enum LoginStatus {
554573
AuthMode(AuthMode),

codex-rs/tui2/src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,9 +524,15 @@ async fn run_ratatui_app(
524524
..
525525
} = cli;
526526

527-
// Determine whether to use alternate screen based on CLI flag and config.
528-
// Alternate screen provides a cleaner fullscreen experience but prevents
529-
// scrollback in terminal multiplexers like Zellij that follow xterm spec.
527+
// Run the main chat + transcript UI on the terminal's alternate screen so
528+
// the entire viewport can be used without polluting normal scrollback. This
529+
// mirrors the behavior of the legacy TUI but keeps inline mode available
530+
// for smaller prompts like onboarding and model migration.
531+
//
532+
// However, alternate screen prevents scrollback in terminal multiplexers like
533+
// Zellij that strictly follow the xterm spec (which disallows scrollback in
534+
// alternate screen buffers). This auto-detects the terminal and disables
535+
// alternate screen in Zellij while keeping it enabled elsewhere.
530536
let use_alt_screen = if no_alt_screen {
531537
// CLI flag explicitly disables alternate screen
532538
false

docs/tui-alternate-screen.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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

Comments
 (0)