From 5581d56e619237ca98809936c98b56b40f8b3204 Mon Sep 17 00:00:00 2001 From: James Murdza Date: Thu, 21 May 2026 18:23:30 +0530 Subject: [PATCH] feat(workspaces): surface session directory filter in list dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The session list silently filters to the current workspace's directory when the filter is enabled, with no UI indication of why sessions are missing. The existing toggle is buried in the command palette under `app.toggle.session_directory_filter` with no default keybind. - Show the filter state in the dialog title: `Sessions · current directory` vs `Sessions · all directories`. - Add a dialog-scoped action `session.list.toggle_filter` bound to `f` so users can flip the filter without leaving the list. The footer label flips between `show all` and `filter to dir` to match the action it will take. - Let dialog-select actions opt out of requiring a selected row (`requireSelection: false`) so the toggle works even when the filter has emptied the list — the exact case this feature addresses. The default of `session_directory_filter_enabled = true` is preserved — only the visibility and reachability change. Co-Authored-By: Claude Opus 4.7 Signed-off-by: James Murdza --- .../cmd/tui/component/dialog-session-list.tsx | 18 +++++++++++++++++- .../opencode/src/cli/cmd/tui/config/keybind.ts | 2 ++ .../src/cli/cmd/tui/ui/dialog-select.tsx | 6 ++++-- 3 files changed, 23 insertions(+), 3 deletions(-) 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 17653af6b9a9..4cbf62c7d048 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 @@ -7,6 +7,7 @@ import { Locale } from "@/util/locale" import { useProject } from "@tui/context/project" import { useTheme } from "../context/theme" import { useSDK } from "../context/sdk" +import { useKV } from "../context/kv" import { useLocal } from "../context/local" import { Flag } from "@opencode-ai/core/flag/flag" import { DialogSessionRename } from "./dialog-session-rename" @@ -27,7 +28,9 @@ export function DialogSessionList() { const { theme } = useTheme() const sdk = useSDK() const local = useLocal() + const kv = useKV() const toast = useToast() + const filterEnabled = createMemo(() => kv.get("session_directory_filter_enabled", true) as boolean) const [toDelete, setToDelete] = createSignal() const [search, setSearch] = createDebouncedSignal("", 150) const deleteHint = useCommandShortcut("session.delete") @@ -218,7 +221,7 @@ export function DialogSessionList() { return ( ) }, }, + { + command: "session.list.toggle_filter", + // Acts on dialog state, not a row, so it must work even when the + // filter has emptied the list (the case this feature exists for). + requireSelection: false, + get title() { + return filterEnabled() ? "show all" : "filter to dir" + }, + onTrigger: async () => { + kv.set("session_directory_filter_enabled", !filterEnabled()) + await sync.session.refresh() + }, + }, ]} footerHints={quickSwitchFooterHints()} /> diff --git a/packages/opencode/src/cli/cmd/tui/config/keybind.ts b/packages/opencode/src/cli/cmd/tui/config/keybind.ts index c03123aed1c0..019d80afed75 100644 --- a/packages/opencode/src/cli/cmd/tui/config/keybind.ts +++ b/packages/opencode/src/cli/cmd/tui/config/keybind.ts @@ -56,6 +56,7 @@ export const Definitions = { app_toggle_diffwrap: keybind("none", "Toggle diff wrapping"), app_toggle_paste_summary: keybind("none", "Toggle paste summary"), app_toggle_session_directory_filter: keybind("none", "Toggle session directory filtering"), + session_list_toggle_filter: keybind("f", "Toggle directory filter in session list"), command_list: keybind("ctrl+p", "List available commands"), help_show: keybind("none", "Open help dialog"), docs_open: keybind("none", "Open documentation"), @@ -255,6 +256,7 @@ export const CommandMap = { app_toggle_diffwrap: "app.toggle.diffwrap", app_toggle_paste_summary: "app.toggle.paste_summary", app_toggle_session_directory_filter: "app.toggle.session_directory_filter", + session_list_toggle_filter: "session.list.toggle_filter", command_list: "command.palette.show", help_show: "help.show", docs_open: "docs.open", 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 700735d38cbf..7ba6bbb13fda 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -36,6 +36,8 @@ export interface DialogSelectProps { title: string side?: "left" | "right" disabled?: boolean + /** When false, the action fires even with no row selected (e.g. an empty list). Defaults to requiring a selection. */ + requireSelection?: boolean onTrigger: (option: DialogSelectOption) => void }[] footerHints?: { @@ -304,8 +306,8 @@ export function DialogSelect(props: DialogSelectProps) { run() { setStore("input", "keyboard") const option = selected() - if (!option) return - item.onTrigger(option) + if (item.requireSelection !== false && !option) return + item.onTrigger(option as DialogSelectOption) }, })), ],