Enforce animations = false for screen readers (#20564)

## Why

Issue #20489 calls out that animated TUI affordances can be noisy for
screen-reader users. Codex already has `tui.animations = false` as a
reduced-motion setting, but some live activity rows render spinner-style
prefixes in that mode. These were relatively recent regressions.

We have also regressed this pattern more than once by adding new
spinner/shimmer callsites that do not think through the reduced-motion
path, so this PR adds a small guardrail while fixing the current
surfaces.

## What changed

- Omit the live status-row spinner when animations are disabled, so the
row starts with stable text like `Working (...)`.
- Render running hook headers without the spinner prefix when animations
are disabled, while preserving shimmer/spinner behavior when animations
are enabled.
- Centralize TUI activity indicators in `tui/src/motion.rs`, with
explicit reduced-motion choices for hidden prefixes, static bullets, and
plain shimmer-text fallbacks.
- Route existing spinner/shimmer callsites through the central motion
helper, including exec rows, MCP/web-search/loading rows, hook rows,
plugin loading, and onboarding loading text.
- Add a source-scan regression test that rejects direct `spinner(...)`
or `shimmer_spans(...)` usage outside the central module and primitive
definition.
- Add focused coverage that reduced-motion active exec rows are stable,
status rows start without a spinner, running hooks omit the spinner, and
MCP inventory loading stays stable.
- Update the one affected status-indicator snapshot; the existing detail
tree prefix remains unchanged.

## Verification

- `cargo test -p codex-tui`
This commit is contained in:
Eric Traut authored and GitHub committed 2026-05-01 09:07:56 -07:00
1 parent f476338f93
commit 227bee0445
11 files changed
+354 -45

No files matched your search

+6 -2
View File
@@ -46,10 +46,11 @@ use uuid::Uuid;
use crate::LoginStatus;
use crate::key_hint::KeyBinding;
use crate::key_hint::KeyBindingListExt;
use crate::motion::MotionMode;
use crate::motion::shimmer_text;
use crate::onboarding::keys;
use crate::onboarding::onboarding_screen::KeyboardHandler;
use crate::onboarding::onboarding_screen::StepStateProvider;
use crate::shimmer::shimmer_spans;
use crate::tui::FrameRequester;
/// Marks buffer cells that have cyan+underlined style as an OSC 8 hyperlink.
@@ -511,7 +512,10 @@ impl AuthModeWidget {
// Schedule a follow-up frame to keep the shimmer animation going.
self.request_frame
.schedule_frame_in(std::time::Duration::from_millis(100));
spans.extend(shimmer_spans("Finish signing in via your browser"));
spans.extend(shimmer_text(
"Finish signing in via your browser",
MotionMode::Animated,
));
} else {
spans.push("Finish signing in via your browser".into());
}