diff --git a/packages/app/e2e/regression/command-palette.spec.ts b/packages/app/e2e/regression/command-palette.spec.ts index afe1d25b471..2687ce30d85 100644 --- a/packages/app/e2e/regression/command-palette.spec.ts +++ b/packages/app/e2e/regression/command-palette.spec.ts @@ -153,6 +153,12 @@ test("Search projects opens a new session's project picker from a session and fr await dialog.getByRole("option", { name: /^Search projects/ }).click() await expect(page).toHaveURL(/\/new-session\?draftId=.+$/) await expect(picker).toBeFocused() + // Just the projects: no "Add project", no worktrees and no key hints. + const projects = page.getByRole("dialog").getByRole("option") + await expect(projects).toHaveCount(1) + await expect(projects).toHaveText(/command-palette/) + await expect(projects.getByRole("button", { name: "Worktrees", exact: true })).toHaveCount(0) + await expect(page.locator(".new-session-location-hints")).toHaveCount(0) await picker.press("Escape") await expect(picker).toHaveCount(0) await expect(editor).toBeFocused() diff --git a/packages/app/src/new-session/location/dialog.tsx b/packages/app/src/new-session/location/dialog.tsx index 156f560b323..3f8924a6874 100644 --- a/packages/app/src/new-session/location/dialog.tsx +++ b/packages/app/src/new-session/location/dialog.tsx @@ -34,7 +34,8 @@ type LocationEntry = /** * Opens the new-session location dialog: one project list across every ready server, → on a Git project lists where - * the session can run, and → on "Add project" picks the server when there are several. + * the session can run, and → on "Add project" picks the server when there are several. Search projects passes + * `projectsOnly` for just the project list: no "Add project", no worktrees and no key hints, like the other palettes. */ export function useNewSessionLocationDialog(input: { project: PromptProjectController @@ -43,13 +44,14 @@ export function useNewSessionLocationDialog(input: { }) { const dialog = useDialog() - return (options?: { onWorkspace?: (value: string) => void }) => { + return (options?: { onWorkspace?: (value: string) => void; projectsOnly?: boolean }) => { void dialog.show( () => ( ), input.onDone, @@ -61,6 +63,7 @@ function NewSessionLocationDialog(props: { project: PromptProjectController workspace: NewSessionWorkspaceController onWorkspace?: (value: string) => void + projectsOnly?: boolean }) { const dialog = useDialog() const language = useLanguage() @@ -127,7 +130,7 @@ function NewSessionLocationDialog(props: { .projects() .filter((project) => matches(displayName(project), project.worktree, tagged(project.server)?.name)) .map((project) => ({ id: projectEntryID(project), type: "project" as const, project })), - addEntry, + ...(props.projectsOnly ? [] : [addEntry]), ]) const serverEntries = createMemo(() => @@ -163,7 +166,9 @@ function NewSessionLocationDialog(props: { return projectEntries() } - const pinned = () => !store.drilled && !store.picking + const pinned = () => !props.projectsOnly && !store.drilled && !store.picking + + const drillable = (project: PromptProject) => !props.projectsOnly && !!project.id && project.vcs === "git" const listEntries = () => entries().filter((entry) => entry.type !== "add") @@ -439,28 +444,30 @@ function NewSessionLocationDialog(props: { -
- - - {language.t("session.new.location.select")} - - - - {activeEntry()?.type === "add" && choosesServer() - ? language.t("session.new.location.servers") - : language.t("session.new.location.worktrees")} - - } - > + +
- - {language.t("session.new.location.back")} + + {language.t("session.new.location.select")} - -
+ + + {activeEntry()?.type === "add" && choosesServer() + ? language.t("session.new.location.servers") + : language.t("session.new.location.worktrees")} + + } + > + + + {language.t("session.new.location.back")} + + +
+ ) @@ -558,10 +565,6 @@ function WorkspaceRow(props: { title: string; value: string; description: string ) } -function drillable(project: PromptProject) { - return !!project.id && project.vcs === "git" -} - function projectEntryID(project: PromptProject) { return `project:${project.server?.key ?? ""}:${project.worktree}` } diff --git a/packages/app/src/new-session/screen.tsx b/packages/app/src/new-session/screen.tsx index 1e5a170886b..2ddd5a342b2 100644 --- a/packages/app/src/new-session/screen.tsx +++ b/packages/app/src/new-session/screen.tsx @@ -53,7 +53,7 @@ export default function NewSessionPage(props: { draftId: string }) { restoreFocus: model.restoreFocus, project: { empty: project.empty, - open: () => location(), + open: () => location({ projectsOnly: true }), }, workspace: { enabled: workspace.bar.visible, @@ -67,7 +67,7 @@ export default function NewSessionPage(props: { draftId: string }) { if (!ready) return // Search projects opened this draft; the picker returns focus to the composer when it closes. - if (layout.draft.projectSearch.take()) location() + if (layout.draft.projectSearch.take()) location({ projectsOnly: true }) else model.restoreFocus() }), ) diff --git a/packages/app/src/shell/titlebar/titlebar.tsx b/packages/app/src/shell/titlebar/titlebar.tsx index 93a594a8653..c67f64eb588 100644 --- a/packages/app/src/shell/titlebar/titlebar.tsx +++ b/packages/app/src/shell/titlebar/titlebar.tsx @@ -37,7 +37,7 @@ import { TitlebarItems, useTitlebarItems } from "@/runtime/extension/titlebar-it export const newTabCommand = { id: "tab.new", keybind: "mod+t,mod+n" } -/** A draft registers its own Search projects; everywhere else it opens a new draft with the project picker open. */ +/** Search projects: a draft searches in place; everywhere else it opens a new draft with the project picker open. */ export const projectSearchCommand = { id: "project.select", keybind: "mod+shift+o" } export const goBackCommand = { id: "common.goBack", keybind: "mod+[" } @@ -556,16 +556,19 @@ export function Titlebar(props: { verticalTabs?: { mount?: HTMLElement } }) { tabsStoreActions.closeTab(tabsStore.findIndex((tab) => current === tab)) }, }, - { - ...projectSearchCommand, - title: language.t("session.new.project.search"), - category: language.t("command.category.project"), - onSelect: () => { - layout.draft.projectSearch.request() + // A draft registers its own, which searches projects in place. + layout.route().type === "draft" + ? undefined + : { + ...projectSearchCommand, + title: language.t("session.new.project.search"), + category: language.t("command.category.project"), + onSelect: () => { + layout.draft.projectSearch.request() - if (!openNewTab()) layout.draft.projectSearch.take() - }, - }, + if (!openNewTab()) layout.draft.projectSearch.take() + }, + }, { id: "tab.reopenClosed", category: language.t("command.category.file"),