mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Move fullscreen transcript control to TUI configuration (#46849)
## What changed Use `tui.fullscreen_transcript` to opt into the fullscreen transcript, including scrolling, selection, and search. It defaults to `false`; `--no-alt-screen` and `tui.alternate_screen = "never"` still take precedence. The new preference controls both the first frame and application startup. Boolean CLI overrides for it are allowed during daemon startup. The old `features.transcript_v2` flag is deprecated and ignored, with a notice directing users to the new setting; it does not migrate into or override the preference. ## Testing Add coverage for default terminal scrollback, fullscreen persistence through startup, alternate-screen restrictions, daemon override validation, and independence from the deprecated flag. Update existing history and activity tests to use the new preference. GitOrigin-RevId: 4117bf1ee548fa8d101a594f9506673d87c74c15
This commit is contained in:
@@ -133,7 +133,7 @@ fn strict_config_is_not_supported_for_cloud_command() -> Result<()> {
|
||||
async fn features_enable_writes_feature_flag_to_config() -> Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
for feature in ["unified_exec", "transcript_v2"] {
|
||||
for feature in ["unified_exec", "shell_tool"] {
|
||||
let mut cmd = codex_command(codex_home.path())?;
|
||||
cmd.args(["features", "enable", feature])
|
||||
.assert()
|
||||
|
||||
@@ -793,6 +793,11 @@ pub struct Tui {
|
||||
#[serde(default)]
|
||||
pub raw_output_mode: bool,
|
||||
|
||||
/// Own the fullscreen transcript, including scrolling, selection, and search.
|
||||
/// Defaults to `false`; alternate-screen restrictions take precedence.
|
||||
#[serde(default)]
|
||||
pub fullscreen_transcript: bool,
|
||||
|
||||
/// Controls whether the TUI uses the terminal's alternate screen buffer.
|
||||
///
|
||||
/// - `auto` (default): Use alternate screen.
|
||||
|
||||
@@ -4226,6 +4226,11 @@
|
||||
},
|
||||
"description": "Individual visual effects. Each also requires animations to be enabled."
|
||||
},
|
||||
"fullscreen_transcript": {
|
||||
"default": false,
|
||||
"description": "Own the fullscreen transcript, including scrolling, selection, and search. Defaults to `false`; alternate-screen restrictions take precedence.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"keymap": {
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -1261,6 +1261,7 @@ fn config_toml_deserializes_model_availability_nux() {
|
||||
vim_mode_default: false,
|
||||
question_esc_back: true,
|
||||
raw_output_mode: false,
|
||||
fullscreen_transcript: false,
|
||||
alternate_screen: AltScreenMode::default(),
|
||||
status_line: None,
|
||||
status_line_use_colors: true,
|
||||
@@ -4388,6 +4389,7 @@ fn tui_config_missing_notifications_field_defaults_to_enabled() {
|
||||
vim_mode_default: false,
|
||||
question_esc_back: true,
|
||||
raw_output_mode: false,
|
||||
fullscreen_transcript: false,
|
||||
alternate_screen: AltScreenMode::Auto,
|
||||
status_line: None,
|
||||
status_line_use_colors: true,
|
||||
|
||||
@@ -770,6 +770,9 @@ pub struct Config {
|
||||
/// Start the TUI in raw scrollback mode for copy-friendly transcript output.
|
||||
pub tui_raw_output_mode: bool,
|
||||
|
||||
/// Own the fullscreen transcript when the alternate screen is enabled.
|
||||
pub tui_fullscreen_transcript: bool,
|
||||
|
||||
/// Start the TUI in the specified collaboration mode (plan/default).
|
||||
|
||||
/// Controls whether the TUI uses the terminal's alternate screen buffer.
|
||||
@@ -4406,6 +4409,10 @@ impl Config {
|
||||
.as_ref()
|
||||
.map(|t| t.raw_output_mode)
|
||||
.unwrap_or(false),
|
||||
tui_fullscreen_transcript: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
.is_some_and(|tui| tui.fullscreen_transcript),
|
||||
tui_alternate_screen: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
|
||||
@@ -95,7 +95,7 @@ pub enum Feature {
|
||||
AnalyticsPlanHistory,
|
||||
/// Discover model catalogs for OpenAI API-key authentication.
|
||||
ApiKeyModelDiscovery,
|
||||
/// Enable the interactive transcript composer and turn-selection UI.
|
||||
/// Deprecated no-op; use `tui.fullscreen_transcript` instead.
|
||||
TranscriptV2,
|
||||
// Stable.
|
||||
/// Enable the default shell tool.
|
||||
@@ -592,6 +592,10 @@ impl Features {
|
||||
Feature::WebSearchCached,
|
||||
);
|
||||
}
|
||||
"transcript_v2" => {
|
||||
self.record_legacy_usage_force("features.transcript_v2", Feature::TranscriptV2);
|
||||
continue;
|
||||
}
|
||||
"tui_app_server" => {
|
||||
continue;
|
||||
}
|
||||
@@ -696,6 +700,10 @@ impl Features {
|
||||
fn legacy_usage_notice(alias: &str, feature: Feature) -> (String, Option<String>) {
|
||||
let canonical = feature.key();
|
||||
match feature {
|
||||
Feature::TranscriptV2 => (
|
||||
"`[features].transcript_v2` is deprecated and ignored.".to_string(),
|
||||
Some("Use `[tui].fullscreen_transcript` in config.toml instead.".to_string()),
|
||||
),
|
||||
Feature::WebSearchRequest | Feature::WebSearchCached => {
|
||||
let label = match alias {
|
||||
"web_search" => "[features].web_search",
|
||||
@@ -937,7 +945,7 @@ pub const FEATURES: &[FeatureSpec] = &[
|
||||
FeatureSpec {
|
||||
id: Feature::TranscriptV2,
|
||||
key: "transcript_v2",
|
||||
stage: Stage::UnderDevelopment,
|
||||
stage: Stage::Deprecated,
|
||||
default_enabled: false,
|
||||
},
|
||||
// Stable features.
|
||||
|
||||
@@ -14,15 +14,6 @@ use std::collections::BTreeMap;
|
||||
use toml::Table;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
#[test]
|
||||
fn transcript_v2_resolves_explicit_config_overrides() {
|
||||
let mut features = Features::with_defaults();
|
||||
for enabled in [false, true, false] {
|
||||
features.apply_map(&BTreeMap::from([("transcript_v2".to_string(), enabled)]));
|
||||
assert_eq!(features.enabled(Feature::TranscriptV2), enabled);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sleep_tool_config_rejects_unknown_mode() {
|
||||
assert!(toml::from_str::<FeaturesToml>("[sleep_tool]\nmode = 'off'").is_err());
|
||||
|
||||
@@ -232,6 +232,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
||||
tui_show_server_version_notice: true,
|
||||
tui_auto_recap: true,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
tui_fullscreen_transcript: false,
|
||||
tui_alternate_screen: AltScreenMode::Auto,
|
||||
tui_status_line: None,
|
||||
tui_status_line_use_colors: true,
|
||||
|
||||
@@ -194,7 +194,7 @@ impl App {
|
||||
}
|
||||
|
||||
// Adopt actual launch ownership before constructing session-local preferences.
|
||||
tui.prepare_owned_screen(config.features.enabled(Feature::TranscriptV2))?;
|
||||
tui.prepare_owned_screen(config.tui_fullscreen_transcript)?;
|
||||
let mut local_settings = crate::local_settings::LocalSettings::for_tui(&config, tui);
|
||||
let startup_started_at = Instant::now();
|
||||
let (app_event_tx, mut app_event_rx) = unbounded_channel();
|
||||
|
||||
@@ -247,9 +247,7 @@ async fn history_hydration_archived_retry_uses_first_attempt_runtime_settings()
|
||||
] {
|
||||
for mode in [TranscriptMode::Terminal, TranscriptMode::Owned] {
|
||||
let (mut app, codex_home, mut target) = history_fixture(&[500]).await?;
|
||||
app.config
|
||||
.features
|
||||
.set_enabled(Feature::TranscriptV2, !mode.is_owned())?;
|
||||
app.config.tui_fullscreen_transcript = !mode.is_owned();
|
||||
app.config.tui_alternate_screen = codex_config::types::AltScreenMode::Always;
|
||||
app.config.terminal_resize_reflow.max_rows = TerminalResizeReflowMaxRows::Limit(1);
|
||||
app.local_settings = crate::local_settings::LocalSettings::from(&app.config);
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::legacy_core::config::TerminalResizeReflowMaxRows;
|
||||
use crate::local_settings::LocalSettings;
|
||||
use codex_config::types::AltScreenMode;
|
||||
use codex_features::Feature;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashSet;
|
||||
|
||||
@@ -42,10 +41,7 @@ async fn owned_initial_history_stops_after_viewport_or_scan_budget() {
|
||||
.build()
|
||||
.await
|
||||
.expect("config");
|
||||
config
|
||||
.features
|
||||
.enable(Feature::TranscriptV2)
|
||||
.expect("enable owned transcript");
|
||||
config.tui_fullscreen_transcript = true;
|
||||
config.tui_alternate_screen = AltScreenMode::Always;
|
||||
config.terminal_resize_reflow.max_rows = TerminalResizeReflowMaxRows::Disabled;
|
||||
let local_settings = LocalSettings::from(&config);
|
||||
@@ -78,10 +74,7 @@ async fn owned_initial_history_stops_after_viewport_or_scan_budget() {
|
||||
Some(100),
|
||||
);
|
||||
|
||||
config
|
||||
.features
|
||||
.disable(Feature::TranscriptV2)
|
||||
.expect("disable owned transcript");
|
||||
config.tui_fullscreen_transcript = false;
|
||||
// The mode selected at launch wins even if thread config changes or is unavailable.
|
||||
for config in [Some(&config), None] {
|
||||
let budget = HistoryLoadBudget::new(
|
||||
|
||||
@@ -37,10 +37,7 @@ async fn terminal_dynamic_activity_retains_calls_across_both_fallbacks() {
|
||||
[(false, AltScreenMode::Always), (true, AltScreenMode::Never)]
|
||||
{
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.config
|
||||
.features
|
||||
.set_enabled(Feature::TranscriptV2, owned_enabled)
|
||||
.expect("configure transcript feature");
|
||||
chat.config.tui_fullscreen_transcript = owned_enabled;
|
||||
chat.config.tui_alternate_screen = alternate_screen;
|
||||
chat.local_settings = LocalSettings::from(&chat.config);
|
||||
assert_eq!(
|
||||
@@ -89,10 +86,7 @@ async fn owned_dynamic_activity_updates_the_retained_row_after_config_refresh()
|
||||
let (mut chat, mut rx, _ops) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.local_settings.transcript_mode = TranscriptMode::Owned;
|
||||
// A thread/config refresh cannot change ownership after the terminal starts.
|
||||
chat.config
|
||||
.features
|
||||
.disable(Feature::TranscriptV2)
|
||||
.expect("disable transcript feature in refreshed config");
|
||||
chat.config.tui_fullscreen_transcript = false;
|
||||
drain_insert_history(&mut rx);
|
||||
chat.on_dynamic_tool_item(dynamic_item("call-1", DynamicToolCallStatus::InProgress));
|
||||
let retained = std::iter::from_fn(|| rx.try_recv().ok())
|
||||
|
||||
@@ -52,7 +52,10 @@ pub(super) fn config_exclusion(
|
||||
if !cli_kv_overrides
|
||||
.iter()
|
||||
.all(|(key, value)| match key.as_str() {
|
||||
"suppress_unstable_features_warning" => value.is_bool(),
|
||||
"suppress_unstable_features_warning" | "tui.fullscreen_transcript" => value.is_bool(),
|
||||
"tui" => value.as_table().is_some_and(|tui| {
|
||||
tui.len() == 1 && tui.get("fullscreen_transcript").is_some_and(toml::Value::is_bool)
|
||||
}),
|
||||
"features" => value.as_table().is_some_and(|features| {
|
||||
!features.is_empty()
|
||||
&& features
|
||||
|
||||
@@ -27,6 +27,12 @@ fn audited_overrides_allow_daemon_without_allowing_arbitrary_config() {
|
||||
("features={worktrees=true,shell_tool=false}", false),
|
||||
("features.shell_tool=false", false),
|
||||
("features.worktrees.enabled=true", false),
|
||||
("tui.fullscreen_transcript=true", true),
|
||||
("tui.fullscreen_transcript=false", true),
|
||||
("tui.fullscreen_transcript=\"true\"", false),
|
||||
("tui={fullscreen_transcript=true}", true),
|
||||
("tui={fullscreen_transcript='true'}", false),
|
||||
("tui={fullscreen_transcript=true,animations=false}", false),
|
||||
("features={}", false),
|
||||
("model='test'", false),
|
||||
] {
|
||||
|
||||
@@ -1843,7 +1843,7 @@ async fn run_ratatui_app(
|
||||
// Cloud configuration and session selection can change screen policy after first paint.
|
||||
let use_alt_screen = determine_alt_screen_mode(cli.no_alt_screen, config.tui_alternate_screen);
|
||||
let mode = crate::transcript_mode::TranscriptMode::resolve(
|
||||
config.features.enabled(Feature::TranscriptV2),
|
||||
config.tui_fullscreen_transcript,
|
||||
use_alt_screen,
|
||||
);
|
||||
if use_alt_screen != tui.is_alt_screen_enabled() || mode.is_owned() != tui.is_owned_screen() {
|
||||
|
||||
@@ -13,7 +13,6 @@ use crate::transcript_mode::TranscriptMode;
|
||||
use codex_config::types::History;
|
||||
use codex_config::types::Notice;
|
||||
use codex_config::types::Tui;
|
||||
use codex_features::Feature;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -56,7 +55,7 @@ impl LocalSettings {
|
||||
};
|
||||
Self {
|
||||
transcript_mode: TranscriptMode::resolve(
|
||||
config.features.enabled(Feature::TranscriptV2),
|
||||
config.tui_fullscreen_transcript,
|
||||
config.tui_alternate_screen != codex_config::types::AltScreenMode::Never,
|
||||
),
|
||||
tui: Tui {
|
||||
@@ -71,6 +70,7 @@ impl LocalSettings {
|
||||
vim_mode_default: config.tui_vim_mode_default,
|
||||
question_esc_back: config.tui_question_esc_back,
|
||||
raw_output_mode: config.tui_raw_output_mode,
|
||||
fullscreen_transcript: config.tui_fullscreen_transcript,
|
||||
alternate_screen: config.tui_alternate_screen,
|
||||
status_line: config.tui_status_line.clone(),
|
||||
status_line_use_colors: config.tui_status_line_use_colors,
|
||||
|
||||
@@ -16,7 +16,7 @@ async fn launch_screen_mode_survives_configuration_reload() -> anyhow::Result<()
|
||||
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
|
||||
.build()
|
||||
.await?;
|
||||
config.features.enable(Feature::TranscriptV2)?;
|
||||
config.tui_fullscreen_transcript = true;
|
||||
config.tui_alternate_screen = AltScreenMode::Auto;
|
||||
|
||||
for (alternate_screen, owned, expected_mode, expected_alt) in [
|
||||
@@ -34,7 +34,7 @@ async fn launch_screen_mode_survives_configuration_reload() -> anyhow::Result<()
|
||||
);
|
||||
|
||||
let mut reloaded_config = config.clone();
|
||||
reloaded_config.features.disable(Feature::TranscriptV2)?;
|
||||
reloaded_config.tui_fullscreen_transcript = false;
|
||||
reloaded_config.tui_alternate_screen = AltScreenMode::Never;
|
||||
reloaded_config.tui_theme = Some("nord".into());
|
||||
let mut expected = LocalSettings::from(&reloaded_config);
|
||||
@@ -98,6 +98,7 @@ whimsy = false # Retired: must not override effects or prevent strict loading.
|
||||
show_tooltips = false
|
||||
show_server_version_notice = false
|
||||
auto_recap = false
|
||||
fullscreen_transcript = true
|
||||
vim_mode_default = true
|
||||
terminal_resize_reflow_max_rows = 0
|
||||
session_picker_view = "comfortable"
|
||||
@@ -119,7 +120,14 @@ fast_default_opt_out = true
|
||||
ignore_project_config: true,
|
||||
..LoaderOverrides::without_managed_config_for_tests()
|
||||
})
|
||||
.cli_overrides(vec![("tui.disable_paste_burst".into(), true.into())])
|
||||
.cli_overrides(vec![
|
||||
("tui.disable_paste_burst".into(), true.into()),
|
||||
// The deprecated flag must not override or migrate into the TUI preference.
|
||||
(
|
||||
"features.transcript_v2".into(),
|
||||
config_text.is_empty().into(),
|
||||
),
|
||||
])
|
||||
.build()
|
||||
.await?;
|
||||
assert_eq!(config.startup_warnings, Vec::<String>::new());
|
||||
@@ -133,11 +141,24 @@ fast_default_opt_out = true
|
||||
expected.show_tooltips = false;
|
||||
expected.show_server_version_notice = false;
|
||||
expected.auto_recap = false;
|
||||
expected.fullscreen_transcript = true;
|
||||
expected.vim_mode_default = true;
|
||||
expected.terminal_resize_reflow_max_rows = Some(0);
|
||||
expected.session_picker_view = Some(SessionPickerViewMode::Comfortable);
|
||||
}
|
||||
assert_eq!(
|
||||
local.transcript_mode.is_owned(),
|
||||
expected.fullscreen_transcript
|
||||
);
|
||||
assert_eq!(local.tui, expected);
|
||||
assert_eq!(
|
||||
config
|
||||
.features
|
||||
.legacy_feature_usages()
|
||||
.map(|usage| usage.alias.as_str())
|
||||
.collect::<Vec<_>>(),
|
||||
vec!["features.transcript_v2"],
|
||||
);
|
||||
assert_eq!(
|
||||
local.terminal_resize_reflow(),
|
||||
config.terminal_resize_reflow
|
||||
|
||||
@@ -12,16 +12,11 @@ use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_config::TomlValue;
|
||||
use codex_features::Feature;
|
||||
use codex_features::FeatureConfigSource;
|
||||
use codex_features::FeatureOverrides;
|
||||
use codex_features::Features;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use crate::Cli;
|
||||
use crate::keymap::RuntimeKeymap;
|
||||
use crate::legacy_core::config::ConfigTomlLoadResult;
|
||||
use crate::legacy_core::config::ManagedFeatures;
|
||||
use crate::legacy_core::config::load_config_toml_with_layer_stack;
|
||||
use crate::startup_draft::StartupScreen;
|
||||
|
||||
@@ -56,25 +51,6 @@ pub(super) async fn load(
|
||||
)
|
||||
.await?;
|
||||
let config_toml = &bootstrap_config.config_toml;
|
||||
let configured_features = Features::from_sources(
|
||||
FeatureConfigSource {
|
||||
features: config_toml.features.as_ref(),
|
||||
experimental_use_unified_exec_tool: config_toml.experimental_use_unified_exec_tool,
|
||||
},
|
||||
FeatureConfigSource::default(),
|
||||
FeatureOverrides::default(),
|
||||
);
|
||||
// Full configuration construction will publish its normal managed-feature warnings once.
|
||||
let mut startup_warnings = Vec::new();
|
||||
let features = ManagedFeatures::from_configured_with_warnings(
|
||||
configured_features,
|
||||
bootstrap_config
|
||||
.config_layer_stack
|
||||
.requirements()
|
||||
.feature_requirements
|
||||
.clone(),
|
||||
&mut startup_warnings,
|
||||
)?;
|
||||
let alternate_screen = config_toml
|
||||
.tui
|
||||
.as_ref()
|
||||
@@ -82,7 +58,10 @@ pub(super) async fn load(
|
||||
.unwrap_or_default();
|
||||
let use_alt_screen = crate::determine_alt_screen_mode(cli.no_alt_screen, alternate_screen);
|
||||
let transcript_mode = crate::transcript_mode::TranscriptMode::resolve(
|
||||
features.enabled(Feature::TranscriptV2),
|
||||
config_toml
|
||||
.tui
|
||||
.as_ref()
|
||||
.is_some_and(|tui| tui.fullscreen_transcript),
|
||||
use_alt_screen,
|
||||
);
|
||||
let status_line_enabled = config_toml
|
||||
|
||||
@@ -50,23 +50,24 @@ async fn selected_profile_controls_submit_before_the_first_frame() -> anyhow::Re
|
||||
async fn first_frame_respects_screen_and_status_line_overrides() -> anyhow::Result<()> {
|
||||
let home = tempfile::tempdir()?;
|
||||
for (config, args, expected) in [
|
||||
("", vec!["codex"], (true, false, true)),
|
||||
(
|
||||
"[features]\ntranscript_v2 = true\n",
|
||||
"[tui]\nfullscreen_transcript = true\n",
|
||||
vec!["codex"],
|
||||
(true, true, true),
|
||||
),
|
||||
(
|
||||
"[features]\ntranscript_v2 = true\n",
|
||||
"[tui]\nfullscreen_transcript = true\n",
|
||||
vec!["codex", "--no-alt-screen"],
|
||||
(false, false, true),
|
||||
),
|
||||
(
|
||||
"[features]\ntranscript_v2 = false\n",
|
||||
"[tui]\nfullscreen_transcript = false\n",
|
||||
vec!["codex"],
|
||||
(true, false, true),
|
||||
),
|
||||
(
|
||||
"[tui]\nalternate_screen = \"never\"\nstatus_line = []\n",
|
||||
"[tui]\nfullscreen_transcript = true\nalternate_screen = \"never\"\nstatus_line = []\n",
|
||||
vec!["codex"],
|
||||
(false, false, false),
|
||||
),
|
||||
|
||||
@@ -161,12 +161,7 @@ fn owned_screen_entry_paints_before_sync_ends_and_exit_clears_inline_draft() ->
|
||||
let mut terminal = PtyCodex::start(
|
||||
&repo_root,
|
||||
codex_home,
|
||||
&[
|
||||
"-c",
|
||||
"tui.alternate_screen=\"always\"",
|
||||
"-c",
|
||||
"features.transcript_v2=true",
|
||||
],
|
||||
&["-c", "tui.fullscreen_transcript=true"],
|
||||
)?;
|
||||
terminal.wait_for_startup()?;
|
||||
let deadline = Instant::now() + STARTUP_TIMEOUT;
|
||||
@@ -180,6 +175,10 @@ fn owned_screen_entry_paints_before_sync_ends_and_exit_clears_inline_draft() ->
|
||||
"owned screen did not open"
|
||||
);
|
||||
terminal.wait_for_screen("GPT-5.6-Terra")?;
|
||||
ensure!(
|
||||
terminal.parser.screen().alternate_screen(),
|
||||
"fullscreen did not survive application startup"
|
||||
);
|
||||
let enter_alt = b"\x1b[?1049h";
|
||||
let begin_sync = b"\x1b[?2026h";
|
||||
let end_sync = b"\x1b[?2026l";
|
||||
@@ -252,6 +251,24 @@ fn owned_screen_entry_paints_before_sync_ends_and_exit_clears_inline_draft() ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fullscreen_transcript_defaults_to_terminal_scrollback() -> Result<()> {
|
||||
let repo_root = codex_utils_cargo_bin::repo_root()?;
|
||||
let codex_home = tempfile::tempdir()?;
|
||||
write_test_config(codex_home.path(), &repo_root)?;
|
||||
let mut terminal = PtyCodex::start(&repo_root, codex_home, &[])?;
|
||||
terminal.wait_for_startup()?;
|
||||
terminal.wait_for_screen("GPT-5.6-Terra")?;
|
||||
ensure!(
|
||||
!terminal
|
||||
.output
|
||||
.windows(b"\x1b[?1049h".len())
|
||||
.any(|bytes| bytes == b"\x1b[?1049h"),
|
||||
"default launch entered the alternate screen"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) struct PtyCodex {
|
||||
master: File,
|
||||
child: Child,
|
||||
|
||||
Reference in New Issue
Block a user