mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
fix(app): focus session search when the Home shortcut opens Home (#53761)
Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
c9e1ed7080
commit
a80ca68c96
@@ -1,9 +1,9 @@
|
||||
import { expect, test, type Page } from "@playwright/test"
|
||||
import { holdRoute, NO_PROVIDER, project, REMOTE_SERVER, seed, sessionHref } from "../utils/app"
|
||||
import { expectPath, holdRoute, NO_PROVIDER, project, REMOTE_SERVER, seed, sessionHref } from "../utils/app"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
import { fixture, mockStressTimeline } from "../utils/session-fixture"
|
||||
import { mockRemoteServer } from "../utils/workspace"
|
||||
import { expectAppVisible } from "../utils/waits"
|
||||
import { APP_READY_TIMEOUT, expectAppVisible } from "../utils/waits"
|
||||
|
||||
test.use({ serviceWorkers: "block" })
|
||||
|
||||
@@ -77,6 +77,33 @@ test("the session context menu renames, exports, and deletes a Home session", as
|
||||
await expect(renamedRow).toBeHidden()
|
||||
})
|
||||
|
||||
test("the Home shortcut focuses session search, and the Home button leaves focus alone", async ({ page }) => {
|
||||
await mockStressTimeline(page)
|
||||
await seed(page, {
|
||||
projects: { local: [{ worktree: fixture.directory, expanded: true }] },
|
||||
lastProject: { local: fixture.directory },
|
||||
tabs: [fixture.sourceID],
|
||||
})
|
||||
await page.goto(sessionHref(fixture.sourceID))
|
||||
const editor = page.locator('[data-component="composer-editor"]')
|
||||
await expect(editor).toBeEditable({ timeout: APP_READY_TIMEOUT })
|
||||
await editor.click()
|
||||
|
||||
await page.keyboard.press("ControlOrMeta+b")
|
||||
const search = page.getByRole("textbox", { name: /Search sessions/ })
|
||||
await expect(search).toBeFocused()
|
||||
await page.keyboard.type("jump")
|
||||
await expect(page.getByRole("option")).toHaveCount(1)
|
||||
await expect(page.getByRole("option")).toContainText(fixture.expected.targetTitle)
|
||||
|
||||
await page.keyboard.press("ControlOrMeta+b")
|
||||
await expectPath(page, sessionHref(fixture.sourceID))
|
||||
|
||||
await page.getByRole("button", { name: "Home", exact: true }).click()
|
||||
await expect(search).toHaveValue("")
|
||||
await expect(search).not.toBeFocused()
|
||||
})
|
||||
|
||||
test("Home shows loaded sessions before the location request resolves", async ({ page }) => {
|
||||
const location = await holdRoute(page, (url) => url.pathname === "/api/location")
|
||||
await openHome(page)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useCommand } from "@/shell/commands/command"
|
||||
import { useLayout } from "@/shell/state/layout"
|
||||
import { useLanguage } from "@/runtime/i18n/language"
|
||||
import { serverName } from "@/runtime/server/registry"
|
||||
import { displayName } from "@opencode/ui/project-avatar"
|
||||
import { sessionLabel } from "@/session/title"
|
||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||
import { createMemo, onCleanup } from "solid-js"
|
||||
import { createMemo, onCleanup, onMount } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import type { HomeController } from "../model"
|
||||
import { homeSessionSearchKey, type HomeSessionRecord, type HomeSessionsController } from "./controller"
|
||||
@@ -15,12 +16,19 @@ type HomeSessionSearchSource = Pick<HomeSessionsController, "data" | "session">
|
||||
export function createHomeSessionSearchController(home: HomeController, sessions: HomeSessionSearchSource) {
|
||||
const command = useCommand()
|
||||
const language = useLanguage()
|
||||
const layout = useLayout()
|
||||
|
||||
const [state, setState] = createStore({
|
||||
const [state, setState] = createStore<{
|
||||
value: string
|
||||
focused: boolean
|
||||
highlighted: string
|
||||
exact: HomeSessionRecord | undefined
|
||||
lookingUp: boolean
|
||||
}>({
|
||||
value: "",
|
||||
focused: false,
|
||||
highlighted: "",
|
||||
exact: undefined as HomeSessionRecord | undefined,
|
||||
exact: undefined,
|
||||
lookingUp: false,
|
||||
})
|
||||
|
||||
@@ -78,6 +86,9 @@ export function createHomeSessionSearchController(home: HomeController, sessions
|
||||
}),
|
||||
)
|
||||
onCleanup(() => lookup++)
|
||||
onMount(() => {
|
||||
if (layout.home.searchFocus.take()) focus()
|
||||
})
|
||||
|
||||
command.register("home.search", () => [
|
||||
{
|
||||
|
||||
@@ -214,6 +214,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
sessionTabPreview: {},
|
||||
})
|
||||
|
||||
// Opening Home with its shortcut focuses session search, so typing filters immediately.
|
||||
let homeSearchFocus = false
|
||||
|
||||
// Names of other session-scoped stores, e.g. extension storage, so pruning drops them with the layout state.
|
||||
const [scoped, setScoped, , scopedReady] = persisted(
|
||||
Persist.global("layout.scoped"),
|
||||
@@ -355,6 +358,17 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
|
||||
setSelection(selection: HomeProjectSelection) {
|
||||
setStore("home", "selection", reconcile(selection))
|
||||
},
|
||||
searchFocus: {
|
||||
request() {
|
||||
homeSearchFocus = true
|
||||
},
|
||||
take() {
|
||||
const requested = homeSearchFocus
|
||||
homeSearchFocus = false
|
||||
|
||||
return requested
|
||||
},
|
||||
},
|
||||
},
|
||||
sessionState: {
|
||||
/** Records a session-scoped store, so pruning a session's layout state drops it too. */
|
||||
|
||||
@@ -452,7 +452,10 @@ export function Titlebar(props: { verticalTabs?: { mount?: HTMLElement } }) {
|
||||
category: language.t("command.category.view"),
|
||||
keybind: windows() ? "alt+home" : "mod+b",
|
||||
hidden: true,
|
||||
onSelect: toggleHome,
|
||||
onSelect: () => {
|
||||
if (layout.route().type !== "home") layout.home.searchFocus.request()
|
||||
toggleHome()
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user