From 1a7aefcbae9614287d42e0f00d4837c7daa1b51d Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Fri, 9 Jan 2026 16:34:44 +0000 Subject: [PATCH] feat(ui): session nav follows visible list Cmd+Shift+[ and Cmd+Shift+] now cycle through visible sessions only (parents + expanded children) and no longer include Instance Info. Sidebar session list auto-scrolls to keep the active session row in view. --- .../components/instance/instance-shell2.tsx | 6 - packages/ui/src/components/session-list.tsx | 140 +++++++++--------- packages/ui/src/lib/hooks/use-commands.ts | 43 +++--- packages/ui/src/lib/shortcuts/navigation.ts | 53 +++---- packages/ui/src/stores/session-state.ts | 95 +++++++++++- packages/ui/src/stores/sessions.ts | 14 +- 6 files changed, 217 insertions(+), 134 deletions(-) diff --git a/packages/ui/src/components/instance/instance-shell2.tsx b/packages/ui/src/components/instance/instance-shell2.tsx index 421376cd..1a1a83f3 100644 --- a/packages/ui/src/components/instance/instance-shell2.tsx +++ b/packages/ui/src/components/instance/instance-shell2.tsx @@ -869,12 +869,6 @@ const InstanceShell2: Component = (props) => { threads={sessionThreads()} activeSessionId={activeSessionIdForInstance()} onSelect={handleSessionSelect} - onClose={(id) => { - const result = props.onCloseSession(id) - if (result instanceof Promise) { - void result.catch((error) => log.error("Failed to close session:", error)) - } - }} onNew={() => { const result = props.onNewSession() if (result instanceof Promise) { diff --git a/packages/ui/src/components/session-list.tsx b/packages/ui/src/components/session-list.tsx index ba1ba8fb..5a165640 100644 --- a/packages/ui/src/components/session-list.tsx +++ b/packages/ui/src/components/session-list.tsx @@ -1,15 +1,22 @@ -import { Component, For, Show, createSignal, createMemo, createEffect, JSX } from "solid-js" +import { Component, For, Show, createSignal, createMemo, createEffect, JSX, onCleanup } from "solid-js" import type { Session, SessionStatus } from "../types/session" import type { SessionThread } from "../stores/session-state" import { getSessionStatus } from "../stores/session-status" -import { Bot, User, Info, X, Copy, Trash2, Pencil, ShieldAlert, ChevronDown } from "lucide-solid" +import { Bot, User, Info, Copy, Trash2, Pencil, ShieldAlert, ChevronDown } from "lucide-solid" import KeyboardHint from "./keyboard-hint" import Kbd from "./kbd" import SessionRenameDialog from "./session-rename-dialog" import { keyboardRegistry } from "../lib/keyboard-registry" import { formatShortcut } from "../lib/keyboard-utils" import { showToastNotification } from "../lib/notifications" -import { deleteSession, loading, renameSession } from "../stores/sessions" +import { + deleteSession, + ensureSessionParentExpanded, + isSessionParentExpanded, + loading, + renameSession, + toggleSessionParentExpanded, +} from "../stores/sessions" import { getLogger } from "../lib/logger" import { copyToClipboard } from "../lib/clipboard" const log = getLogger("session") @@ -22,7 +29,6 @@ interface SessionListProps { threads: SessionThread[] activeSessionId: string | null onSelect: (sessionId: string) => void - onClose: (sessionId: string) => void onNew: () => void showHeader?: boolean showFooter?: boolean @@ -41,24 +47,6 @@ function formatSessionStatus(status: SessionStatus): string { } } -function arraysEqual(prev: readonly string[] | undefined, next: readonly string[]): boolean { - if (!prev) { - return false - } - - if (prev.length !== next.length) { - return false - } - - for (let i = 0; i < prev.length; i++) { - if (prev[i] !== next[i]) { - return false - } - } - - return true -} - const SessionList: Component = (props) => { const [renameTarget, setRenameTarget] = createSignal<{ id: string; title: string; label: string } | null>(null) const [isRenaming, setIsRenaming] = createSignal(false) @@ -69,35 +57,13 @@ const SessionList: Component = (props) => { return deleting ? deleting.has(sessionId) : false } - const [expandedParents, setExpandedParents] = createSignal>(new Set()) - - const toggleParentExpanded = (parentId: string) => { - setExpandedParents((prev) => { - const next = new Set(prev) - if (next.has(parentId)) { - next.delete(parentId) - } else { - next.add(parentId) - } - return next - }) - } - - const ensureParentExpanded = (parentId: string) => { - setExpandedParents((prev) => { - if (prev.has(parentId)) return prev - const next = new Set(prev) - next.add(parentId) - return next - }) - } const selectSession = (sessionId: string) => { if (sessionId !== "info") { const session = props.sessions.get(sessionId) const parentId = session?.parentId ?? session?.id if (parentId) { - ensureParentExpanded(parentId) + ensureSessionParentExpanded(props.instanceId, parentId) } } @@ -162,7 +128,6 @@ const SessionList: Component = (props) => { const SessionRow: Component<{ sessionId: string - canClose?: boolean isChild?: boolean isLastChild?: boolean hasChildren?: boolean @@ -186,6 +151,7 @@ const SessionList: Component = (props) => {