Scroll region rendering broken in SwiftTerm after recent flickering fix
Description
After the recent flickering fix (mentioned as fixed ~Dec 18-19, 2025), Claude Code's TUI has broken scroll region behavior when viewed through SwiftTerm-based terminal emulators (iOS).
Symptoms
- The bottom part of the terminal (status bar, prompt area) stays correctly in place
- The top part (main content area) scrolls up one line on each terminal update
- Content gets pushed off the top of the screen progressively
- The scroll region (DECSTBM) seems to be set correctly, but something causes an extra scroll on each output
Visual
The terminal looks like Claude Code has set scroll regions correctly (status bar fixed at bottom), but every time new output arrives, the top region scrolls up one extra line, pushing content off screen.
Environment
- Terminal emulator: SwiftTerm (iOS)
- App: Xtro (iOS terminal client connecting to Mac)
- Issue started: Around Dec 18-19, 2025 (coinciding with flickering fix)
Notes
- Other TUI apps (vim, htop, etc.) work correctly - only Claude Code exhibits this behavior
- Rotating the device (forcing terminal resize/redraw) used to fix the issue temporarily, but no longer does
- The issue exists across multiple commits of our app, suggesting the change is on Claude Code's side
Workaround attempts
- Terminal resize (SIGWINCH) - no longer fixes it
- Different terminal sizes - same issue
Request
Could you check if the recent flickering fix changed how scroll regions (DECSTBM - ESC[top;bottomr) are being set or how the screen is being refreshed? Something in that change appears incompatible with SwiftTerm's scroll region handling.
Just the same issue when working remotely using SSH Android client (Termius).
Closing - the issue resolved after restarting. Apologies for the noise.
Reopening - other users have reported similar scroll region issues. The bug may be intermittent or environment-specific. A restart temporarily resolved it on my end but the underlying issue may still exist.
Update: Pinpointed the versions that introduced bugs
After testing version by version:
| Version | Scroll Region Bug | Input Line Shift Bug |
|---|---|---|
| 2.0.70 | ✅ Works | ✅ Works |
| 2.0.71 | ❌ Introduced | ✅ Works |
| 2.0.72 | ❌ Persists | ❌ Introduced |
| 2.0.73 | ❌ Persists | ✅ Fixed |
Bug 1: Scroll Region Bug (introduced in 2.0.71, still present)
Triggered when Claude Code shows an extra status row at the bottom:
⏵⏵ accept edits onmode → scroll bug occurs⏸ plan mode on (shift+tab to cycle)mode → scroll bug occurs- Normal mode (no extra row) → works correctly
When that additional row is added, the scroll region (top portion) scrolls incorrectly - each update causes the top to scroll up by one line while the bottom status bar stays fixed.
Bug 2: Input Line Shift Bug (introduced in 2.0.72, fixed in 2.0.73)
In 2.0.72, the input line shifted down by one row, causing it to overlap with the horizontal delimiter lines in the prompt area. This was fixed in 2.0.73.
The scroll region bug appears to be related to how terminal height/scroll region boundaries are recalculated when the UI adds that extra status row. Something changed in 2.0.71 that breaks this behavior.
Escape Sequence Analysis Update
I've implemented escape sequence logging in my terminal client to capture exactly what Claude Code is sending. Here are the detailed findings:
Test Environment
- Terminal: 55 rows
- Claude Code version: 2.0.73
Key Finding: Extra Erase Line (EL) Commands
Normal mode (no status bar):
=== BATCH: 1549 bytes | CUP:0 EL:15 ED:0 DECSTBM:0 ===
With 'accept edits' status bar:
=== BATCH: 1649 bytes | CUP:0 EL:16 ED:0 DECSTBM:0 [ACCEPT_EDITS] ===
The Problem
When the ⏵⏵ accept edits or ⏸ plan mode status bar appears at the bottom, Claude Code sends 1 extra EL (Erase Line) command per synchronized output batch (16 instead of 15).
What's Missing
Claude Code does NOT set a scroll region (DECSTBM / ESC[<top>;<bottom>r). Without a scroll region defined, the terminal treats the entire screen as scrollable. When the extra EL commands push content past the visible area, it causes the scroll.
Suggested Fix
When one more row in the status bar is added, Claude Code should either:
- Set a proper scroll region - e.g.,
ESC[1;54rfor a XX-row terminal with one more status row - Adjust the EL count - Send 15 EL commands instead of 16 when the status bar is present
Raw Log Samples
Before accept edits mode (16:50:55):
[16:50:55.551] === BATCH: 1548 bytes | CUP:0 EL:15 ED:0 DECSTBM:0 ===
[16:50:55.579] === BATCH: 1527 bytes | CUP:0 EL:15 ED:0 DECSTBM:0 ===
[16:50:55.966] === BATCH: 1538 bytes | CUP:0 EL:15 ED:0 DECSTBM:0 ===
After accept edits mode activates (16:50:57):
[16:50:57.466] === BATCH: 4739 bytes | CUP:0 EL:45 ED:0 DECSTBM:0 [ACCEPT_EDITS] ===
[16:50:57.494] === BATCH: 1649 bytes | CUP:0 EL:16 ED:0 DECSTBM:0 [ACCEPT_EDITS] ===
[16:50:57.502] === BATCH: 3298 bytes | CUP:0 EL:32 ED:0 DECSTBM:0 [ACCEPT_EDITS] ===
[16:50:57.518] === BATCH: 1649 bytes | CUP:0 EL:16 ED:0 DECSTBM:0 [ACCEPT_EDITS] ===
The pattern is consistent: batches that would have 15 EL now have 16 EL, batches with 30 EL now have 32 EL, etc.
Ditto. Nothing to add other than my vote to fix this!