From 55e80b0d3064e4cbc9fdbf41f7dc2c325a48bf3f Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Wed, 14 Jan 2026 18:30:46 -0500 Subject: [PATCH 1/2] fix: center selected item in viewport when dialog opens - Add 'center' parameter to moveTo() function for viewport centering - Use setTimeout to defer scroll until component is rendered - Pass center=true from filter/current effect for initial positioning - Keyboard navigation retains edge-scroll behavior for smooth UX This fixes an issue where the selected item could be outside the visible viewport when opening dialogs like the session list. --- .../src/cli/cmd/tui/ui/dialog-select.tsx | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 98adcdeb135..21cbb2928bf 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -109,15 +109,16 @@ export function DialogSelect(props: DialogSelectProps) { createEffect( on([() => store.filter, () => props.current], ([filter, current]) => { - if (filter.length > 0) { - setStore("selected", 0) - } else if (current) { - const currentIndex = flat().findIndex((opt) => isDeepEqual(opt.value, current)) - if (currentIndex >= 0) { - setStore("selected", currentIndex) + setTimeout(() => { + if (filter.length > 0) { + moveTo(0, true) + } else if (current) { + const currentIndex = flat().findIndex((opt) => isDeepEqual(opt.value, current)) + if (currentIndex >= 0) { + moveTo(currentIndex, true) + } } - } - scroll?.scrollTo(0) + }, 0) }), ) @@ -129,7 +130,7 @@ export function DialogSelect(props: DialogSelectProps) { moveTo(next) } - function moveTo(next: number) { + function moveTo(next: number, center = false) { setStore("selected", next) props.onMove?.(selected()!) if (!scroll) return @@ -138,13 +139,18 @@ export function DialogSelect(props: DialogSelectProps) { }) if (!target) return const y = target.y - scroll.y - if (y >= scroll.height) { - scroll.scrollBy(y - scroll.height + 1) - } - if (y < 0) { - scroll.scrollBy(y) - if (isDeepEqual(flat()[0].value, selected()?.value)) { - scroll.scrollTo(0) + if (center) { + const centerOffset = Math.floor(scroll.height / 2) + scroll.scrollBy(y - centerOffset) + } else { + if (y >= scroll.height) { + scroll.scrollBy(y - scroll.height + 1) + } + if (y < 0) { + scroll.scrollBy(y) + if (isDeepEqual(flat()[0].value, selected()?.value)) { + scroll.scrollTo(0) + } } } } From f2fef5cf7128191198b7025ebdb0f32869d6274e Mon Sep 17 00:00:00 2001 From: Ariane Emory Date: Wed, 14 Jan 2026 23:27:19 -0500 Subject: [PATCH 2/2] feat: always center viewport on keyboard navigation in selection dialogs Centers the viewport on the selected item whenever keyboard navigation (up/down arrows, page up/down) is used in any DialogSelect-based modal. Mouse hover retains edge-scroll behavior to avoid jumpy interactions. Applies to: session list, command list, timeline, model selection, theme list, and all other selection dialogs. --- packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 21cbb2928bf..d4f25b66d1d 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -127,7 +127,7 @@ export function DialogSelect(props: DialogSelectProps) { let next = store.selected + direction if (next < 0) next = flat().length - 1 if (next >= flat().length) next = 0 - moveTo(next) + moveTo(next, true) } function moveTo(next: number, center = false) {