feat(app): add an Add project button to the location search

This commit is contained in:
Dax Raad
2026-10-25 11:40:12 -04:00
Unverified
parent 0997e371c0
commit b88fd27fec
4 changed files with 68 additions and 1 deletions
+1 -1
View File
@@ -224,11 +224,11 @@ test("Home lists the used server projects by recent activity, hides them, and li
await newSessionLocation(page).click()
const options = page.getByRole("dialog").getByRole("option")
await expect(options.locator(".command-palette-title")).toHaveText([
"Add project",
"oldest",
"fresh",
"unused",
"recent",
"Add project",
])
await options.filter({ hasText: "C:/OpenCode/recent" }).click()
await expect(newSessionLocation(page)).toContainText("recent")
@@ -590,6 +590,7 @@ test("the location dialog lists every ready server's projects in one list and ta
const paths = options.locator(".command-palette-description")
const chip = dialog.getByRole("button", { name: "Back to projects", exact: true })
const heading = dialog.getByText("Projects", { exact: true })
const addButton = dialog.getByRole("button", { name: "Add project", exact: true })
// Used projects first, then the rest, each by recent activity across servers; only remote ones carry a tag.
await expect(names).toHaveText(["opencodeapollo", "opencode", "console", "sstapollo"])
await expect(paths).toHaveText(["/home/dax/opencode", "C:/work/opencode", "C:/work/console", "/home/dax/sst"])
@@ -606,6 +607,10 @@ test("the location dialog lists every ready server's projects in one list and ta
})
.toBe(true)
await expect(rows.nth(0)).toHaveAttribute("aria-selected", "true")
// The search field also ends with an "Add project" button, next in the Tab order.
await page.keyboard.press("Tab")
await expect(addButton).toBeFocused()
await search.focus()
// The picker shares the palette's full-width search, compact rows, and single-line titles.
await expect(rows.nth(0)).toHaveCSS("height", "36px")
@@ -644,6 +649,7 @@ test("the location dialog lists every ready server's projects in one list and ta
await page.keyboard.press("ArrowRight")
// The worktree step has no "Add project".
await expect(add).toHaveCount(0)
await expect(addButton).toHaveCount(0)
await expect(chip).toContainText("opencode")
await expect(chip.locator('[data-component="tag"]')).toHaveText("apollo")
await expect(options.locator(".command-palette-title")).toHaveText(["opencode", "New worktree", "opencode-beta"])
@@ -672,6 +678,13 @@ test("the location dialog lists every ready server's projects in one list and ta
await page.keyboard.press("ArrowUp")
await page.keyboard.press("ArrowRight")
await expect(chip).toHaveText("Add project")
await expect(addButton).toHaveCount(0)
// The search field's button starts the same flow.
await page.keyboard.press("Backspace")
await expect(addButton).toBeVisible()
await page.keyboard.press("Tab")
await page.keyboard.press("Enter")
await expect(chip).toHaveText("Add project")
// The server the app runs beside shows its hostname and "This computer" instead of its address.
await expect(options).toHaveText(["hailmaryThis computer", "apollo"])
await options.filter({ hasText: "apollo" }).click()
@@ -66,3 +66,41 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.new-session-location-add {
display: flex;
height: 24px;
align-items: center;
gap: 4px;
padding: 0 8px 0 6px;
border-radius: 4px;
color: var(--v2-text-text-muted);
font-size: 13px;
font-weight: 440;
line-height: 16px;
white-space: nowrap;
outline: none;
}
.new-session-location-add:is(:hover, :focus-visible) {
background: var(--v2-overlay-simple-overlay-hover);
color: var(--v2-text-text-base);
}
.new-session-location-add:focus-visible {
outline: 2px solid var(--v2-border-border-focus);
outline-offset: -2px;
}
/* Narrow screens keep only the icon; the tooltip names it. */
@media (max-width: 480px) {
.new-session-location-add {
width: 24px;
justify-content: center;
padding: 0;
}
.new-session-location-add > span {
display: none;
}
}
@@ -11,6 +11,7 @@ import {
ProjectAvatar,
} from "@opencode/ui/project-avatar"
import { TextInput } from "@opencode/ui/text-input"
import { Tooltip } from "@opencode/ui/tooltip"
import { createTypeahead } from "@opencode/ui/typeahead"
import { getFilename } from "@opencode/util/path"
import { createEffect, createMemo, createResource, For, type JSX, Match, onCleanup, Show, Switch } from "solid-js"
@@ -418,6 +419,21 @@ function NewSessionLocationDialog(props: {
aria-activedescendant={typeahead.activeID()}
onInput={(event) => setStore("query", event.currentTarget.value)}
onKeyDown={handleKeyDown}
trailing={
<Show when={!props.projectsOnly && !store.drilled && !store.picking}>
<Tooltip placement="top" value={language.t("session.new.project.add")}>
<button
type="button"
class="new-session-location-add"
aria-label={language.t("session.new.project.add")}
onClick={() => choose(addEntry)}
>
<Icon name="plus" size="small" />
<span>{language.t("session.new.project.add")}</span>
</button>
</Tooltip>
</Show>
}
/>
</div>
<div class="command-palette-scroll" data-scrollable ref={(el) => (resultsRef = el)}>