From b7c3c30c8c8e4c4c679e7a3e1847dce00dd60670 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 23 Mar 2026 16:59:48 +0100 Subject: [PATCH] fix: send Ctrl+L after tab switch to clear stale Ink CUP frames Tailed terminal buffers contain multiple CUP-positioned Ink frames from different time points. When replayed in xterm, old frames at viewport positions not covered by the latest frame persist as ghost content (e.g. duplicate "bypass permissions" bars). After buffer load, send Ctrl+L via the session input API to trigger a full Ink redraw, which overwrites all stale frame content with the correct current state. Co-Authored-By: Claude Opus 4.6 --- src/web/public/app.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/web/public/app.js b/src/web/public/app.js index 6bc54847..17f1322a 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -2240,9 +2240,19 @@ class CodemanApp { }); } - // Fire-and-forget resize — don't await to avoid blocking UI. - // The resize triggers an Ink redraw in Claude which streams back via SSE. - this.sendResize(sessionId); + // Fire-and-forget resize + Ctrl+L to force Ink redraw. + // Tailed buffers accumulate stale CUP-positioned Ink frames that overlap + // in the viewport (e.g. duplicate "bypass permissions" bars). Ctrl+L + // triggers a full Ink redraw which overwrites all stale frame content. + // sendResize may be a no-op if dimensions match, so Ctrl+L is essential. + this.sendResize(sessionId).then(() => { + if (selectGen !== this._selectGeneration) return; + fetch(`/api/sessions/${sessionId}/input`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ input: '\x0c' }) + }).catch(() => {}); + }); // Defer secondary panel updates so they don't block the main thread // after terminal content is already visible.