fix(tui): auto-disable DEC 2026 sync output on Ptyxis to stop VTE 0.84 flicker

Ptyxis 50.x (the new default terminal on Ubuntu 26.04) ships with
VTE 0.84.x, which parses the `\x1b[?2026h` / `\x1b[?2026l` synchronized-
output begin/end pair but still flashes the entire viewport on every
wrapped frame instead of deferring rendering. gnome-terminal 3.58 on
the same VTE renders cleanly, so the heuristic stays narrow: trigger
only on TERM_PROGRAM matching `ptyxis` (case-insensitive) or
PTYXIS_VERSION non-empty.

Add a new `synchronized_output` setting (`auto` | `on` | `off`,
default `auto`) controlling whether the renderer wraps each frame in
DEC 2026. `apply_env_overrides` flips `auto` → `off` when Ptyxis is
detected; the four wrapping sites in ui.rs (`draw_app_frame_inner`,
`reset_terminal_viewport`, `resume_terminal`, and the early-init
viewport reset) now respect the resolved flag. Users on Ptyxis who
upgrade past an upstream fix or want to confirm one landed can
override with `/set synchronized_output on`.

8 new tests cover: default-auto resolves enabled, off disables,
on stays enabled, set/aliases, Ptyxis via TERM_PROGRAM, Ptyxis via
PTYXIS_VERSION alone, explicit `on` beats the heuristic, explicit
`off` is preserved, and no non-Ptyxis TERM_PROGRAM (including Ghostty
and VS Code, which both keep DEC 2026 on) regresses.

Reported via WeChat by Cyrux on Ubuntu 26.04 with v0.8.30 npm install;
analysis by Hunter pinpointed Ptyxis + VTE 0.84 as the cause.
This commit is contained in:
Hunter Bown
2026-05-11 22:04:26 -05:00
parent 74fbc83b9e
commit fd82f85800
4 changed files with 411 additions and 8 deletions
+33
View File
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Fixed
- **DEC 2026 synchronized output is auto-disabled on Ptyxis** (the new
default terminal on Ubuntu 26.04 and an increasingly common Linux
TUI host). Ptyxis 50.x ships on VTE 0.84.x, which parses the
`\x1b[?2026h` / `\x1b[?2026l` begin/end pair but still flashes the
entire viewport on every wrapped frame instead of deferring
rendering — so a TUI that uses DEC 2026 to avoid tearing
experiences visible flicker on every redraw. gnome-terminal 3.58
on the same VTE renders cleanly, so the heuristic must stay narrow:
we trigger only on `TERM_PROGRAM` matching `ptyxis`
case-insensitively, or `PTYXIS_VERSION` set to any non-empty value.
Either signal flips the new `synchronized_output` setting from
`auto` to `off`; the renderer then skips the begin/end pair on
every draw, in `reset_terminal_viewport`, and in `resume_terminal`.
Users on Ptyxis who upgrade past the upstream fix (or who want to
confirm a fix landed) can override with
`/set synchronized_output on` or by adding
`synchronized_output = "on"` to `~/.config/deepseek/settings.toml`.
### Added
- **New `synchronized_output` setting** controls whether the renderer
wraps each frame in DEC mode 2026 synchronized output. Accepts
`auto` (default; respect the Ptyxis env opt-out), `on` (always emit
DEC 2026, override the heuristic), or `off` (never emit DEC 2026).
The cost of `off` is brief tearing on terminals that handle DEC
2026 cleanly; it is purely a rendering-quality knob, not a
correctness one. Set via `/set synchronized_output <auto|on|off>`
or in `~/.config/deepseek/settings.toml`.
## [0.8.30] - 2026-05-11
A "tighten what we shipped" release. Bare single-letter keystrokes
+327
View File
@@ -236,6 +236,23 @@ pub struct Settings {
/// replaced the whale during the dots era.
/// - `"off"`: hide the indicator entirely.
pub status_indicator: String,
/// Whether to wrap each draw in DEC mode 2026 synchronized output
/// (`\x1b[?2026h` … `\x1b[?2026l`). Synchronized output asks the
/// terminal to defer rendering until the whole frame is staged so
/// GPU-accelerated terminals (Ghostty, VS Code, Kitty, WezTerm)
/// don't flash a blank intermediate frame.
///
/// - `"auto"` (default): emit DEC 2026 unless an environment signal
/// says the active terminal mishandles it (currently Ptyxis 50.x
/// on VTE 0.84.x — see [`Settings::apply_env_overrides`]).
/// - `"on"`: always emit DEC 2026 (override the auto opt-out).
/// - `"off"`: never emit DEC 2026. Use this if your terminal flashes
/// the whole screen on every redraw — most often Ptyxis on
/// Ubuntu 26.04 today; historically also some legacy ssh+screen
/// stacks. The cost of `off` is brief tearing on terminals that
/// *do* support DEC 2026; it is purely a rendering-quality knob,
/// not a correctness one.
pub synchronized_output: String,
}
impl Default for Settings {
@@ -274,6 +291,7 @@ impl Default for Settings {
default_model: None,
provider_models: None,
status_indicator: "whale".to_string(),
synchronized_output: "auto".to_string(),
}
}
}
@@ -315,6 +333,8 @@ impl Settings {
s.transcript_spacing = normalize_transcript_spacing(&s.transcript_spacing).to_string();
s.sidebar_focus = normalize_sidebar_focus(&s.sidebar_focus).to_string();
s.status_indicator = normalize_status_indicator(&s.status_indicator).to_string();
s.synchronized_output =
normalize_synchronized_output(&s.synchronized_output).to_string();
s.locale = normalize_configured_locale(&s.locale)
.unwrap_or("en")
.to_string();
@@ -349,6 +369,26 @@ impl Settings {
self.low_motion = true;
self.fancy_animations = false;
}
// Ptyxis 50.x (the new default terminal on Ubuntu 26.04) ships with
// VTE 0.84.x which mishandles DEC mode 2026 synchronized output: the
// begin/end pair is parsed but each wrapped frame still triggers a
// full-viewport flash on the GPU compositor side, so any TUI that
// uses DEC 2026 to avoid tearing instead gets visible flicker on
// every redraw. gnome-terminal 3.58 on the same VTE renders cleanly,
// so we can't broaden the opt-out to all VTE-based terminals —
// only the Ptyxis-specific signals trigger it. Confirmed
// user-visible regression starting with Ubuntu 26.04's default
// terminal swap; cargo-installed binaries are not exempt because
// the bug is in the terminal, not the binary.
//
// Only flip `auto` to `off`; respect an explicit `"on"` so users
// who upgrade Ptyxis or want to confirm the fix landed upstream
// can override the heuristic from `~/.config/deepseek/settings.toml`
// or `/set synchronized_output on`.
if self.synchronized_output.eq_ignore_ascii_case("auto") && detected_ptyxis_terminal() {
self.synchronized_output = "off".to_string();
}
}
/// Save settings to disk
@@ -445,6 +485,15 @@ impl Settings {
}
self.status_indicator = normalized.to_string();
}
"synchronized_output" | "sync_output" | "sync" => {
let normalized = normalize_synchronized_output(value);
if !["auto", "on", "off"].contains(&normalized) {
anyhow::bail!(
"Failed to update setting: invalid synchronized_output '{value}'. Expected: auto, on, off."
);
}
self.synchronized_output = normalized.to_string();
}
"default_mode" | "mode" => {
let normalized = normalize_mode(value);
if !["agent", "plan", "yolo"].contains(&normalized) {
@@ -557,6 +606,10 @@ impl Settings {
lines.push(format!(" composer_vim_mode: {}", self.composer_vim_mode));
lines.push(format!(" transcript_spacing: {}", self.transcript_spacing));
lines.push(format!(" status_indicator: {}", self.status_indicator));
lines.push(format!(
" synchronized_output: {}",
self.synchronized_output
));
lines.push(format!(" default_mode: {}", self.default_mode));
lines.push(format!(
" sidebar_width: {}%",
@@ -629,6 +682,10 @@ impl Settings {
"status_indicator",
"Header status indicator next to effort chip: whale, dots, off",
),
(
"synchronized_output",
"DEC 2026 synchronized output: auto, on, off (set off if your terminal flickers)",
),
("default_mode", "Default mode: agent, plan, yolo"),
("sidebar_width", "Sidebar width percentage: 10-50"),
(
@@ -650,6 +707,16 @@ impl Settings {
.get_or_insert_with(std::collections::HashMap::new)
.insert(provider.to_string(), model.to_string());
}
/// Resolved boolean for whether the renderer should wrap each frame in
/// DEC mode 2026 synchronized output. `auto` and `on` enable; `off`
/// disables. The `auto` → `off` flip for known-bad terminals happens
/// earlier in [`Self::apply_env_overrides`]; this method only inspects
/// the final state.
#[must_use]
pub fn synchronized_output_enabled(&self) -> bool {
!self.synchronized_output.eq_ignore_ascii_case("off")
}
}
fn normalize_default_model(value: &str) -> Option<String> {
@@ -714,6 +781,45 @@ fn normalize_status_indicator(value: &str) -> &str {
}
}
/// Normalize the `synchronized_output` setting. Accepts the canonical
/// `"auto"` / `"on"` / `"off"` plus the usual truthy/falsey spellings.
/// Unknown values fall through unchanged so the parser in `set` can
/// surface a clear error.
fn normalize_synchronized_output(value: &str) -> &str {
match value.trim().to_ascii_lowercase().as_str() {
"auto" | "default" => "auto",
"on" | "true" | "yes" | "1" | "enabled" => "on",
"off" | "false" | "no" | "0" | "disabled" => "off",
_ => value,
}
}
/// Returns `true` when the active terminal is Ptyxis (the new default
/// terminal on Ubuntu 26.04). Used by [`Settings::apply_env_overrides`]
/// to flip `synchronized_output` from `auto` to `off` so DEC mode 2026
/// flicker on Ptyxis 50.x + VTE 0.84.x stops at the source.
///
/// We deliberately keep this narrow:
///
/// - `TERM_PROGRAM` matches `ptyxis` case-insensitively (the value
/// Ptyxis sets when it forwards a process-launch context).
/// - `PTYXIS_VERSION` is set to any non-empty value (the binary's
/// own version probe, present whether or not `TERM_PROGRAM` made it
/// into the child environment).
///
/// Either signal is sufficient. We do *not* trigger on `VTE_VERSION`
/// alone because gnome-terminal 3.58 ships with the same VTE 0.84.x
/// and renders cleanly — broadening the heuristic would regress every
/// gnome-terminal user.
pub fn detected_ptyxis_terminal() -> bool {
if let Ok(program) = std::env::var("TERM_PROGRAM")
&& program.trim().to_ascii_lowercase().contains("ptyxis")
{
return true;
}
matches!(std::env::var("PTYXIS_VERSION"), Ok(v) if !v.trim().is_empty())
}
fn normalize_optional_background_color(value: Option<&str>) -> Option<String> {
value.and_then(|raw| normalize_background_color_setting(raw).ok().flatten())
}
@@ -1053,6 +1159,227 @@ mod tests {
}
}
// ────────────────────────────────────────────────────────────────────────
// synchronized_output / Ptyxis flicker detection
// ────────────────────────────────────────────────────────────────────────
#[test]
fn synchronized_output_defaults_to_auto_and_resolves_to_enabled() {
let s = Settings::default();
assert_eq!(s.synchronized_output, "auto");
assert!(
s.synchronized_output_enabled(),
"auto must keep DEC 2026 on so terminals that support it stay tear-free"
);
}
#[test]
fn synchronized_output_off_disables_dec_2026() {
let s = Settings {
synchronized_output: "off".to_string(),
..Settings::default()
};
assert!(!s.synchronized_output_enabled());
}
#[test]
fn synchronized_output_on_keeps_dec_2026_enabled() {
let s = Settings {
synchronized_output: "on".to_string(),
..Settings::default()
};
assert!(s.synchronized_output_enabled());
}
#[test]
fn synchronized_output_set_command_accepts_aliases() {
let mut s = Settings::default();
for value in ["auto", "AUTO", "default"] {
s.set("synchronized_output", value).expect("valid");
assert_eq!(s.synchronized_output, "auto");
}
for value in ["on", "true", "yes", "1", "ENABLED"] {
s.set("sync_output", value).expect("valid");
assert_eq!(s.synchronized_output, "on");
}
for value in ["off", "false", "no", "0", "DISABLED"] {
s.set("sync", value).expect("valid");
assert_eq!(s.synchronized_output, "off");
}
let err = s
.set("synchronized_output", "maybe")
.expect_err("unknown value rejected");
assert!(
err.to_string().contains("synchronized_output"),
"error names the offending key: {err}"
);
}
#[test]
fn ptyxis_term_program_flips_synchronized_output_off() {
let _g = term_program_test_guard();
let prev = std::env::var_os("TERM_PROGRAM");
let prev_ptyxis = std::env::var_os("PTYXIS_VERSION");
// SAFETY: serialised by the guard.
unsafe {
std::env::set_var("TERM_PROGRAM", "Ptyxis");
std::env::remove_var("PTYXIS_VERSION");
}
let mut s = Settings::default();
assert_eq!(s.synchronized_output, "auto");
s.apply_env_overrides();
assert_eq!(
s.synchronized_output, "off",
"Ptyxis 50.x mishandles DEC 2026 — auto must flip to off so VTE 0.84 stops flickering"
);
assert!(
!s.synchronized_output_enabled(),
"resolved boolean must agree with stored string"
);
// SAFETY: cleanup under the guard.
unsafe {
match prev {
Some(v) => std::env::set_var("TERM_PROGRAM", v),
None => std::env::remove_var("TERM_PROGRAM"),
}
match prev_ptyxis {
Some(v) => std::env::set_var("PTYXIS_VERSION", v),
None => std::env::remove_var("PTYXIS_VERSION"),
}
}
}
#[test]
fn ptyxis_version_env_alone_flips_synchronized_output_off() {
let _g = term_program_test_guard();
let prev = std::env::var_os("TERM_PROGRAM");
let prev_ptyxis = std::env::var_os("PTYXIS_VERSION");
// SAFETY: serialised by the guard.
unsafe {
std::env::remove_var("TERM_PROGRAM");
std::env::set_var("PTYXIS_VERSION", "50.1");
}
let mut s = Settings::default();
s.apply_env_overrides();
assert_eq!(
s.synchronized_output, "off",
"PTYXIS_VERSION alone is sufficient — Ptyxis sets this even when TERM_PROGRAM isn't propagated"
);
// SAFETY: cleanup under the guard.
unsafe {
match prev {
Some(v) => std::env::set_var("TERM_PROGRAM", v),
None => std::env::remove_var("TERM_PROGRAM"),
}
match prev_ptyxis {
Some(v) => std::env::set_var("PTYXIS_VERSION", v),
None => std::env::remove_var("PTYXIS_VERSION"),
}
}
}
#[test]
fn ptyxis_does_not_override_user_explicit_on() {
// Users who set `synchronized_output = "on"` (e.g. to confirm a
// Ptyxis upgrade fixed it) must keep DEC 2026 even on Ptyxis.
let _g = term_program_test_guard();
let prev = std::env::var_os("TERM_PROGRAM");
// SAFETY: serialised by the guard.
unsafe {
std::env::set_var("TERM_PROGRAM", "ptyxis");
}
let mut s = Settings {
synchronized_output: "on".to_string(),
..Settings::default()
};
s.apply_env_overrides();
assert_eq!(
s.synchronized_output, "on",
"explicit user override must beat the Ptyxis env heuristic"
);
// SAFETY: cleanup under the guard.
unsafe {
match prev {
Some(v) => std::env::set_var("TERM_PROGRAM", v),
None => std::env::remove_var("TERM_PROGRAM"),
}
}
}
#[test]
fn ptyxis_does_not_override_user_explicit_off() {
// A user with `synchronized_output = "off"` on a non-Ptyxis
// terminal stays off after env detection (no-op flip).
let _g = term_program_test_guard();
let prev = std::env::var_os("TERM_PROGRAM");
// SAFETY: serialised by the guard.
unsafe {
std::env::set_var("TERM_PROGRAM", "xterm-256color");
}
let mut s = Settings {
synchronized_output: "off".to_string(),
..Settings::default()
};
s.apply_env_overrides();
assert_eq!(s.synchronized_output, "off");
// SAFETY: cleanup under the guard.
unsafe {
match prev {
Some(v) => std::env::set_var("TERM_PROGRAM", v),
None => std::env::remove_var("TERM_PROGRAM"),
}
}
}
#[test]
fn non_ptyxis_term_programs_keep_synchronized_output_auto() {
let _g = term_program_test_guard();
let prev = std::env::var_os("TERM_PROGRAM");
let prev_ptyxis = std::env::var_os("PTYXIS_VERSION");
// SAFETY: clean slate so non-Ptyxis programs don't see a leaked
// PTYXIS_VERSION from another test.
unsafe {
std::env::remove_var("PTYXIS_VERSION");
}
for program in [
"iTerm.app",
"Apple_Terminal",
"WezTerm",
"xterm-256color",
"gnome-terminal-server",
// The Ghostty / VS Code paths force low_motion but must NOT
// disable DEC 2026 — they handle synchronized output cleanly.
"ghostty",
"vscode",
] {
// SAFETY: serialised by the guard.
unsafe {
std::env::set_var("TERM_PROGRAM", program);
}
let mut s = Settings::default();
s.apply_env_overrides();
assert_eq!(
s.synchronized_output, "auto",
"TERM_PROGRAM={program:?} must not opt out of DEC 2026"
);
assert!(
s.synchronized_output_enabled(),
"resolved boolean for {program:?} must stay enabled"
);
}
// SAFETY: cleanup under the guard.
unsafe {
match prev {
Some(v) => std::env::set_var("TERM_PROGRAM", v),
None => std::env::remove_var("TERM_PROGRAM"),
}
match prev_ptyxis {
Some(v) => std::env::set_var("PTYXIS_VERSION", v),
None => std::env::remove_var("PTYXIS_VERSION"),
}
}
}
// ────────────────────────────────────────────────────────────────────────
// TuiPrefs tests
// ────────────────────────────────────────────────────────────────────────
+10
View File
@@ -810,6 +810,14 @@ pub struct App {
/// until the footer widget consumes it.
#[allow(dead_code)]
pub fancy_animations: bool,
/// Whether the renderer should wrap each frame in DEC mode 2026
/// synchronized output. Resolved from `Settings::synchronized_output`
/// at construction; `auto`/`on` → `true`, `off` → `false`. The Ptyxis
/// auto-detect path in `Settings::apply_env_overrides` flips `auto`
/// to `off` before App is built, so by the time we read this flag in
/// the draw loop the decision is already made. See the
/// `Settings::synchronized_output` doc for the user-facing knob.
pub synchronized_output_enabled: bool,
/// Header status-indicator chip mode. One of `"whale"` (default, cycles
/// 🐳→🐋 frames keyed off `turn_started_at`), `"dots"` (geometric ◌
/// frames), or `"off"` (chip hidden entirely). Loaded from settings;
@@ -1232,6 +1240,7 @@ impl App {
let calm_mode = settings.calm_mode;
let low_motion = settings.low_motion;
let fancy_animations = settings.fancy_animations;
let synchronized_output_enabled = settings.synchronized_output_enabled();
let status_indicator = settings.status_indicator.clone();
let show_thinking = settings.show_thinking;
let show_tool_details = settings.show_tool_details;
@@ -1425,6 +1434,7 @@ impl App {
calm_mode,
low_motion,
fancy_animations,
synchronized_output_enabled,
status_indicator,
show_thinking,
verbose_transcript: false,
+41 -8
View File
@@ -271,7 +271,18 @@ pub async fn run_tui(config: &Config, options: TuiOptions) -> Result<()> {
);
let backend = ColorCompatBackend::new(stdout, color_depth, palette_mode);
let mut terminal = Terminal::new(backend)?;
reset_terminal_viewport(&mut terminal)?;
// At this point Settings hasn't loaded yet, so we can't read the
// user's `synchronized_output` knob. Use the same env-based Ptyxis
// detection that `Settings::apply_env_overrides` uses, so the
// startup viewport reset matches what every later draw will do on
// this terminal. A user who has explicitly set
// `synchronized_output = "on"` to override Ptyxis detection will
// get sync wrap from the main draw loop onward; the one-time
// startup viewport reset stays opt-out for them, which is the safe
// default because the cost is at most brief tearing on the first
// frame.
let sync_output_at_init = !crate::settings::detected_ptyxis_terminal();
reset_terminal_viewport(&mut terminal, sync_output_at_init)?;
let event_broker = EventBroker::new();
// Local mutable copy so runtime config flips (e.g. `/provider` switch)
@@ -1215,6 +1226,7 @@ async fn run_event_loop(
app.use_alt_screen,
app.use_mouse_capture,
app.use_bracketed_paste,
app.synchronized_output_enabled,
)?;
event_broker.resume_events();
terminal_paused_at = None;
@@ -1289,6 +1301,7 @@ async fn run_event_loop(
app.use_alt_screen,
app.use_mouse_capture,
app.use_bracketed_paste,
app.synchronized_output_enabled,
)?;
event_broker.resume_events();
terminal_paused_at = None;
@@ -1550,6 +1563,7 @@ async fn run_event_loop(
app.use_alt_screen,
app.use_mouse_capture,
app.use_bracketed_paste,
app.synchronized_output_enabled,
)?;
event_broker.resume_events();
terminal_paused_at = None;
@@ -1748,7 +1762,7 @@ async fn run_event_loop(
);
}
reset_terminal_viewport(terminal)?;
reset_terminal_viewport(terminal, app.synchronized_output_enabled)?;
app.handle_resize(final_w, final_h);
// #macos-resize: some terminals (macOS Terminal.app, Windows
// ConHost) briefly report stale dimensions via
@@ -4821,6 +4835,7 @@ async fn apply_command_result(
app.use_alt_screen,
app.use_mouse_capture,
app.use_bracketed_paste,
app.synchronized_output_enabled,
)?;
match editor_result {
Ok(outcome) => {
@@ -5801,7 +5816,15 @@ fn draw_app_frame_inner(
full_repaint: bool,
) -> Result<()> {
terminal.backend_mut().set_palette_mode(app.ui_theme.mode);
let _ = terminal.backend_mut().write_all(BEGIN_SYNC_UPDATE);
// DEC 2026 wrapping is on by default but can be turned off for
// terminals that mishandle it (Ptyxis 50.x + VTE 0.84.x flashes the
// whole viewport on every wrapped frame instead of deferring as the
// standard requires). Settings::synchronized_output_enabled resolves
// the user's setting against the Ptyxis env auto-detect.
let wrap_in_sync_update = app.synchronized_output_enabled;
if wrap_in_sync_update {
let _ = terminal.backend_mut().write_all(BEGIN_SYNC_UPDATE);
}
// Run fallible draw operations in a closure so END_SYNC_UPDATE is
// always sent even if an intermediate step fails. Without this, a
@@ -5818,7 +5841,9 @@ fn draw_app_frame_inner(
})();
// Always end the synchronized update, regardless of success or failure.
let _ = terminal.backend_mut().write_all(END_SYNC_UPDATE);
if wrap_in_sync_update {
let _ = terminal.backend_mut().write_all(END_SYNC_UPDATE);
}
let _ = terminal.backend_mut().flush();
result
}
@@ -6700,6 +6725,7 @@ fn resume_terminal(
use_alt_screen: bool,
use_mouse_capture: bool,
use_bracketed_paste: bool,
sync_output_enabled: bool,
) -> Result<()> {
enable_raw_mode()?;
if use_alt_screen {
@@ -6710,11 +6736,11 @@ fn resume_terminal(
use_mouse_capture,
use_bracketed_paste,
);
reset_terminal_viewport(terminal)?;
reset_terminal_viewport(terminal, sync_output_enabled)?;
Ok(())
}
fn reset_terminal_viewport(terminal: &mut AppTerminal) -> Result<()> {
fn reset_terminal_viewport(terminal: &mut AppTerminal, sync_output_enabled: bool) -> Result<()> {
// Reset scroll margins and origin mode before clearing. Some interactive
// child processes leave DECSTBM/DECOM behind; if ratatui's diff renderer
// then writes "row 0", terminals can place it relative to the leaked
@@ -6728,7 +6754,12 @@ fn reset_terminal_viewport(terminal: &mut AppTerminal) -> Result<()> {
// (`\x1b[?2026h` … `\x1b[?2026l`) so GPU-accelerated terminals
// (Ghostty, VSCode, Kitty, WezTerm) defer rendering until the whole
// frame is staged. Terminals that don't support it silently ignore.
let _ = terminal.backend_mut().write_all(BEGIN_SYNC_UPDATE);
// The wrap is opt-out via `synchronized_output = "off"` for terminals
// that mishandle the sequence (Ptyxis 50.x on VTE 0.84.x flashes the
// whole viewport on each wrapped frame).
if sync_output_enabled {
let _ = terminal.backend_mut().write_all(BEGIN_SYNC_UPDATE);
}
let result = (|| -> Result<()> {
terminal.backend_mut().write_all(TERMINAL_ORIGIN_RESET)?;
@@ -6738,7 +6769,9 @@ fn reset_terminal_viewport(terminal: &mut AppTerminal) -> Result<()> {
})();
// Always end the synchronized update, regardless of success or failure.
let _ = terminal.backend_mut().write_all(END_SYNC_UPDATE);
if sync_output_enabled {
let _ = terminal.backend_mut().write_all(END_SYNC_UPDATE);
}
let _ = terminal.backend_mut().flush();
result
}