mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 19:46:16 +03:00
feat(app): show recent sessions in the empty command palette (#53727)
Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
69219470a2
commit
68aa4f8017
@@ -38,6 +38,16 @@ test("home commands do not wait for session search", async ({ page }) => {
|
||||
release.resolve()
|
||||
})
|
||||
|
||||
test("lists recent sessions before a query is entered", async ({ page }) => {
|
||||
const { dialog } = await openCommandPalette(page, true)
|
||||
const session = dialog.getByRole("option", { name: /Palette fixture session/ })
|
||||
await expect(dialog.getByText("Recent sessions", { exact: true })).toBeVisible()
|
||||
await expect(session).toHaveAttribute("aria-selected", "false")
|
||||
await session.click()
|
||||
await expect(dialog).toHaveCount(0)
|
||||
await expect(page.getByRole("heading", { name: paletteSession.title, exact: true })).toBeVisible()
|
||||
})
|
||||
|
||||
test("appends search results without resetting the selected command", async ({ page }) => {
|
||||
const { dialog, input } = await openCommandPalette(page)
|
||||
const files = Promise.withResolvers<void>()
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
createCommandPaletteCommandEntry,
|
||||
createServerSessionEntries,
|
||||
type CommandPaletteEntry,
|
||||
type PaletteHighlight,
|
||||
} from "@/shell/commands/palette"
|
||||
import { CommandPaletteView, matchesCommandPaletteEntry } from "@/shell/commands/dialog"
|
||||
|
||||
@@ -20,7 +21,7 @@ export function HomeCommandPalette(props: {
|
||||
const global = useGlobal()
|
||||
const language = useLanguage()
|
||||
const server = global.ensureServerCtx(props.server)
|
||||
const state = { cleanup: undefined as (() => void) | void, committed: false }
|
||||
const state: PaletteHighlight = { cleanup: undefined, committed: false }
|
||||
|
||||
const commandEntries = createMemo(() => {
|
||||
const category = language.t("palette.group.commands")
|
||||
@@ -36,6 +37,7 @@ export function HomeCommandPalette(props: {
|
||||
get: (sessionID, signal) => server.sdk.api.session.get({ sessionID }, { signal }),
|
||||
untitled: () => language.t("command.session.new"),
|
||||
category: () => language.t("command.category.session"),
|
||||
recentCategory: () => language.t("palette.group.recentSessions"),
|
||||
})
|
||||
|
||||
const highlight = (item: CommandPaletteEntry | undefined) => {
|
||||
|
||||
@@ -86,6 +86,7 @@ export const dict = {
|
||||
"palette.empty": "No results found",
|
||||
"palette.group.commands": "Commands",
|
||||
"palette.group.files": "Files",
|
||||
"palette.group.recentSessions": "Recent sessions",
|
||||
|
||||
"dialog.provider.search.placeholder": "Search providers",
|
||||
"dialog.provider.empty": "No providers found",
|
||||
|
||||
@@ -73,7 +73,7 @@ export function createCommandPaletteFileOpener(onOpenFile?: (path: string) => vo
|
||||
}
|
||||
|
||||
/** The highlighted option's preview cleanup, and whether the palette committed a choice. */
|
||||
type PaletteHighlight = { cleanup: (() => void) | void; committed: boolean }
|
||||
export type PaletteHighlight = { cleanup: (() => void) | void; committed: boolean }
|
||||
|
||||
export function createCommandPaletteModel(props: { filesOnly?: () => boolean; onOpenFile?: (path: string) => void }) {
|
||||
const command = useCommand()
|
||||
@@ -148,6 +148,7 @@ export function createCommandPaletteModel(props: { filesOnly?: () => boolean; on
|
||||
get: (sessionID, signal) => serverSDK.api.session.get({ sessionID }, { signal }),
|
||||
untitled: () => language.t("command.session.new"),
|
||||
category: () => language.t("command.category.session"),
|
||||
recentCategory: () => language.t("palette.group.recentSessions"),
|
||||
})
|
||||
|
||||
const highlight = (item: CommandPaletteEntry | undefined) => {
|
||||
@@ -232,6 +233,7 @@ export function createServerSessionEntries(props: {
|
||||
get: (sessionID: string, signal: AbortSignal) => Promise<SessionInfo>
|
||||
untitled: () => string
|
||||
category: () => string
|
||||
recentCategory: () => string
|
||||
}) {
|
||||
let abort: AbortController | undefined
|
||||
|
||||
@@ -240,17 +242,13 @@ export function createServerSessionEntries(props: {
|
||||
return async (text: string): Promise<CommandPaletteEntry[]> => {
|
||||
const search = text.trim()
|
||||
|
||||
if (!search) {
|
||||
abort?.abort()
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
abort?.abort()
|
||||
const current = new AbortController()
|
||||
abort = current
|
||||
|
||||
// Typed searches wait for a pause; an empty query lists recent sessions right away.
|
||||
await new Promise<void>((resolve) => {
|
||||
const timer = setTimeout(resolve, 100)
|
||||
const timer = setTimeout(resolve, search ? 100 : 0)
|
||||
current.signal.addEventListener(
|
||||
"abort",
|
||||
() => {
|
||||
@@ -264,8 +262,9 @@ export function createServerSessionEntries(props: {
|
||||
if (current.signal.aborted) return []
|
||||
const opened = props.opened()
|
||||
const stored = props.stored().map((project) => ({ ...project, expanded: false }))
|
||||
const category = search ? props.category() : props.recentCategory()
|
||||
|
||||
return Promise.all([
|
||||
const entries = await Promise.all([
|
||||
props.load(search, current.signal).then(
|
||||
(result) => result.data,
|
||||
() => [],
|
||||
@@ -289,7 +288,7 @@ export function createServerSessionEntries(props: {
|
||||
type: "session" as const,
|
||||
title: session.title || props.untitled(),
|
||||
description: project ? displayName(project) : getFilename(session.location.directory),
|
||||
category: props.category(),
|
||||
category,
|
||||
directory: session.location.directory,
|
||||
sessionID: session.id,
|
||||
server: props.server,
|
||||
@@ -300,5 +299,7 @@ export function createServerSessionEntries(props: {
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
return search ? entries : entries.slice(0, ENTRY_LIMIT)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user