diff --git a/packages/ui/src/components/instance/instance-shell2.tsx b/packages/ui/src/components/instance/instance-shell2.tsx index ce98e7da..6972b022 100644 --- a/packages/ui/src/components/instance/instance-shell2.tsx +++ b/packages/ui/src/components/instance/instance-shell2.tsx @@ -56,6 +56,11 @@ import SessionView from "../session/session-view" import { formatTokenTotal } from "../../lib/formatters" import { sseManager } from "../../lib/sse-manager" import { getLogger } from "../../lib/logger" +import { + SESSION_SIDEBAR_EVENT, + type SessionSidebarRequestAction, + type SessionSidebarRequestDetail, +} from "../../lib/session-sidebar-events" const log = getLogger("session") @@ -217,6 +222,17 @@ const InstanceShell2: Component = (props) => { onCleanup(() => window.removeEventListener("resize", handleResize)) }) + onMount(() => { + if (typeof window === "undefined") return + const handler = (event: Event) => { + const detail = (event as CustomEvent).detail + if (!detail || detail.instanceId !== props.instance.id) return + handleSidebarRequest(detail.action) + } + window.addEventListener(SESSION_SIDEBAR_EVENT, handler) + onCleanup(() => window.removeEventListener(SESSION_SIDEBAR_EVENT, handler)) + }) + createEffect(() => { if (typeof window === "undefined") return window.localStorage.setItem(LEFT_DRAWER_STORAGE_KEY, sessionSidebarWidth().toString()) @@ -322,6 +338,58 @@ const InstanceShell2: Component = (props) => { ), ) + const runAfterDrawerReady = (callback: (() => void) | undefined, attempts = 12) => { + if (!callback) return + if (leftDrawerContentEl()) { + requestAnimationFrame(() => callback()) + return + } + if (attempts <= 0) return + requestAnimationFrame(() => runAfterDrawerReady(callback, attempts - 1)) + } + + const ensureLeftDrawerVisible = (callback?: () => void) => { + if (leftPinned()) { + runAfterDrawerReady(callback) + return + } + if (!leftOpen()) { + setLeftOpen(true) + measureDrawerHost() + } + runAfterDrawerReady(callback) + } + + const focusAgentSelectorControl = () => { + const agentTrigger = leftDrawerContentEl()?.querySelector("[data-agent-selector]") as HTMLElement | null + if (!agentTrigger) return + agentTrigger.focus() + setTimeout(() => agentTrigger.click(), 20) + } + + const focusModelSelectorControl = () => { + const trigger = leftDrawerContentEl()?.querySelector( + "[data-model-selector-control] .selector-trigger", + ) as HTMLElement | null + if (!trigger) return + trigger.focus() + setTimeout(() => trigger.click(), 20) + } + + const handleSidebarRequest = (action: SessionSidebarRequestAction) => { + if (action === "focus-agent-selector") { + ensureLeftDrawerVisible(() => focusAgentSelectorControl()) + return + } + if (action === "focus-model-selector") { + ensureLeftDrawerVisible(() => focusModelSelectorControl()) + return + } + if (action === "show-session-list") { + ensureLeftDrawerVisible() + } + } + const handleSessionSelect = (sessionId: string) => { setActiveSession(props.instance.id, sessionId) } diff --git a/packages/ui/src/lib/hooks/use-commands.ts b/packages/ui/src/lib/hooks/use-commands.ts index 197bd42f..7c2eedcb 100644 --- a/packages/ui/src/lib/hooks/use-commands.ts +++ b/packages/ui/src/lib/hooks/use-commands.ts @@ -18,9 +18,11 @@ import type { MessageRecord } from "../../stores/message-v2/types" import { messageStoreBus } from "../../stores/message-v2/bus" import { cleanupBlankSessions } from "../../stores/session-state" import { getLogger } from "../logger" +import { emitSessionSidebarRequest } from "../session-sidebar-events" const log = getLogger("actions") + export interface UseCommandsOptions { preferences: Accessor toggleShowThinkingBlocks: () => void @@ -191,7 +193,10 @@ export function useCommands(options: UseCommandsOptions) { if (ids.length <= 1) return const current = ids.indexOf(activeSessionMap().get(instanceId) || "") const next = (current + 1) % ids.length - if (ids[next]) setActiveSession(instanceId, ids[next]) + if (ids[next]) { + setActiveSession(instanceId, ids[next]) + emitSessionSidebarRequest({ instanceId, action: "show-session-list" }) + } }, }) @@ -212,7 +217,10 @@ export function useCommands(options: UseCommandsOptions) { if (ids.length <= 1) return const current = ids.indexOf(activeSessionMap().get(instanceId) || "") const prev = current <= 0 ? ids.length - 1 : current - 1 - if (ids[prev]) setActiveSession(instanceId, ids[prev]) + if (ids[prev]) { + setActiveSession(instanceId, ids[prev]) + emitSessionSidebarRequest({ instanceId, action: "show-session-list" }) + } }, }) @@ -345,21 +353,9 @@ export function useCommands(options: UseCommandsOptions) { keywords: ["model", "llm", "ai"], shortcut: { key: "M", meta: true, shift: true }, action: () => { - const modelInput = document.querySelector("[data-model-selector]") as HTMLInputElement - if (modelInput) { - modelInput.focus() - setTimeout(() => { - const event = new KeyboardEvent("keydown", { - key: "ArrowDown", - code: "ArrowDown", - keyCode: 40, - which: 40, - bubbles: true, - cancelable: true, - }) - modelInput.dispatchEvent(event) - }, 10) - } + const instance = activeInstance() + if (!instance) return + emitSessionSidebarRequest({ instanceId: instance.id, action: "focus-model-selector" }) }, }) @@ -371,21 +367,9 @@ export function useCommands(options: UseCommandsOptions) { keywords: ["agent", "mode"], shortcut: { key: "A", meta: true, shift: true }, action: () => { - const agentTrigger = document.querySelector("[data-agent-selector]") as HTMLElement - if (agentTrigger) { - agentTrigger.focus() - setTimeout(() => { - const event = new KeyboardEvent("keydown", { - key: "Enter", - code: "Enter", - keyCode: 13, - which: 13, - bubbles: true, - cancelable: true, - }) - agentTrigger.dispatchEvent(event) - }, 50) - } + const instance = activeInstance() + if (!instance) return + emitSessionSidebarRequest({ instanceId: instance.id, action: "focus-agent-selector" }) }, }) diff --git a/packages/ui/src/lib/session-sidebar-events.ts b/packages/ui/src/lib/session-sidebar-events.ts new file mode 100644 index 00000000..a8529a24 --- /dev/null +++ b/packages/ui/src/lib/session-sidebar-events.ts @@ -0,0 +1,13 @@ +export type SessionSidebarRequestAction = "focus-agent-selector" | "focus-model-selector" | "show-session-list" + +export interface SessionSidebarRequestDetail { + instanceId: string + action: SessionSidebarRequestAction +} + +export const SESSION_SIDEBAR_EVENT = "opencode:session-sidebar-request" + +export function emitSessionSidebarRequest(detail: SessionSidebarRequestDetail) { + if (typeof window === "undefined") return + window.dispatchEvent(new CustomEvent(SESSION_SIDEBAR_EVENT, { detail })) +}