From 35d5c24eae5fdbf46bb20deac6a0fd9adedec349 Mon Sep 17 00:00:00 2001 From: kami Date: Fri, 29 May 2026 17:48:27 +0400 Subject: [PATCH] chore: add TODO for unbounded scrollOffset in diff view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The diffScrollOffset in NavUp/NavDown has no upper bound — it can scroll past the last line. The ApprovalSurface clamps the viewport with coerceIn/minOf, so it doesn't crash, but the offset keeps growing. Marked with TODO(diff-scroll) in both handlers. --- .../main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt index 725ec8c8..70f124fa 100644 --- a/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt +++ b/apps/tui/src/main/kotlin/com/correx/apps/tui/reducer/RootReducer.kt @@ -194,6 +194,9 @@ object RootReducer { // Diff scrolling in APPROVAL mode when expanded. is Action.NavigateUp -> { if (withBgReset.displayState == DisplayState.APPROVAL && withBgReset.diffExpanded) { + // TODO(diff-scroll): cap scrollOffset at totalLines - PREVIEW_MAX_LINES to prevent + // scrolling past the end of the diff. Needs total line count in state or a clamp + // communicated from ApprovalSurface. withBgReset.copy(diffScrollOffset = maxOf(0, withBgReset.diffScrollOffset - 1)) } else if (withBgReset.displayState == DisplayState.IN_SESSION && prevInputMode == InputMode.ROUTER) { val selectedId = withBgReset.sessions.selectedId @@ -235,6 +238,8 @@ object RootReducer { // Diff scrolling in APPROVAL mode when expanded. is Action.NavigateDown -> { if (withBgReset.displayState == DisplayState.APPROVAL && withBgReset.diffExpanded) { + // TODO(diff-scroll): cap scrollOffset at totalLines - PREVIEW_MAX_LINES; same issue + // as NavigateUp above — the reducer doesn't know the diff length. withBgReset.copy(diffScrollOffset = withBgReset.diffScrollOffset + 1) } else if (withBgReset.displayState == DisplayState.IN_SESSION && prevInputMode == InputMode.ROUTER) { val selectedId = withBgReset.sessions.selectedId