fix(app): keep recent project picker open

This commit is contained in:
Aiden Cline
2026-08-17 09:32:01 +00:00
parent 5deb0b4fc8
commit d6b2e154ef
6 changed files with 64 additions and 55 deletions
+4
View File
@@ -138,6 +138,9 @@ function createServerController(
limit: RECENTLY_CLOSED_DISPLAY_LIMIT,
}).map((worktree) => enrich({ worktree, expanded: false }))
})
const recentProjectsList = createMemo(() =>
[...recentlyClosedList(), ...knownProjectsList()].slice(0, RECENTLY_CLOSED_DISPLAY_LIMIT),
)
const isLocal =
(conn?.type === "sidecar" && conn.variant === "base") || (conn?.type === "http" && isLocalHost(conn.http.url))
@@ -151,6 +154,7 @@ function createServerController(
list: projectsList,
recentlyClosed: recentlyClosedList,
known: knownProjectsList,
recent: recentProjectsList,
},
permission,
notification,
+1 -2
View File
@@ -637,8 +637,7 @@ export const dict = {
"home.title": "Home",
"home.projects": "Projects",
"home.project.add": "Add project",
"home.recentlyClosed": "Recently closed",
"home.knownProjects": "Known projects",
"home.recentlyClosed": "Recent projects",
"home.server.collapse": "Collapse server projects",
"home.server.expand": "Expand server projects",
"home.sessions.search.placeholder": "Search sessions",
@@ -63,8 +63,7 @@ export function createHomeController() {
selected: selectedProject,
newSession: newSessionProject,
forServer: (conn: ServerConnection.Any) => global.ensureServerCtx(conn).projects.list(),
recentlyClosedForServer: (conn: ServerConnection.Any) => global.ensureServerCtx(conn).projects.recentlyClosed(),
knownForServer: (conn: ServerConnection.Any) => global.ensureServerCtx(conn).projects.known(),
recentForServer: (conn: ServerConnection.Any) => global.ensureServerCtx(conn).projects.recent(),
homedirForServer: (conn: ServerConnection.Any) => global.ensureServerCtx(conn).sync.data.path.home,
select: (conn: ServerConnection.Any, directory: string) => {
const key = ServerConnection.key(conn)
@@ -68,8 +68,7 @@ export function createHomeProjectsController(home: HomeController) {
},
project: {
list: home.project.list,
recentlyClosedForServer: home.project.recentlyClosedForServer,
knownForServer: home.project.knownForServer,
recentForServer: home.project.recentForServer,
homedirForServer: home.project.homedirForServer,
select: home.project.select,
add: home.project.add,
@@ -34,8 +34,8 @@ export type HomeProjectsViewProps = {
selection: HomeProjectSelection
serverHealth: (server: ServerConnection.Any) => ServerHealth | undefined
projectsForServer: (server: ServerConnection.Any) => LocalProject[]
recentlyClosedForServer: (server: ServerConnection.Any) => LocalProject[]
knownForServer: (server: ServerConnection.Any) => LocalProject[]
recentForServer: (server: ServerConnection.Any) => LocalProject[]
showRecentForServer: (server: ServerConnection.Any) => boolean
homedirForServer: (server: ServerConnection.Any) => string
collapsed: (server: ServerConnection.Any) => boolean
canDefaultServer: boolean
@@ -85,11 +85,7 @@ export function HomeProjectsView(props: HomeProjectsViewProps) {
<Show
when={
props.servers.length === 1 &&
!(
props.projects.length === 0 &&
(props.recentlyClosedForServer(props.servers[0]).length > 0 ||
props.knownForServer(props.servers[0]).length > 0)
)
!(props.showRecentForServer(props.servers[0]) && props.recentForServer(props.servers[0]).length > 0)
}
>
<TooltipV2 placement="bottom" value={props.language.t("home.project.add")}>
@@ -112,13 +108,12 @@ export function HomeProjectsView(props: HomeProjectsViewProps) {
fallback={
<div class="pr-3">
<Show
when={props.projects.length > 0}
when={!props.showRecentForServer(props.servers[0]) && props.projects.length > 0}
fallback={
<HomeProjectEmpty
{...props}
server={props.servers[0]}
recentlyClosed={props.recentlyClosedForServer(props.servers[0])}
known={props.knownForServer(props.servers[0])}
items={props.recentForServer(props.servers[0])}
/>
}
>
@@ -131,11 +126,11 @@ export function HomeProjectsView(props: HomeProjectsViewProps) {
<For each={props.servers}>
{(item) => {
const projects = () => props.projectsForServer(item)
const recentlyClosed = () => props.recentlyClosedForServer(item)
const known = () => props.knownForServer(item)
const recent = () => props.recentForServer(item)
const healthy = () => !!props.serverHealth(item)?.healthy
const hasProjects = () => projects().length > 0
const hasChildren = () => hasProjects() || recentlyClosed().length > 0 || known().length > 0
const showRecent = () => props.showRecentForServer(item)
const hasChildren = () => hasProjects() || recent().length > 0
const collapsed = () => props.collapsed(item)
return (
<div class="flex min-w-0 flex-col gap-1">
@@ -151,15 +146,8 @@ export function HomeProjectsView(props: HomeProjectsViewProps) {
<Show when={healthy() && hasChildren() && !collapsed()}>
<div class="mx-3 h-px bg-v2-border-border-base" />
<Show
when={hasProjects()}
fallback={
<HomeProjectEmpty
{...props}
server={item}
recentlyClosed={recentlyClosed()}
known={known()}
/>
}
when={!showRecent() && hasProjects()}
fallback={<HomeProjectEmpty {...props} server={item} items={recent()} />}
>
<HomeProjectList {...props} {...contextMenuProps} server={item} items={projects()} />
</Show>
@@ -415,8 +403,7 @@ function HomeProjectSlot(
function HomeProjectEmpty(
props: HomeProjectsViewProps & {
server: ServerConnection.Any
recentlyClosed: LocalProject[]
known: LocalProject[]
items: LocalProject[]
},
) {
const unreachable = () => props.serverHealth(props.server)?.healthy === false
@@ -432,20 +419,12 @@ function HomeProjectEmpty(
<IconV2 name="folder-add-left" size="small" />
<span class={HOME_PROJECT_NAV_LABEL}>{props.language.t("home.project.add")}</span>
</HomeProjectNavButton>
<Show when={props.recentlyClosed.length > 0}>
<Show when={props.items.length > 0}>
<div class="mt-3 flex h-7 min-w-0 shrink-0 items-center pl-1.5 pr-3">
<div class="text-v2-text-text-faint [font-weight:530]">{props.language.t("home.recentlyClosed")}</div>
</div>
<For each={props.recentlyClosed}>
{(project) => <HomeSuggestedProjectRow {...props} project={project} server={props.server} kind="recent" />}
</For>
</Show>
<Show when={props.known.length > 0}>
<div class="mt-3 flex h-7 min-w-0 shrink-0 items-center pl-1.5 pr-3">
<div class="text-v2-text-text-faint [font-weight:530]">{props.language.t("home.knownProjects")}</div>
</div>
<For each={props.known}>
{(project) => <HomeSuggestedProjectRow {...props} project={project} server={props.server} kind="known" />}
<For each={props.items}>
{(project) => <HomeSuggestedProjectRow {...props} project={project} server={props.server} />}
</For>
</Show>
</div>
@@ -456,7 +435,6 @@ function HomeSuggestedProjectRow(
props: HomeProjectsViewProps & {
project: LocalProject
server: ServerConnection.Any
kind: "recent" | "known"
},
) {
const unreachable = () => props.serverHealth(props.server)?.healthy === false
@@ -467,18 +445,38 @@ function HomeSuggestedProjectRow(
return worktree
}
return (
<TooltipV2 placement="right" value={path()}>
<HomeProjectNavButton
type="button"
data-component={props.kind === "recent" ? "home-recently-closed-row" : "home-known-project-row"}
class="disabled:opacity-60"
disabled={unreachable()}
onClick={() => props.onAddProjects(props.server, [props.project.worktree])}
<div class="group/project relative flex h-7 min-w-0 items-center rounded-[6px]">
<TooltipV2 placement="right" value={path()}>
<HomeProjectNavButton
type="button"
data-component="home-recent-project-row"
class="pr-10 disabled:opacity-60"
disabled={unreachable()}
onClick={() => props.onAddProjects(props.server, [props.project.worktree])}
>
<HomeProjectAvatar project={props.project} outline />
<span class={HOME_PROJECT_NAV_LABEL}>{displayName(props.project)}</span>
</HomeProjectNavButton>
</TooltipV2>
<div
class={`
hover-reveal absolute right-1 top-1/2 flex -translate-y-1/2
group-hover/project:opacity-100 focus-within:opacity-100
`}
>
<HomeProjectAvatar project={props.project} outline />
<span class={HOME_PROJECT_NAV_LABEL}>{displayName(props.project)}</span>
</HomeProjectNavButton>
</TooltipV2>
<TooltipV2 placement="bottom" value={props.language.t("home.project.add")}>
<IconButtonV2
data-action="home-add-recent-project"
variant="ghost-muted"
size="small"
icon={<IconV2 name="plus" />}
aria-label={props.language.t("home.project.add")}
disabled={unreachable()}
onClick={() => props.onAddProjects(props.server, [props.project.worktree])}
/>
</TooltipV2>
</div>
</div>
)
}
+12 -2
View File
@@ -1,8 +1,18 @@
import type { HomeProjectsController } from "./home-projects-controller"
import { HomeProjectsView } from "./home-projects-view"
import type { HomeScrollController } from "./home-scroll-controller"
import { ServerConnection } from "@/context/servers"
export function HomeProjects(props: { projects: HomeProjectsController; scroll: HomeScrollController }) {
const recentMode = new Map<ServerConnection.Key, boolean>()
const showRecentForServer = (server: ServerConnection.Any) => {
const key = ServerConnection.key(server)
if (recentMode.get(key)) return true
if (props.projects.server.projects(server).length > 0) return false
recentMode.set(key, true)
return true
}
return (
<HomeProjectsView
language={props.projects.copy.language}
@@ -11,8 +21,8 @@ export function HomeProjects(props: { projects: HomeProjectsController; scroll:
selection={props.projects.selection.value()}
serverHealth={props.projects.server.health}
projectsForServer={props.projects.server.projects}
recentlyClosedForServer={props.projects.project.recentlyClosedForServer}
knownForServer={props.projects.project.knownForServer}
recentForServer={props.projects.project.recentForServer}
showRecentForServer={showRecentForServer}
homedirForServer={props.projects.project.homedirForServer}
collapsed={props.projects.server.collapsed}
canDefaultServer={props.projects.server.canDefault()}