From e2724636d08ef0dbb488422fd2eb866878b8584f Mon Sep 17 00:00:00 2001 From: pfergi42 Date: Fri, 20 Mar 2026 09:18:51 -0700 Subject: [PATCH] tui: refine transcript wheel scrolling --- src/tui/components/chat-log.ts | 19 ++++++++++++++----- src/tui/tui.ts | 5 +++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/tui/components/chat-log.ts b/src/tui/components/chat-log.ts index 1f6d8832109..c8b595a8b3d 100644 --- a/src/tui/components/chat-log.ts +++ b/src/tui/components/chat-log.ts @@ -10,6 +10,7 @@ export class ChatLog extends Container { private readonly maxComponents: number; private viewportHeight: number | null = null; private scrollOffset = 0; + private lastRenderWidth = 120; private toolById = new Map(); private streamingRuns = new Map(); private btwMessage: BtwInlineMessage | null = null; @@ -68,13 +69,19 @@ export class ChatLog extends Container { scrollPageUp() { const page = this.viewportHeight ?? 10; - this.scrollOffset += page; - this.clampScrollOffset(); + this.scrollLines(page); } scrollPageDown() { const page = this.viewportHeight ?? 10; - this.scrollOffset -= page; + this.scrollLines(-page); + } + + scrollLines(delta: number) { + if (!Number.isFinite(delta) || delta === 0) { + return; + } + this.scrollOffset += Math.trunc(delta); this.clampScrollOffset(); } @@ -93,7 +100,7 @@ export class ChatLog extends Container { return Math.max(0, this.getRenderedLineCount(width) - this.viewportHeight); } - private clampScrollOffset(width = 120) { + private clampScrollOffset(width = this.lastRenderWidth) { this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, this.getMaxScrollOffset(width))); } @@ -235,13 +242,15 @@ export class ChatLog extends Container { } override render(width: number) { + this.lastRenderWidth = width; const lines = super.render(width); if (!this.viewportHeight || lines.length <= this.viewportHeight) { this.scrollOffset = 0; return lines; } - this.clampScrollOffset(width); + const maxOffset = Math.max(0, lines.length - this.viewportHeight); + this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxOffset)); const start = Math.max(0, lines.length - this.viewportHeight - this.scrollOffset); const end = start + this.viewportHeight; return lines.slice(start, end); diff --git a/src/tui/tui.ts b/src/tui/tui.ts index aa9c0a2daad..f66ce240042 100644 --- a/src/tui/tui.ts +++ b/src/tui/tui.ts @@ -560,9 +560,9 @@ export async function runTui(opts: TuiOptions) { return undefined; } if (mouse.direction === "up") { - chatLog.scrollPageUp(); + chatLog.scrollLines(3); } else { - chatLog.scrollPageDown(); + chatLog.scrollLines(-3); } tui.requestRender(); return { consume: true }; @@ -917,6 +917,7 @@ export async function runTui(opts: TuiOptions) { return; } exitRequested = true; + tui.terminal.write("\x1b[?1000l\x1b[?1002l\x1b[?1006l"); client.stop(); stopTuiSafely(() => tui.stop()); process.exit(0);