Skip to content

fix(tui): skip full redraw when only offscreen lines change - #3105

Closed
Mic92 wants to merge 2 commits into
earendil-works:mainfrom
Mic92:tui-offscreen-spinner-redraw
Closed

fix(tui): skip full redraw when only offscreen lines change#3105
Mic92 wants to merge 2 commits into
earendil-works:mainfrom
Mic92:tui-offscreen-spinner-redraw

Conversation

@Mic92

@Mic92 Mic92 commented Apr 13, 2026

Copy link
Copy Markdown

When using tui extensions like questionnaire or permission gate and the content is larger than the viewport (console), than pi will re-draw a lot causing heavy flickering.

Fixed by checking for offscreen-only changes in which case we don't have to redraw.

Mic92 added 2 commits April 12, 2026 11:32
The early-return paths each open-code the same five-line "sync previous*
state and position the hardware cursor" tail. Consolidating it into a
local closure keeps the invariant in one place and makes the next change
(an additional early return for offscreen-only diffs) a three-liner.

No behaviour change.
When rendered content exceeds terminal height and a component above the
visible viewport mutates (e.g. the Loader spinner ticking while a tall
ui.custom dialog such as a questionnaire or permission gate fills the
screen), doRender() saw firstChanged < prevViewportTop and fell through
to fullRender(true) on every frame. At the spinner's 80ms interval this
produced a clear+reprint storm that looked like the screen scrolling
uncontrollably.

If lastChanged is also above the viewport and the line count is
unchanged, every diff is invisible and the terminal already shows the
correct content. Sync state via commitState() and return without
writing instead.
@Mic92 Mic92 changed the title Tui offscreen spinner redraw fix(tui): skip full redraw when only offscreen lines change Apr 13, 2026
@badlogic

Copy link
Copy Markdown
Collaborator

I'm afraid this is not the correct fix and will corrupt the scrollback due to the missing redraw. It's an invariant that if something above the visible viewport changes, the whole TUI needs to be redrawn. This PR would break that invariant.

@badlogic badlogic closed this Apr 14, 2026
Mic92 added a commit to Mic92/dotfiles that referenced this pull request Apr 14, 2026
The questionnaire and permission-gate extensions can mount a ui.custom
component taller than the terminal, which scrolls the Working spinner
into native scrollback while it keeps ticking every 80ms. The TUI
differential renderer cannot reach those lines, so every tick triggers
a full clear+redraw and the screen flickers continuously.

Override the llm-agents pi package with a postInstall patch that adds
Loader.pause()/resume() and freezes the status loaders for the lifetime
of extension dialogs. Route home-manager, pim and the opencrow agents
through self.packages.pi so they all pick up the patched build.

Upstream: earendil-works/pi#3105 — drop the
override once a release contains the fix.
Mic92 added a commit to Mic92/dotfiles that referenced this pull request Apr 14, 2026
The questionnaire and permission-gate extensions can mount a ui.custom
component taller than the terminal, which scrolls the Working spinner
into native scrollback while it keeps ticking every 80ms. The TUI
differential renderer cannot reach those lines, so every tick triggers
a full clear+redraw and the screen flickers continuously.

Override the llm-agents pi package with a postInstall patch that adds
Loader.pause()/resume() and freezes the status loaders for the lifetime
of extension dialogs. Route home-manager, pim and the opencrow agents
through self.packages.pi so they all pick up the patched build.

Upstream: earendil-works/pi#3105 — drop the
override once a release contains the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants