diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 04c6b9945c8f..e7521dc6c47f 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -1,5 +1,5 @@ import { useDialog } from "@tui/ui/dialog" -import { DialogSelect } from "@tui/ui/dialog-select" +import { DialogSelect, type DialogSelectRef } from "@tui/ui/dialog-select" import { useRoute } from "@tui/context/route" import { useSync } from "@tui/context/sync" import { createMemo, createResource, createSignal, onMount } from "solid-js" @@ -31,7 +31,7 @@ export function DialogSessionList() { const toast = useToast() const [toDelete, setToDelete] = createSignal() const [search, setSearch] = createDebouncedSignal("", 150) - + const [selectRef, setSelectRef] = createSignal>() const [searchResults, { refetch }] = createResource( () => ({ query: search(), filter: sync.session.query() }), async (input) => { @@ -179,12 +179,14 @@ export function DialogSessionList() { return ( { + onMove={(option) => { + if (toDelete() === option.value) return setToDelete(undefined) }} onSelect={(option) => { @@ -200,8 +202,16 @@ export function DialogSessionList() { title: "delete", onTrigger: async (option) => { if (toDelete() === option.value) { + const ref = selectRef() + const currentIndex = ref?.filtered.findIndex((opt) => opt.value === option.value) ?? -1 + const adjacentID = + currentIndex < 0 + ? undefined + : currentIndex < (ref?.filtered.length ?? 0) - 1 + ? ref?.filtered[currentIndex + 1]?.value + : ref?.filtered[currentIndex - 1]?.value const session = sessions().find((item) => item.id === option.value) - const status = session?.workspaceID ? project.workspace.status(session.workspaceID) : undefined + const wsStatus = session?.workspaceID ? project.workspace.status(session.workspaceID) : undefined try { const result = await sdk.client.session.delete({ @@ -233,11 +243,21 @@ export function DialogSessionList() { setToDelete(undefined) return } - if (status && status !== "connected") { + if (ref) ref.skipAutoScroll = true + if (wsStatus && wsStatus !== "connected") { await sync.session.refresh() } if (search()) await refetch() setToDelete(undefined) + + if (ref && adjacentID) { + setTimeout(() => { + ref.scrollToValue(adjacentID, true) + }, 50) + } + setTimeout(() => { + if (ref) ref.skipAutoScroll = false + }, 100) return } setToDelete(option.value) 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 4d68c4430891..e878cafbe8a1 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -50,6 +50,9 @@ export interface DialogSelectOption { export type DialogSelectRef = { filter: string filtered: DialogSelectOption[] + moveTo: (index: number, center?: boolean) => void + scrollToValue: (value: T, center?: boolean) => void + skipAutoScroll: boolean } export function DialogSelect(props: DialogSelectProps) { @@ -147,7 +150,7 @@ export function DialogSelect(props: DialogSelectProps) { setTimeout(() => { if (filter.length > 0) { moveTo(0, true) - } else if (current) { + } else if (current && !ref.skipAutoScroll) { const currentIndex = flat().findIndex((opt) => isDeepEqual(opt.value, current)) if (currentIndex >= 0) { moveTo(currentIndex, true) @@ -226,12 +229,20 @@ export function DialogSelect(props: DialogSelectProps) { let scroll: ScrollBoxRenderable | undefined const ref: DialogSelectRef = { + skipAutoScroll: false, get filter() { return store.filter }, get filtered() { return filtered() }, + moveTo, + scrollToValue(value: T, center?: boolean) { + const index = flat().findIndex((opt) => isDeepEqual(opt.value, value)) + if (index >= 0) { + moveTo(index, center) + } + }, } props.ref?.(ref)