Skip to content

Commit 1f108bc

Browse files
authored
feat(app): recent projects section in command pallette (#15270)
1 parent 6b31188 commit 1f108bc

1 file changed

Lines changed: 56 additions & 3 deletions

File tree

packages/app/src/components/dialog-select-directory.tsx

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import fuzzysort from "fuzzysort"
88
import { createMemo, createResource, createSignal } from "solid-js"
99
import { useGlobalSDK } from "@/context/global-sdk"
1010
import { useGlobalSync } from "@/context/global-sync"
11+
import { useLayout } from "@/context/layout"
1112
import { useLanguage } from "@/context/language"
1213

1314
interface DialogSelectDirectoryProps {
@@ -19,6 +20,7 @@ interface DialogSelectDirectoryProps {
1920
type Row = {
2021
absolute: string
2122
search: string
23+
group: "recent" | "folders"
2224
}
2325

2426
function cleanInput(value: string) {
@@ -101,7 +103,7 @@ function displayPath(path: string, input: string, home: string) {
101103
return tildeOf(full, home) || full
102104
}
103105

104-
function toRow(absolute: string, home: string): Row {
106+
function toRow(absolute: string, home: string, group: Row["group"]): Row {
105107
const full = trimTrailing(absolute)
106108
const tilde = tildeOf(full, home)
107109
const withSlash = (value: string) => {
@@ -113,7 +115,16 @@ function toRow(absolute: string, home: string): Row {
113115
const search = Array.from(
114116
new Set([full, withSlash(full), tilde, withSlash(tilde), getFilename(full)].filter(Boolean)),
115117
).join("\n")
116-
return { absolute: full, search }
118+
return { absolute: full, search, group }
119+
}
120+
121+
function uniqueRows(rows: Row[]) {
122+
const seen = new Set<string>()
123+
return rows.filter((row) => {
124+
if (seen.has(row.absolute)) return false
125+
seen.add(row.absolute)
126+
return true
127+
})
117128
}
118129

119130
function useDirectorySearch(args: {
@@ -237,6 +248,7 @@ function useDirectorySearch(args: {
237248
export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
238249
const sync = useGlobalSync()
239250
const sdk = useGlobalSDK()
251+
const layout = useLayout()
240252
const dialog = useDialog()
241253
const language = useLanguage()
242254

@@ -266,9 +278,42 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
266278
start,
267279
})
268280

281+
const recentProjects = createMemo(() => {
282+
const projects = layout.projects.list()
283+
const byProject = new Map<string, number>()
284+
285+
for (const project of projects) {
286+
let at = 0
287+
const dirs = [project.worktree, ...(project.sandboxes ?? [])]
288+
for (const directory of dirs) {
289+
const sessions = sync.child(directory, { bootstrap: false })[0].session
290+
for (const session of sessions) {
291+
if (session.time.archived) continue
292+
const updated = session.time.updated ?? session.time.created
293+
if (updated > at) at = updated
294+
}
295+
}
296+
byProject.set(project.worktree, at)
297+
}
298+
299+
return projects
300+
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
301+
.sort((a, b) => b.at - a.at || a.index - b.index)
302+
.slice(0, 5)
303+
.map(({ project }) => {
304+
const row = toRow(project.worktree, home(), "recent")
305+
const name = project.name || getFilename(project.worktree)
306+
return {
307+
...row,
308+
search: `${row.search}\n${name}`,
309+
}
310+
})
311+
})
312+
269313
const items = async (value: string) => {
270314
const results = await directories(value)
271-
return results.map((absolute) => toRow(absolute, home()))
315+
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
316+
return uniqueRows([...recentProjects(), ...directoryRows])
272317
}
273318

274319
function resolve(absolute: string) {
@@ -285,6 +330,14 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
285330
items={items}
286331
key={(x) => x.absolute}
287332
filterKeys={["search"]}
333+
groupBy={(item) => item.group}
334+
sortGroupsBy={(a, b) => {
335+
if (a.category === b.category) return 0
336+
return a.category === "recent" ? -1 : 1
337+
}}
338+
groupHeader={(group) =>
339+
group.category === "recent" ? language.t("home.recentProjects") : language.t("command.project.open")
340+
}
288341
ref={(r) => (list = r)}
289342
onFilter={(value) => setFilter(cleanInput(value))}
290343
onKeyEvent={(e, item) => {

0 commit comments

Comments
 (0)