@@ -8,6 +8,7 @@ import fuzzysort from "fuzzysort"
88import { createMemo , createResource , createSignal } from "solid-js"
99import { useGlobalSDK } from "@/context/global-sdk"
1010import { useGlobalSync } from "@/context/global-sync"
11+ import { useLayout } from "@/context/layout"
1112import { useLanguage } from "@/context/language"
1213
1314interface DialogSelectDirectoryProps {
@@ -19,6 +20,7 @@ interface DialogSelectDirectoryProps {
1920type Row = {
2021 absolute : string
2122 search : string
23+ group : "recent" | "folders"
2224}
2325
2426function 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
119130function useDirectorySearch ( args : {
@@ -237,6 +248,7 @@ function useDirectorySearch(args: {
237248export 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